Hi, we are currently trying to create a single modal window with a few buttons inside. So basically we are calling GUI.Window in every frame and the window shows up perfectly. However it seems that you can actually click and interact (hover) with buttons that are located behind the window, so the window does not block the mouse interactions. Is there some way to make a window block mouse input? Otherwise it would not be a very useful component to use.. Kind regards, Jan
*bump* Is noone using the GUIWindows of Unity? I am almost 100% positive I am doing something wrong as components below a window should not react on mouseovers etc...
GUI.Window are to give your UI a structure, but its not a modal thing. That being said, you could make it modal if you have a global UI manager class that handles the input and will just ignore the input to all other windows other than the modal one if a modal one is active
hmm okay... Is there any documentation about writing such an ui manager? Another question would be why buttons behind the window get highlighted when the mouse is over them. Even if the window is not modal, these buttons should not get mouseover events, as the window is over them, so actually the window should get the events.
I don't think this behaviour is intentional. You can code to ignore clicks in controls hidden behind the window, but this still doesn't get around the highlighting on hover. Perhaps you could file a bug report about this?
Indeed, it doesnt look like intended behaviour. I'll file a bug then. Buttons should not be highlighted behind windows and should not receive clicks behind windows either. I am just wondering why noone had an issue with that so far, is noone using GUIWindow?
This is what we've done, and it's worked very well. We wrote the GUIManager to handle this as well as other situations where we wanted to inhibit or re-route normal behavior (such as an interactive tutorial that needs to gain/remit control of the UI as it leads the user through the tutorial).
From what I've ascertained reading this and other forums, this has been a known issue, that the highlight state of buttons is done at the lower level style rendering, independent of the events caused by the buttons themselves. In general, if you have overlapping interactive UI like buttons, they will always highlight if you're over both of them, and the lower of the buttons (the farthest back in the draw order) will always be the button that gets the click event (unless you have a button in a window atop a button beneath the window, in which case the window will block the event of the button beneath it but not block the highlighting of the button beneath it). Tricky.
Hmm well tricky indeed, but this actually makes GUIWindow hard to use currently. It's hard to explain to a user why he suddenly clicked a button below the window, which he might not even see when the window is not translucent. A quick fix would be very nice for that.
Just giving this a bump because it's a serious issue for those of us who use GUI.Window for windows that have to correctly overlap everything below them (why ever would you want it otherwise? If you want just a box around some new content, use GUI.Box, not GUI.Window). Has anyone got a super simple project wide fix script for this? One that can be added once in the project and affect the behavior of every GUI.Window?
How about putting it in a separate script and using GUI.depth to assure it's _really_ on top. At the end of the OnGUI you then put in this code: Code (csharp): if (Event.current.type != EventType.Layout Event.current.type != EventType.Repaint) Event.current.Use (); That will stop all events from going somewhere else.
Bumping this again because this is still a problem. From what I can tell, you can still only block other buttons from being clicked, but *not the mouseover events, which should automatically be done by the GUI build for the window. Short of creating a new function to replace GUI.Window, I don't see there being a fix for this, unless unity is willing to do it. Or rather, no easy way around this, which effectively makes windows kind of superfluous. afaik, if the window is set to draggable, it will automatically block onclick events from passing through it. This is not the case when the window is held in a static location, however, and therefore a workaround must be manually coded in. In my experience, I have a sound that plays on mouse over of my buttons, as well as the skin that changes through a GUI.Style. Until this is fixed, there is no quick fix to resolve this :/ * = minor spelling/grammar edits Big Edit: My current workaround, while unity does not automatically do it is the following: Code (csharp): //custom button gui var menuButtonGUI : GUIStyle; var hoverWithWindow : GUIStyleState;//this should be no bg set var hoverWithoutWindow : GuiStleState;//this should be hover bg set var activeWindow = false; //Determines if a window is active /*must set this to true when a window is open, and false when a window is closed. Pending on how you code, this is either simple, or stupid (depending on how many windows you have, and how many ways you can close it)*/ function OnGUI { if (activeWindow) { menuButtonGUI.hover = hoverWithWindow; } else { menuButtonGUI.hover = hoverWithoutWindow; } } Then I just make the window draggable so that clicks don't go through (or you can use another work around for that). This works for how my setup is. Note, it does not stop the OnMouseOver events of the buttons, just makes it appear as though it does since button img will not be altered unless the windows are closed.[/code]
Hi, After some hacking I've managed to overcome this problem with a use of GUIUtility.hotControl. Here is the code: Code (csharp): static function ModalWindow(rect:Rect,title:String,windowFunction:Function) { return GUILayout.Window(GUIUtility.GetControlID(FocusType.Passive),rect,function(id:int) { GUI.depth=0; //first get a control id. every subsequent call to GUI control function will get a larger id var min=GUIUtility.GetControlID(FocusType.Native); //we can use the id to check if current control is inside our window if(GUIUtility.hotControl<min) setHotControl(0);//if it's not - set hot control to 0, to allow window controls to become hot //if it is, we can't change it, because mouseover effect would not be there //draw our window windowFunction(); //get max control id in our window var max=GUIUtility.GetControlID(FocusType.Native); //once again check current hot control //if it's outside our window - set it to -1 - it prevent's clicks! //we can't block clicks inside our window, so we have to check max!=-1 //max equals -1 if a control inside our window has taken focus in this frame, because //we can't get a valid id in this frame any more if(GUIUtility.hotControl<min || (GUIUtility.hotControl>max max!=-1)) setHotControl(-1); //focus the window and bring it to front GUI.FocusWindow(id); GUI.BringWindowToFront(id); },title); } static function setHotControl(id:int) { //don't block events if mousePointer is outside "Screen", otherwise if we run the game in editor //we would block the editor GUI as well. if(Rect(0,0,Screen.width,Screen.height).Contains(GUIUtility.GUIToScreenPoint(Event.current.mousePosition))) GUIUtility.hotControl=id; }
Translated it to C#: Code (csharp): public delegate void Callback(); public class ModalGUI : MonoBehaviour { public static void Window(Rect rect, string title, Callback windowFunction) { GUILayout.Window(GUIUtility.GetControlID(FocusType.Passive), rect, (int id) => { GUI.depth = 0; // first get a control id. every subsequent call to GUI control function will get a larger id int min = GUIUtility.GetControlID(FocusType.Native); // we can use the id to check if current control is inside our window if (GUIUtility.hotControl < min) setHotControl(0); // draw the window windowFunction(); int max = GUIUtility.GetControlID(FocusType.Native); if (GUIUtility.hotControl < min || (GUIUtility.hotControl > max max != -1)) setHotControl(-1); GUI.FocusWindow(id); GUI.BringWindowToFront(id); }, title); } private static void setHotControl(int id) { if (new Rect(0, 0, Screen.width, Screen.height).Contains(GUIUtility.GUIToScreenPoint(Event.current.mousePosition))) GUIUtility.hotControl = id; } } Untested as of right now, hope this works (I've been looking for something like this as well here). BTW, just noticed the code window on the forums is vastly improved, awesome!! Cheers
This actually seems to work for the most part. It opens a window and prevents other elements not inside that window from being clicked or hovering. However when you close that modal window, the other elements dont have their hover effects again until you click on one of them (which gives all elements back their hover effects), can anyone suggest why that might be? Is there a way to clear the hot control to restore them?
Although it may seem dirty, the way modal windows are usually done by many GUI frameworks is using the overlay: behind the window you just have to draw a single button of 100x100% screen size that will receive mouse clicks (of course, style that button using the skin to be single-colored and semi-transparent).
I actually solved my problem, so this does work fine. The overlay wasn't suitable for what I needed, I'm actually using a window for dropdown menus, which seems the best way to get around the Unity limitations.
In that case you can also go without a Window "wrapper". Just track the Rect (bounds) of your menu, and watch for the following events to make the menu dissapear (this is kind of a standard): - user clicked (actually mouse-down-ed) outside of the menu - user mouse-wheel-ed outside of the menu - user resized the screen
Nah I'm using GUILayout, which makes things more difficult since you have to get the rect on the repaint frame etc. There are other issues as well, such as the dropdown needing to show inside a window, and buttons sitting behind the dropdown shouldn't be clickable etc. I have seen other implementations without using a window, but they make you hold the mouse down and choose whatever was highlighted when you release the mouse, but this isn't standard dropdown behaviour and wasn't what I wanted.