- Home /
GUILayout ArgumentException when combining with GUI.Tooltip
Sample code:
GUILayout.BeginArea(new Rect(1620f, 150f, 290f, 560f));
GUILayout.BeginHorizontal("box");
GUILayout.Label("[TITLE]", new GUILayoutOption[]
{
GUILayout.Height(40f)
});
GUILayout.EndHorizontal();
GUILayout.BeginVertical("box");
this.myToggle = GUILayout.Toggle(this.myToggle, new GUIContent("Toggle Title", "togglehint"));
GUILayout.EndVertical();
GUILayout.BeginVertical("box");
if (GUI.tooltip == "togglehint")
{
GUILayout.Label("Hint Title");
GUILayout.Label("Hint Body");
}
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndArea();
Getting this error continuously:
ArgumentException: Getting control 0's position in a group with only 0 controls when doing Repaint Aborting at UnityEngine.GUILayoutGroup.GetNext ()
All I want is to make sure I display relevant tooltip because ugly primitive implementation like from the docs resource:
GUI.Label(new Rect(10, 40, 100, 40), GUI.tooltip);
Will display ANY tooltip regardless of group or area.
Answer by Bunny83 · Aug 22, 2017 at 02:25 AM
Well, the problem is that "GUI.tooltip" is set from inside GUIStyle.Draw. That means it's content changes during the Repaint event. However since you check the tooltip in between elements you will mess up the layout system since during the layout event you would "draw" / call a different number of elements.
There are basically two solutions:
Either make sure you always draw the same number of elements, no matter what's the content of "GUI.tooltip".
Cache the tooltip at the end of your GUI code during the repaint event and use the cached version in your actual code. That way it won't change in between.
First case:
GUILayout.BeginVertical("box");
if (GUI.tooltip == "togglehint")
{
GUILayout.Label("Hint Title");
GUILayout.Label("Hint Body");
}
else
{
GUILayout.Label("");
GUILayout.Label("");
}
GUILayout.EndVertical();
Second case:
// [ ... ]
GUILayout.BeginVertical("box");
if (cachedTooltip == "togglehint")
{
GUILayout.Label("Hint Title");
GUILayout.Label("Hint Body");
}
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
GUILayout.EndArea();
if (Event.current.type == EventType.Repaint)
cachedTooltip = GUI.tooltip;
}
If you have many different tooltips you may want to just create a method that gets the tooltip text based on the "GUI.tooltip" string. That way you can always draw the tool tip gui elements. Just let the method return an empty string if you don't want to display anything.
I would avoid showing the tooltip integrated in the actual layout. Keep in mind that showing the tooltip can change the layout which is actually quite nasty. Tooltips usually should be displayed either in a fix position or outside / on top of the layout.
I would avoid showing the tooltip integrated in the actual layout. $$anonymous$$eep in $$anonymous$$d that showing the tooltip can change the layout which is actually quite nasty.
That's the thing! I wanted to display tooltip in a box-style area that only being drawn if tooptip for element exist. Which seems like impossible case because, as you said, you ALWAYS need to draw tooltip which means I always will be drawing tooltip's background box.
$$anonymous$$an, this sucks, but thanks for the tip.
Uhm, have you actually read my second solution? That allows you to draw the tool tip conditionally inside the layout. You just have to ensure you don't change the layout during a redraw. Every event that is processed by OnGUI / OnInspectorGUI / ... is always paired with a Layout event that is issued right before the event. The GUILayout elements will collect informations about all elements in order to construct the layout. During the actual event (Repaint, $$anonymous$$eyDown, ....) you have to process the same elements as it encountered during the corresponding layout event.
By using a cached tooltip like i explained the layout will be the same between "Layout" and "Repaint" as the "condition" is only changed after the last element during the repaint event.
Ps: You don't need to always draw the exact same elements. You just need the same kind and number of elements. You can also do:
if (GUI.tooltip == "togglehint")
{
GUILayout.BeginVertical("box");
GUILayout.Label("Hint Title");
GUILayout.Label("Hint Body");
GUILayout.EndVertical();
}
else
{
GUILayout.BeginVertical();
GUILayout.Label("");
GUILayout.Label("");
GUILayout.EndVertical();
}
This will just replace the elements with empty elements. Though the layout space is still reserved so the layout stays the same. Though as i said if you do like to change the layout when the tool tip is active you can do it like i explained above. I just wouldn't recommend it. In your case it might work as you have a FlexibleSpace after the two groups.
Also this line looks strange ^^:
GUILayout.Label("[TITLE]", new GUILayoutOption[]
{
GUILayout.Height(40f)
});
The GUILayoutOption array is a "params" array. So you don't need to manually create that array. You can simply do:
GUILayout.Label("[TITLE]", GUILayout.Height(40f) );
and if you need more options, just add them at the end
GUILayout.Label("[TITLE]", GUILayout.Height(40f), GUILayout.$$anonymous$$inWidth(100) );
You can write your own methods with params arrays:
void Some$$anonymous$$ethod(string aText, params int[] a$$anonymous$$oreParameters)
{
}
this can be called like this:
Some$$anonymous$$ethod("Hello World");
Some$$anonymous$$ethod("Hello World", 42);
Some$$anonymous$$ethod("Hello World",1,2,3,4,5,6,7,8,9);