Godot Control node mouse input handling order

Godot mouse input handling is a source of lot of confusion.

The basic idea is that Mouse Filter set to Ignore ignores the element. Stop processes the received event and doesn’t pass it along. It stops there. While Pass handles the event but then passes it along so other nodes can process it.

Where most of the confusion happens is trying to figure out in what order the mouse input events get processed.

Godot handles the input in nodes in reverse depth-first order.

In other words the order is:

  1. Bottom to top (ie. reverse)
  2. Children first (ie. depth).

Tree order example

- Last
    - Sixth
        - Fifth
    - Fourth
        - Third
            - Second
            - First

One of the more important things to understand is that sibling nodes DO NOT react to Mouse Filter being set to Pass!!!

When a node is Passing event forward it ONLY passes it to its direct parent.

And of course only elements affected by the mouse clicking - or other mouse input events - are processed in the first place. You can’t Pass event forward to something that wouldn’t receive an event to begin with!

Using Pass, an example

Let’s assume a situation where we have a button that we want to press, and we also want to detect when the mouse enters and leaves the window - we use the MouseEntered and MouseExited events set up for that and the detection element covers the whole screen. OK, so.

This doesn’t work:

- SceneRoot (Control)
    - Button (Texturebutton)
    - MouseDetectionElement (Control)

Why?

Because in this case MouseDetectionElement will block clicks to Button if it has its MouseFilter set to Stop and Pass. And if it’s set to Ignore then it’s just inactive and doesn’t perform the function we would like it to do.

This won’t improve the situation:

- SceneRoot (Control)
     - MouseDetectionElement (Control)
    - Button (Texturebutton)

Now we can click the Button, but MouseDetectionElement will generate erroneous mouse entered and mouse exited events whether it’s set to Pass or Stop because when the mouse moves over the Button, it is counted as exiting/entering the MouseDetectionElement. It’s not what we want here.

The correct solution is to make the MouseDetectionElement part of the hierarchy so that the Button can Pass the events forward to its parent, the MouseDetectionElement:

- SceneRoot (Control)
     - MouseDetectionElement (Control)
        - Button (Texturebutton)

Here Button is processed first and with its Mouse Filter is set to Pass it will both:

  1. Process the mouse events
  2. and pass them along to its parent, the MouseDetectionElement.

The MouseDetectionElement can then on its turn handle all the events correctly.