I’ve finally got into using Ui Toolkit but I find that to draw text on a VisualElement (for labels on a graph), I have to either create a bunch of Label VisualElements and place them on the axis or manually draw the text using Painter2D MoveTo and LineTo.
The latter feels very inefficient, the former works but has issues. For instance a VisualElement cannot be attached to a parent from within a draw call, so I have had to use an async to wait until the next frame and add them then, hardly ideal.
Am I missing something? Is there an easier way to draw text to a VisualElement? If not, can the UI team say if this will be a feature in the future?
Adding a TextElement or using MeshGenerationContext.DrawText are the two recommended ways to draw text.
Yes you have to modify the hierarchy earlier. It can be done from a MonoBehaviour Update/LateUpdate or you can also use the scheduler.
That’s just what I need thanks. The next question (I am searching for answers first I promise), is: can the size of the text be determined before drawing, so it be placed appropriately. There is the ContentSizeFitter class but I’m not sure it applies in this case.
ContentSizeFitter is for uGUI, another UI framework. It won’t work with UI Toolkit.
Can you give more precise examples of the sizing behavior you want to achieve?
Sure, this is to draw values along a graph axis, so in order to draw them centered on axis points, I need to know the width and height of the text. With a VisualElement this is simply the resolved width and height, however with DrawText, I would need to know the size before drawing to position it correctly.
Ok I found a solution that works ok, would be good to know if there is a more elegant one.
I create a TextElement with a transparent background and text, and style.position = Position.Absolute (so it doesn’t affect other elements).
Attach the TextElement to the parent (in my case, a graph), and before drawing the text, use:
("Text", 0, VisualElement.MeasureMode.Undefined, 0 VisualElement.MeasureMode.Undefined)```
A TextElement will not measure text unless it is attached to a parent element, so this allows the text to be measured with minimal additional elements.
TextElement.MeasureTextSize is the way to go. However, you don’t need to be attached to a parent in order for the measuring to work. You simply need to make sure the font is assigned in the style, as well as an appropriate font size.
Here’s a minimal code snippet that would work:
var textElement = new TextElement();
var font = Resources.Load<FontAsset>("Fonts/OpenSans-Regular SDF");
textElement.style.unityFontDefinition = new FontDefinition { fontAsset = font };
textElement.style.fontSize = 12;
var size = textElement.MeasureTextSize("Text", 0, VisualElement.MeasureMode.Undefined, 0, VisualElement.MeasureMode.Undefined);
Debug.Log($"size: {size}");
That’s much nicer, thanks.
Are there any plans to allow rotation of the text in MeshGenerationContext.DrawText at any time? This can be achieved using the Label, but I’m avoiding VisualElements for text in this use case.