- Home /
How to use GUILayoutUtility.GetRect() properly?
Hello all,
I am trying to make a simple component that would display a text of arbitrary length at given position of the screen, with the possibility to wordwrap at a given width and/or to display a background.
Here's the code:
public class TextDisplayer : ScaledGUI
{
public Rect Area;
public GameObject Reference;
public string Text;
public string CustomStyle;
public bool DisplayAsBox;
protected override void OnScaledGUI()
{
GUIContent content = new GUIContent(Text);
GUIStyle style = (CustomStyle != string.Empty) ? GUI.skin.GetStyle(CustomStyle) : (DisplayAsBox ? GUI.skin.box : GUI.skin.label);
GUILayoutOption[] options = new GUILayoutOption[] {GUILayout.MaxWidth(Area.width)};
Rect area = GUILayoutUtility.GetRect(content, style, options);
area.x = Area.x;
area.y = Area.y;
if (Reference)
{
Vector3 refPos = WorldToScaledGUIPoint(Reference.transform.position);
area.x += refPos.x;
area.y += refPos.y;
}
GUILayout.BeginArea(area);
if (DisplayAsBox)
{
GUILayout.Box(content, style, options);
}
else
{
GUILayout.Label(content, style, options);
}
GUILayout.EndArea();
}
}
The problem is: nothing is displayed!
If I set area.width
and area.height
manually to arbitrary values, it works well but I don't find this solution very elegant.
I found out here that OnGUI
(from which OnScaledGUI
is called in the parent ScaledGUI
class) is called to times: one time for EventType.Layout
, then one time for EventType.Repaint
. During EventType.Layout
, it is expected that GUILayoutUtility.GetRect()
returns 0 values as the layout is not computed yet. But if I understood correctly, GUILayout calls don't do anything in that case, so it shouldn't be a problem. Yet, It doesn't work as expected and as long as I base my area
on GUILayoutUtility.GetRect()
, nothing shows up on screen. So I must obviously miss something here, and do something wrong.
Would anyone know what is wrong with my use of GUILayoutUtility.GetRect()
, and how to fix it?
You are not showing/telling where Area is initialized so I have to ask, is it initialized? Also, what does Debug.Log(area.ToString());
print out just before you call BeginArea?
TextDisplayer
inherits from ScaledGUI
, which inherits from $$anonymous$$onoBehavior
. Area
being a public member variable, it is initialized with the values entered in the Inspector.
Regarding the logs, here's what I added:
...
if (Event.current.type == EventType.Layout)
{
Debug.Log("Layout " + area.ToString());
}
else if (Event.current.type == EventType.Repaint)
{
Debug.Log("Repaint " + area.ToString());
}
else
{
Debug.Log ("Other event " + area.ToString());
}
GUILayout.BeginArea(area);
...
And here's what I get:
Layout (x:421.11, y:301.78, width:1.00, height:1.00)
Repaint (x:421.11, y:301.78, width:100.00, height:68.00)
...
Hey bob, I've got exactly the same problem as you: Rect given by a GetRect is behaving strangely, which gives sometimes really small widths and heights. Did you figure out what was going wrong?
I never figured out why it would behave like this, but I found a workaround:
Rect textArea = new Rect(Area.x, Area.y, Area.width, style.CalcHeight(content, Area.width));
[...]
if (DisplayAsBox)
{
GUI.Box(textArea, content, style);
}
else
{
GUI.Label(textArea, content, style);
}
Hope this can help you.
Answer by fredericv · May 08, 2017 at 01:04 PM
Hi,
There is a first call with EventType.Layout
that registers all GUILayout requests, then just before the call with EventType.Repaint
, the rect are calculated properly.
Thus, GUILayoutUtility.GetRect( ... )
will return a dummy rect on Layout phase (0, 0, 1, 1)
and the correct rect on Repaint phase.
But, GUILayout.BeginArea(Rect)
uses the size on the Layout phase (so the dummy (0, 0, 1, 1)
).
One way to go, is to store the calculated rect on the Repaint phase and give it to GUILayout.BeginArea
on the Layout phase.
You can try something like this:
Rect m_AreaRect;
void OnGUI()
{
...
var rect = GUILayoutUtility.GetRect(...);
if (Event.current.type == EventType.Repaint)
m_AreaRect = rect;
GUILayout.BeginArea(m_AreaRect);
...
}
A question posted in 2013, answered at 2017, and reused at 2019. This is actually very hard to find via Google the explanation and workaround these days. I did not even know to use Event.current.type or that it had any role at all with OnGUI development.
If you did not know that fundamental fact about the IMGUI system, you may want to read my IMGUI crash course.