Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MouseEnter/Exit events not correctly passed on from children to parent script since Godot 4.2 dev3 #81909

Closed
mrTag opened this issue Sep 19, 2023 · 7 comments · Fixed by #84547

Comments

@mrTag
Copy link
Contributor

mrTag commented Sep 19, 2023

Godot version

4.2 dev3 and 4.2 dev4

System information

Windows 10

Issue description

With the following Control Node hierarchy:
image
the TextureRect Node (godot icon) blocks the mouse_entered / mouse_exited signals in the script of the ParentWithScript Node (the white area). The TextureRect uses its default mouse_filter of Pass. When using the mouse_filter Ignore, the signals are triggered correctly on the parent.

I went back the official versions to see when the behavior started and it seems to have started with 4.2 dev3.

In 4.2 dev2 the behavior is still a bit weird, but it has been like that for a long time (tested with 4.1.1) : when moving the mouse from "over the parent" (the white area in the MRP) directly to "over the child" (the godot icon), the mouse_exited signal gets triggered first and then directly the mouse_entered again. But at least the last signal that gets triggered is mouse_entered (important for custom tooltips!).

Another quick observation (that is not in the MRP): I added a tooltip to the ParentWithScript node and that gets correctly shown when hovering over the TextureRect child node! So it seems to only affect the signals.

Steps to reproduce

  • Open the minimal reproduction project in Godot 4.2 dev3 or dev4
  • Run it
  • Observe that entered gets printed when hovering over the white area (the parent), but exited gets called as soon as the mouse moves over the godot icon (the child)

Minimal reproduction project

MouseEnterExitTest.zip

@Sauermann
Copy link
Contributor

If I understand it correctly, then you want to have the ability, that the mouse should be considered simultaneously over multiple different Controls? This behavior previously led to several problematic situations, where for example some mouse-exited signals were not sent in edge-cases.

A different effect, that the previous behavior had, was that in the following variation of your MRP the mouse could be considered over ParentWithScript, while the mouse is outside of its area, which seems unexpected.

image

The behavior got changed in #67791 which was the attempt to solve these issues. Now there is only a single Control, that the mouse can be over at all times.

But at least the last signal that gets triggered is mouse_entered (important for custom tooltips!).

I have tested in v4.2.dev.custom_build [571cd0e] that custom tooltips (via Control._make_custom_tooltip()) work as expected. Which problem do you have with custom tooltips?

In order to solve your use-case, of knowing if the mouse is somewhere within the rectangle of ParentWithScript, I can think of the following two approaches:

  • use Rect2(Vector2.ZERO, size_of_control).has_point(position_of_mouse_in_local_coordinates)
  • Track the mouse-entered/exited signals of both ParentWithScript and TextureRect and update a variable based on all these singals.

@mournguard
Copy link

mournguard commented Sep 19, 2023

I agree that mouse events have been weird for a little while for me as well, and from what I understand of @Sauermann's reply, I now absolutely do not understand why Pass should even exist.

In the example, yes, having the mouse being over the parent on a child that isn't in it's bounding box is weird, but I feel like bubbling up like that is exactly what Pass was meant for. So if this is considered and issue, then... ? Maybe there's something I don't see here.

@mrTag
Copy link
Contributor Author

mrTag commented Sep 20, 2023

Oh I didn't even know "custom tooltips" were a high level concept in Godot 😀 What I meant was completely handling the tooltip logic yourself (via the mouse_entered/exited signals) for maximum customization...

Here is a concrete example from our game where this issue became a problem after the update to 4.2 dev4:
screenshot
The way we do the tooltips is that the top Node of the Stash Item (ItemStashNode) has a script on it with functions connected to its mouse_enter and mouse_exit signals. The tooltip gets shown and positioned to the TooltipPos node with mouse_enter and hidden with mouse_exit. This worked great and is exactly what I expect the signals to do when the children's mouse_filter is set to Pass. And yes, even when the child is not even in the bounding box of the parent (I can imagine designs where this would be the case).

With the update to 4.2 dev4 the tooltip now only gets shown when I hover over the small margin where the Icon node doesn't overlap the ItemStashNode. I have to specifically set every child mouse_filter to Ignore, so that they don't block the mouse inputs.

Here is the mouse_filter documentation for Pass:

The control will receive mouse button input events through _gui_input if clicked on. And the control will receive the mouse_entered and mouse_exited signals. If this control does not handle the event, the parent control (if any) will be considered, and so on until there is no more parent control to potentially handle it. This also allows signals to fire in other controls. If no control handled it, the event will be passed to Node._unhandled_input for further processing.

So the behavior before 4.2 dev3 was more or less exactly what was described there. The way it is now, that description is no longer correct. I would even say that the description for Stop fits the way Pass works now:

The control will receive mouse button input events through _gui_input if clicked on. And the control will receive the mouse_entered and mouse_exited signals. These events are automatically marked as handled, and they will not propagate further to other controls. This also results in blocking signals in other controls.

@Sauermann
Copy link
Contributor

I have to specifically set every child mouse_filter to Ignore, so that they don't block the mouse inputs.

If you don't need to handle input events in the children nodes, then this is in my opinion the best approach for your use-case. This has also a performance benefit, since these nodes simply ignore mouse events. I didn't mention this approach as a possible solution to your situation in my previous post, because I did assume, that these Control nodes process mouse events.

So the behavior before 4.2 dev3 was more or less exactly what was described there. The way it is now, that description is no longer correct. I would even say that the description for Stop fits the way Pass works now:

I agree, that the documentation needs to be improved.

@kitbdev
Copy link
Contributor

kitbdev commented Sep 27, 2023

For more complex UIs, using mouse filter ignore wont work and the workarounds become more lengthy.
Also the current system won't help for stuff like custom drag and drop like functionality.
Another issue is _gui_input's mouse move notifications are not in sync with mouse_entered and mouse_exited.

We could instead have multiple signals like mouse enter / exit and mouse over / out (or hover enter / exit).
Although the functionality should be different to the original and become 'Triggers when the mouse enters/exits the control or any of its descendants'. This way the events are more intuitive than the previous implementation. The mouse filter can also be respected.
For example, if the mouse is moving in between a parent and child controls with overlap like #81909 (comment) only the child would receive those events. Only when the mouse leaves the parent and all descendants does the parent receive mouse_exit.
Implementation details:

  • The mouse entered signal would not propagate up past the last gui.mouse_over,
  • The mouse exited signal would not propagate up past where the new gui.mouse_over is a parent of the old gui.mouse_over.

The current implementation is great for stuff like scene picking, so both are useful.
For reference unity has both mouse over/out and mouse enter/exit
https://docs.unity3d.com/Manual/UIE-Mouse-Events.html
It also seems like other ui libraries more commonly incorporate child elements or are based solely on the rect.

@akien-mga
Copy link
Member

Tested #84547, I confirm that it fixes the MRP in the OP.

@mrTag
Copy link
Contributor Author

mrTag commented Nov 9, 2023

Thanks for testing! I also tried #84547 with our game and it definitely closes this issue 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment