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:
- Bottom to top (ie. reverse)
- 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 Pass
ing 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:
- Process the mouse events
- and pass them along to its parent, the
MouseDetectionElement
.
The MouseDetectionElement
can then on its turn handle all the events correctly.