Search Unity

Modal GUI.Windows?

Discussion in 'Immediate Mode GUI (IMGUI)' started by kork, Jan 13, 2010.

  1. kork

    kork

    Joined:
    Jul 14, 2009
    Posts:
    280
    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
     
  2. kork

    kork

    Joined:
    Jul 14, 2009
    Posts:
    280
    *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...
     
  3. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    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
     
  4. kork

    kork

    Joined:
    Jul 14, 2009
    Posts:
    280
    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.
     
  5. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    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?
     
  6. kork

    kork

    Joined:
    Jul 14, 2009
    Posts:
    280
    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?
     
  7. auzette

    auzette

    Joined:
    Mar 23, 2009
    Posts:
    74
    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).
     
  8. auzette

    auzette

    Joined:
    Mar 23, 2009
    Posts:
    74
    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.
     
  9. kork

    kork

    Joined:
    Jul 14, 2009
    Posts:
    280
    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.
     
  10. Dakta

    Dakta

    Joined:
    Apr 8, 2008
    Posts:
    492
    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?
     
  11. NicholasFrancis

    NicholasFrancis

    Joined:
    Apr 8, 2005
    Posts:
    1,587
    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):
    1. if (Event.current.type != EventType.Layout  Event.current.type != EventType.Repaint)
    2.     Event.current.Use ();
    That will stop all events from going somewhere else.
     
  12. Ezzerland

    Ezzerland

    Joined:
    Jul 1, 2010
    Posts:
    405
    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):
    1. //custom button gui
    2. var menuButtonGUI : GUIStyle;
    3. var hoverWithWindow : GUIStyleState;//this should be no bg set
    4. var hoverWithoutWindow : GuiStleState;//this should be hover bg set
    5.  
    6. var activeWindow = false; //Determines if a window is active
    7.  
    8. /*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)*/
    9.  
    10.  
    11. function OnGUI {
    12.  
    13.   if (activeWindow) {
    14.     menuButtonGUI.hover = hoverWithWindow;
    15.   } else {
    16.     menuButtonGUI.hover = hoverWithoutWindow;
    17.   }
    18.  
    19. }
    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]
     
  13. kmierzejewski

    kmierzejewski

    Joined:
    Feb 11, 2011
    Posts:
    3
    Hi,
    After some hacking I've managed to overcome this problem with a use of GUIUtility.hotControl. Here is the code:
    Code (csharp):
    1.  
    2. static function ModalWindow(rect:Rect,title:String,windowFunction:Function)
    3.  
    4. {
    5.  
    6.     return GUILayout.Window(GUIUtility.GetControlID(FocusType.Passive),rect,function(id:int)
    7.  
    8.     {
    9.  
    10.         GUI.depth=0;
    11.  
    12.         //first get a control id. every subsequent call to GUI control function will get a larger id
    13.  
    14.         var min=GUIUtility.GetControlID(FocusType.Native);
    15.  
    16.         //we can use the id to check if current control is inside our window
    17.  
    18.         if(GUIUtility.hotControl<min)
    19.  
    20.             setHotControl(0);//if it's not - set hot control to 0, to allow window controls to become hot
    21.  
    22.         //if it is, we can't change it, because mouseover effect would not be there
    23.  
    24.        
    25.  
    26.         //draw our window
    27.  
    28.         windowFunction();
    29.  
    30.        
    31.  
    32.         //get max control id in our window
    33.  
    34.         var max=GUIUtility.GetControlID(FocusType.Native);
    35.  
    36.         //once again check current hot control
    37.  
    38.         //if it's outside our window - set it to -1 - it prevent's clicks!
    39.  
    40.         //we can't block clicks inside our window, so we have to check max!=-1
    41.  
    42.         //max equals -1 if a control inside our window has taken focus in this frame, because
    43.  
    44.         //we can't get a valid id in this frame any more
    45.  
    46.         if(GUIUtility.hotControl<min || (GUIUtility.hotControl>max  max!=-1))
    47.  
    48.             setHotControl(-1);
    49.  
    50.        
    51.  
    52.         //focus the window and bring it to front
    53.  
    54.         GUI.FocusWindow(id);
    55.  
    56.         GUI.BringWindowToFront(id);
    57.  
    58.     },title);
    59.  
    60. }
    61.  
    62.  
    63.  
    64. static function setHotControl(id:int)
    65.  
    66. {
    67.  
    68.     //don't block events if mousePointer is outside "Screen", otherwise if we run the game in editor
    69.  
    70.     //we would block the editor GUI as well.
    71.  
    72.     if(Rect(0,0,Screen.width,Screen.height).Contains(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)))
    73.  
    74.         GUIUtility.hotControl=id;      
    75.  
    76. }
    77.  
     
    Last edited: Mar 31, 2011
  14. Alchemyst

    Alchemyst

    Joined:
    Jun 1, 2010
    Posts:
    51
    I totally can't figure out how to translate this into C#. Does anyone know it well enough?

     
  15. HarvesteR

    HarvesteR

    Joined:
    May 22, 2009
    Posts:
    531
    Translated it to C#:

    Code (csharp):
    1.  
    2. public delegate void Callback();
    3.  
    4. public class ModalGUI : MonoBehaviour
    5. {
    6.  
    7.     public static void Window(Rect rect, string title, Callback windowFunction)
    8.     {
    9.  
    10.         GUILayout.Window(GUIUtility.GetControlID(FocusType.Passive), rect, (int id) =>
    11.             {
    12.                 GUI.depth = 0;
    13.  
    14.                 // first get a control id. every subsequent call to GUI control function will get a larger id
    15.                 int min = GUIUtility.GetControlID(FocusType.Native);
    16.  
    17.                 // we can use the id to check if current control is inside our window
    18.                 if (GUIUtility.hotControl < min)
    19.                     setHotControl(0);
    20.  
    21.                 // draw the window
    22.                 windowFunction();
    23.  
    24.                 int max = GUIUtility.GetControlID(FocusType.Native);
    25.  
    26.                 if (GUIUtility.hotControl < min || (GUIUtility.hotControl > max  max != -1))
    27.                     setHotControl(-1);
    28.  
    29.                 GUI.FocusWindow(id);
    30.                 GUI.BringWindowToFront(id);
    31.  
    32.             }, title);
    33.  
    34.  
    35.  
    36.     }
    37.  
    38.     private static void setHotControl(int id)
    39.     {
    40.         if (new Rect(0, 0, Screen.width, Screen.height).Contains(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)))
    41.             GUIUtility.hotControl = id;
    42.     }
    43.  
    44. }
    45.  
    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
     
    Last edited: Feb 8, 2012
  16. Staples

    Staples

    Joined:
    Jan 14, 2009
    Posts:
    224
    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?
     
  17. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    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).
     
  18. Staples

    Staples

    Joined:
    Jan 14, 2009
    Posts:
    224
    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.
     
  19. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    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
     
  20. Staples

    Staples

    Joined:
    Jan 14, 2009
    Posts:
    224
    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.