About the (infamous) “processing order” bug

Recently I had a chat with IMGUI authors about the processing order bug.

Processing order bug

Processing order bug

In the image above, the big button on in the back is rendered first. Then the small button is rendered on top.

However, there is a serious problem: the top button isn’t clickable. There’s no way to process the click from your code if buttons overlap!

The following code will never output the “Top button clicked” message:

using UnityEngine;
public class Test : MonoBehaviour {
    void OnGUI () {
        if (GUI.Button (new Rect(100, 100, 300, 300), "Bottom"))
            Debug.Log("Bottom button clicked");
        if (GUI.Button (new Rect(200, 280, 100, 100), "Top"))
            Debug.Log("Top button clicked");
    }
}

Additionally, when the mouse is over the small button, all the buttons under the mouse change state to “hover” – but this isn’t a huge problem (in eDriven.Gui I solved it using the GUIStyle.Draw).

The problem

This is related to how the OnGUI calls are being processed by Unity: controls at 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).

Rather than processing all the overlapping buttons (back to front) the system chooses to process the first button (the one at the back) and use the event (the events is being “swallowed” – the click never gets to a top button).

Reversing the order?

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.

It turns out that this reverses the rendering order, which we unfortunately don’t want. :(

GUi.Window “solution”

What Unity team is proposing is using the 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 at all, but someone might find it useful. :)

Should we overlap GUI controls at all?

Note that this problem happens only when having the overlapped controls.

Now, there is quite a few people advocating the thesis of overlapping GUI controls being a bug. This – of course – cannot be true because it is a widely used practice: in a number of user interfaces there is a possibility of clicking the container, as well as the child of container (both are interactive and clickable):

The example of overlapping GUI elements

The example of overlapping GUI elements

Clickable rows and buttons on top

Clickable rows and buttons on top

Solution

There is a working 2-step solution I came up with:

1. using a single button in the application
2. avoiding the usage of scrollviews, scrollbars and sliders

This solution will be built into eDriven.Gui v.12.

eDriven dynamically looks for the top-most button under the mouse on each mouse move. All the other buttons are being rendered as labels (using the same button style).

Also, you shouldn’t use any of the complex controls provided by Unity: scrollviews, scrollbars and sliders.

The good news is that I’ve successfully built a scrollbar (using 4 buttons: track, thumb, up and down button) which I could individually target and apply the “single button on screen” policy mentioned above. :)

Posted in News