- Home /
EditorWindow GUI.Button activation is oposite from render order?
Hi, in my custom class I have a GUI.Button and I call its OnGUI method for each element of a list in a specific order to achieve the depth (the call order is draw order so my last called element gets on top and so do the buttons in each).
The problem is that GUI.Button, EditorGUI.EnumPopup and similar are getting activated on click in exactly opposite from the order they were rendered. If I have two buttons and one is under the other, I press the one drawn last but the one drawn first gets triggered. How to fix this?
Thanks!
Answer by Radivarig · May 15, 2014 at 03:21 AM
I have created two display methods, Active and Fake, inside fake I copied the whole gui code but instead each button and enumPopup I used a label, so from many buttons I created a situation where there is only one button at the time so no overlapping accours therefore bothside orders are equal and the bug is bypassed. Additionally I used a custom guistyle, and ObjectNames.Nicify that puts spaces before each capital in variables since that was being for focused elements and was not processed otherwise.
Answer by Bunny83 · Apr 12, 2014 at 08:13 AM
Well, just found this question ;)
It's not a bug, it's how immediate GUI systems work. Each "element" is on it's own. OnGUI is called several times a frame for different "events". Repaint is called every frame and makes the elements redraw itself in the order you invoke them. If a MouseDown even happens OnGUI is called again and your elements are invoked in the same order. That means the first element gets the event first.
GUI elements don't "know" about other elements since it's an immediate mode. The only ways to kind of order GUI elements are:
setting GUI.depth which will only work between different in-game GUI scripts. So it only affects in which order different scripts are invoked. What's inside OnGUI stays the same.
Roll your own object based GUI system where each GUI element is an object with it's own "OnGUI" method. Now the containing OnGUI method can simply reverse the invkoation order for mouse / keyboard events. However keep in mind that this won't work with GUILayout since GUILayout saves the layouting information based on the order the elements got called. So the layouting information wouldn't match the correct elements.
using windows.
For the editor the best bet would be windows. Only one window can have the focus at a time. So if a window is on top of another window the one that has the focus will get the events first. Keep in mind that clicking into the area of another window will focus that one.
It's usually the best to avoid overlapping GUI elements all together. If you need it in some rare cases you can use windows. The window callbacks are executed seperatly from the OnGUI code.
Thanks for the reply! I am writting an editor window, the first thing I did before I did anything was tried to use windows, but I lost hours and hours searching for a way to get the hovered window id that I need to pass as argument manipulate focus and at the end I decided to write my own implementation, I made a class that stores rectangles and then I simply check if that rect contains mouse and the reference to that class gets passed. This way I made it possible to change focus, select multiple elements and a lot of other things that I was not able to even start thinking about because I couldn't get the hovered window in the first place. If you have a solution for the window id please tell me even tho at this moment it's not a big concern for me.
So, the first way you mentioned is the one I actually am using, each element (my custom class) has its own OnGUI that I have to call from the editor, and as I change focus I have a reference list where I move the selected one to the end, that way I call their OnGUIs in foreach and the last selected is at the end rendering them as expected. In those OnGUIs I use GUI and not GUILayout.
Here is an interesting thing, when I render them in reversed order the oposite thing happens, the last element gets on top as expected, but now when I press a button it contains and if lets say 3 other buttons are under it, the last one the 4th one which is the "furthest" (drawn first) gets activated. So, as I described, the order of elements is exactly the oposite of the GUI.Texture textures being drawn from the same OnGUI as GUI.Button. I even did this, I made a condition that calls only one part of OnGUI to only draw textures, and another condition to only draw another part where the buttons are, then I called them both but in reversed order, and as predicted I achieved the textures being drawn good with their buttons drawn in wrong, but the buttons were triggered as I would want them to.
In short, because of calling GUI.Texture and GUI.Button from the same OnGUI and getting the textures drawn one over another as expected and buttons drawn one over another as expected but getting the buttons activate in reversed order they are drawn I would say it is a bug specific to UnityEditor. What do you think?
Well, again, the first button you draw gets the mouse event first since it's the first that can handle it. Of course the one you draw first is the one at the bottom since the others are drawn on top.
You might take a look at my GUI basics post. The GUI system is exactly the same in game or in an EditorWindow. The only real difference is that an EditorWindow isn't constantly redrawn. This is not a bug but the expected behaviour.
In general you should avoid overlapping GUI elements.
However if you don't use GUILayout you can create some "wrapper classes" for each GUI-element-type (button, toggle, ...) and have all your elements in a List. Now you can iterate through the list depending on what event occurred:
//C#
List<$$anonymous$$yGUIElement> elements = new List<$$anonymous$$yGUIElement>();
void OnGUI()
{
Event e = Event.current;
if (e.type == EventType.Repaint)
{
// invoke Repaint event in forward order
for(int i = 0; i < elements.Count; i++)
elements[i].OnGUI();
}
else
{
// invoke all other events reverse, so the visually top element (which was drawn last) gets the events first.
for(int i = elements.Count-1; i>=0 i--)
elements[i].OnGUI();
}
}
Another way could be to disable parts of the GUI which shouldn't get any input at the moment. This is usually the best solution if you want to show some kind of modal popup. Just set GUI.enable to false before you draw the elements and only set it to true before the elements which should be enabled.
Hm, this seems interesting, but I always and only at the end call Repaint, maybe not the best thing but it saved me a lot of frustrations. I've made a simple demonstration, if you could take a look please, everything is set just open the window add two elemeents and drag one over another and click the button of the front one. If you won't find a solution then I'll have to give windows another try I guess.. and thanks again for your time!
Hi, I have made a window version, and the buttons of the front one do get focus, but the problem I described is still visible with any buttons even with window approach, and again I think this is a bug because with both GUI and GUILayout the window acts the same as my custom class acted - button activation is reversed to order the button is drawn. If I call the button first and window second the window will be above it but I will be able to click the button trough the window, other case if the window is under then the button doesn't get clicked. Bug.
Answer by Xavier78 · Mar 29, 2014 at 06:55 AM
try switching there position, the one called first might be put on the top layer, and then every one called after gets below it, that is if it make a new layer for each, basically opposite of photo shop, the one that was made first is always on top, which it might be giving a value to that layer, so first layer = 0 secondlayer = 1, ect because it was first in line it is called first.
just my guess.
Thanks for the reply! Can you explain more in detail what you mean by layers in EditorWindow?
Your answer
Follow this Question
Related Questions
Show camera view in an editor window 1 Answer
Change SceneViewState via code (Fog, Skybox, Animated Materials, Image effects, etc)? 0 Answers
OnSceneGUI event Use() ates too much :) 1 Answer
PropertyDrawer and EditorWindow 1 Answer
How to get Event.KeyboardEvent from non-focused EditorWindow? 0 Answers