Search Unity

ScrollView and/or TextArea overlapping and focus, GUI.depth...

Discussion in 'Immediate Mode GUI (IMGUI)' started by dkozar, May 20, 2012.

  1. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    This is the only issue I didn't manage to solve with Unity GUI.:confused:

    I'm rendering multiple ScrollViews (as on the image here) - from the same OnGUI() call.

    As you can see at the link above, I solved the mouse scroll wheel issue. But somehow I cannot solve the click-through. Meaning when two TextFields overlap, and clicked on their intersection, the bottom one gets focused (and text selected). The same problem when clicking ScrollView arrows or thumb.

    The problem could easily be solved when in different OnGUI() calls, explained here: http://unity3d.com/support/documentation/ScriptReference/GUI-depth

    However, all my GUI calls are all being called from one script, thus I cannot get the effect of changing the GUI.depth.

    Just to show what I've been trying is to accomplish. I've put this line before GUI.TextField() or GUI.BeginScrollView():

    Code (csharp):
    1. GUI.depth = MouseStateSingleton.MouseTarget == this ? 0 : 1000;
    MouseTarget is a reference to the top component under the mouse. I'm forcing all other components to render with higher depth (since my drawing order doesn't really depend on GUI.depth parameter, I can set each component to the same depth of 1000).

    However, GUI.depth seems to play no role since I'm not using multiple OnGUI() calls (multiple scripts).

    Does anyone have an idea how to solve this?

    Thanks
     
    Last edited: May 20, 2012
  2. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Furthermore, the default flow inside the Unity GUI (focusing the component by Unity) is canceled after you cancel the mousedown event:

    Code (csharp):
    1. Event.current.Use();
    It would be great if I had a low-level approach for setting the clicked TextArea / ScrollView on mousedown, which the default functionality does. This way I could set the top one to be focused or scrolled.
     
  3. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    I found out the simple solution that solves the TextArea problems.

    TextArea's click-enabled area is much wider than of a ScrollView track - so, this is solving about 90% of problems.

    The idea si to render TextArea as Box when not on the top (under the mouse):

    Code (csharp):
    1. if (MouseStateSingleton.MouseTarget == this)
    2. {
    3.     _text = GUI.TextArea(_rect, _text, _style);
    4. }
    5. else
    6. {
    7.     GUI.Box(_rect, _text, _style);
    8. }
    There's no visual difference, since the same GUIStyle is being used for drawing both TextArea and Box.

    Also, I believe there must be some optimization lying in this code, since we are rendering a much simple Boxes. We are using only one TextArea per application, walking it around.

    PS. The only problem left now is with overlapping ScrollView tracks with other ScrollView tracks and active TextArea. Since this is the rare case, I won't complain, but still waiting for the solution :)

    The issue could be illustrated with images in the attach.
     

    Attached Files:

  4. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    I've just reported the bug... I'd really like to see it solved, because UnityGUI is a great stuff, and this is the glitch that cannot be solved from the code (not without building a custom scrollbar system).

    The bug is filed as Case 501358.
     

    Attached Files:

    Last edited: Nov 22, 2012
  5. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Here's the case of overlapping scrollbars and scrolling by dragging the scrollbar thumb. The area where scrollbars overlap (for the top scrollbar) is non-sensitive for mouse events such as mouse down.
     

    Attached Files:

  6. maraoz

    maraoz

    Joined:
    Nov 15, 2012
    Posts:
    20
    no reply? bug solved?
     
  7. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Nope - nothing for now. :)
     
  8. dkozar

    dkozar

    Joined:
    Nov 30, 2009
    Posts:
    1,410
    Update:

    I had a chat with IMGUI authors (Nicholas, Shawn) and the reason for this problem is how the OnGUI calls are being processed.

    The thing is that controls on the back are being rendered first, and there's no way of knowing what will be rendered (and clicked) on top (at least this is my understanding of what's going on).

    For example, the top button could never be clicked here:

    Code (csharp):
    1. using UnityEngine;
    2. public class Test : MonoBehaviour {
    3.     void OnGUI () {
    4.         if (GUI.Button (new Rect(100, 100, 300, 300), "Bottom"))
    5.             Debug.Log("Bottom button clicked");
    6.         if (GUI.Button (new Rect(200, 280, 100, 100), "Top"))
    7.             Debug.Log("Top button clicked"); // cannot click me. Neither Chuck Norris cannot do it :)
    8.     }
    9. }
    $depth.png

    So, rather than processing all the overlapping buttons (back to front) the system decides to process the first button (the one on the back) and use the event (the click never gets to top button).

    What they are proposing the usage of GUI.Window for wrapping up controls that have to be on top and clickable at the same time. I personally don't like this solution, but someone might find it useful :)

    Conclusion:

    Components are being rendered back to front - this is how it should normally be, and is the same as with other systems (Flash display list, HTML rendering tree).

    However, due to clicks being processed in the same order, if you want to have top-most controls processed first, you should reverse the order of your components. However, this would reverse the rendering order, which we unfortunately don't want. :(
     
  9. DiRoll

    DiRoll

    Joined:
    Jan 5, 2014
    Posts:
    32
    Two years have passed but the problem remained. I'm at a dead end, I do not know how to solve it too.
     
  10. Verrazano

    Verrazano

    Joined:
    Apr 20, 2014
    Posts:
    1
    I found a way to deal with this scenario.

    In my setup I have a list of text areas that each have a depth. I reorder the text areas such that they are rendered in the correct order. However this results in the discussed behavior where the text area on the bottom catches the input.

    before I loop and draw each text area I setup some variables
    Code (CSharp):
    1. int controlId = 0;
    2. string controlName = "";
    then when drawing each text area I do the following.
    Code (CSharp):
    1. Rect textRect = new Rect (guiPos.x, guiPos.y, size.x, size.y);
    2.  
    3. // area is an instance of class containing information about my text area
    4. string name = area.ToString();
    5. GUI.depth = area.depth;
    6. GUI.SetNextControlName (name);
    7. area.text = GUI.TextArea (textRect, area.text);
    The crux of the problem is that when the text area on bottom is clicked it consumes the event. We can fix this by tricking the GUI into believing the event wasn't used.

    Code (CSharp):
    1. // type is the value of e.type and e is set to Event.current
    2. if (type == EventType.MouseDown && e.button == 0) {
    3.     if(textRect.Contains(e.mousePosition)) {
    4.         e.type = type;
    5.         controlID = GUIUtility.hotControl;
    6.         controlName = name;
    7.         GUIUtility.hotControl = 0;
    8.         GUI.FocusControl ("");
    9.     }
    10. }
    Finally after the loop we need to give control to the correct text area and consume the event.

    Code (CSharp):
    1. if (controlID != 0) {
    2.     Event.current.Use ();
    3.     GUI.FocusControl (controlName);
    4.     GUIUtility.hotControl = controlID;
    5. }
    The following code results in the desired behavior where text areas rendered on top take control.