There seems to be an issue with GetPreferredValues and Word Wrapping

Hello everyone, I seem to have encountered a problem with TextMeshPro, in which it ‘lies’ to me regarding the expected values and the values it gives.

(Using Unity 2019.2.10f1 , TMP 2.0.1 from package manager, claiming to be up to date)

Specifically, it seems that if you call TMP_Text.GetPreferredValues(someTextThatWouldBeWrapped, someLimitedValue, someNotLimitedValue), and you have wrapping enabled…

  1. the height returned would be that of the multiple lines of the text that got wrapped, which is fine
  2. the width returned would be that of the text as one line, which is not expected because the height is now that of multiple lines.

Even worse, after the text has been wrapped, trying to check the text.bounds.size or text.textBounds.size gives the values without wrapping at all, even after using Canvas.ForceUpdateCanvases() to force the canvas to update.

Is there any way to get the properly wrapped text’s actual sizes? I am trying to force text to wrap only if it would exceed a certain arbitary value and regardless of the case, ensure the text is in a box of its proper minimal size along with other elements. not being able to get the proper wrapped bounds makes this impossible.

For extra reference, here is a gif displaying the issue Imgur: The magic of the Internet
The gif shows the code in action, but in the case of CriticalStrikeChance, the text gets wrapped, and the preferred size it reports with limited width is (308, 34) , and the size with unlimited width is (308, 16) - the preferred height increases without actually lowering the preferred width.

Thanks for reading, any help to getting the proper wrapped values would be appreciated!

Preferred Width is the width of the text assuming no limitation on width. So unless the text contains a line feed, the Preferred Width will be the width needed to fit all the text on a single line. If the text contains line feeds, then Preferred Width will be the width of the longest line.

The bounds of the text and textInfo is populated when the text is processed which happens just before the frame is rendered. In situations where you need to get updated bounds or textInfo before this normal update, you can call TMP_Text.ForceMeshUpdate() for force processing of the text object right away.

The TMP_Text.bounds are those of the resulting geometry and based on the actual RectTransform size.

Once populated the TMP_Text.textInfo will contain data about the text including character, word, line count, etc. With this information you can then query the lineInfo[ ] to check the width of individual lines, etc.

In terms of forcing wrapping… How are you planning on forcing the wrapping?

2 Likes

As it stands I am using a manually written sort of ‘bootleg’ layout wherein I…

  1. Go through all my textsmeshprouguis, setting their .text to their respective words
  2. Go through them all again, (currently, before reading your reply) and getting their preferred size (limited by my own manual max width), keeping the highest preferred width
  3. Going through all of them again… setting all the sizes to (highest needed width limited by maxwidth, their preferred height considering said max width)
  4. going through them all again, one last time, setting their position on the container with respect to their height
  5. setting the size of the contained to that of the highest width and total height plus padding.

Note that I only do this once, on demand, whenever hovering over a different tooltip target than the old one. (using canvasgroup to hide the tooltip otherwise)

Thanks for the response, so if I understand this correctly, is there no way to ask textmeshpro ‘what would my size be if wrapped’?
(assuming the answer is ‘no way’) I guess this means I should…

  1. Set all the text widths to the maximum one regardless of circumstance
  2. Use TMP_Text.ForceMeshUpdate() on them
  3. Query the textInfo.LineInfos to see the longest line, and keep the highest
  4. go through them all again, setting the width to the highest LineInfo’s width?

Assuming your text containers have a maximum width based on some UI layout / design:
(1) Set the width of the text container to its default max width.
(2) Set the text on the text object
(3) Call ForceMeshUpdate()
(4) Get TMP_Text.bounds which would return the bounds of the text factoring in word wrapping and limited by this max width. In the event the max width is larger than a single line of text then the bounds would be smaller than max width. Assuming the text container max default width is smaller than a single line, the bounds would be smaller than this width.
(5) Repeat process for each text object.

If you were trying to balance the text or have it wrap before the default max width, then you would have to look at each line in the textInfo.lineInfo[ ] to check their current width or any other data in there to force some layout.

Most of the time, there is a way :wink:

I’ll take a look tomorrow at adding something like public Vector2 GetRenderedValues(string text, float width, float height) which would return the information you seek but without having to actually set the text on the text object and forcing a mesh update.

1 Like

Thank you very much for your response!

I went right ahead to trying the way I proposed after having finished posting it, and, aside a few blunders where lineInfo.width is apparently always equal to the recttransform’s width and needing to use lineInfo.length instead, and having to add 1 pixel because of what I assume is just float miscalcuations(?), I managed to get it working!

I am blessed to have gotten support this quickly! Thank you!

You are most welcome.

I will still take a look at adding this new public Vector2 GetRenderedValues(string text, float width, float height) function which should simplify this in the future.

2 Likes

I can confirm that GetRenderedValues is present as of TMP 2.0.1, and it works fantastically. Thanks very much!

I can’t find GetRenderedValues in the documentation or in the code for 2.0.1+!
Anyone got any pointers? We’re doing the ForceMeshUpdate approach but it’s quite expensive!

@inkletom I can’t find GetRenderedValues(string text, float width, float height), but there are a couple of other overloads buried: Class TMP_Text | TextMeshPro | 3.0.9