-
-
Notifications
You must be signed in to change notification settings - Fork 21.5k
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
Control's mouse_exit()
is emitted when the mouse is moved to another control inside that control
#16854
Comments
I'm experiencing the same problem. 3.1.1 stable mono. Any updates / workarounds for this? |
Same here, having a way to say "If the mouse is inside the container" instead of "if the mouse is inside the container, as long as its not hovering over a child of the container" would be great. |
I'm facing the same issue on v3.2.1 |
Sorry for the late comment, maybe you have already solved this problem or moved on, but writing this to be helpful for other encountering this problem: A simple but brute-force method is to check if the mouse coordinates are inside the Control:
A simpler method is via
Edit: edited source-code formatting |
I'm facing the same issue on v3.2.2 stable build, ubuntu 20.04 |
I'm having the same issue on 3.2.2 stable, but with Area2D. |
I'm having the same issue on 3.2.2 stable, but with Area2D. on MacOS Catalina ver 10.15 EDIT: |
Yes, it looks like the mouse_exited() and mouse_entered() signals are sent even when a the mouse moves from one control node, to a child node inside that node while that child is set to pass. I believe this is happening because even though the signal passes through the child, it is coming from a different source. Before, the input event it was coming from the viewport, while now it's coming from the child. I do agree that this is counter-intuitive though. |
Ugh, the workaround with checking manually if the mouse is within the children rect is really hacky and has too many edge cases. For example, If I rotate the parent, the mouse-coordinates check is of course not aware, so I would need to offset the position it thinks the children rect are by the rotation they received. There has to be a better way... |
Been headbutting into this issue a few times now as well. It's very counter-intuitive and hard to track down. |
I encountered this issue too but I have an alternative solution that is (IMO) more convienent for my problem. I have an Autoload that plays a sound when a button is hovered over. However due to this issue the sound gets played when the cursor leaves the Control too. To fix this I came up with this: var audio := AudioStreamPlayer.new()
func _enter_tree() -> void:
var e := get_tree().connect("node_added", self, "node_added")
assert(e == OK)
e = get_tree().connect("node_removed", self, "node_removed")
assert(e == OK)
audio.stream = preload("res://addons/ui/397599__nightflame__menu-fx-02.wav")
audio.bus = "UI"
add_child(audio)
func node_added(node: Node) -> void:
if node is BaseButton:
var e := node.connect("mouse_entered", self, "mouse_entered", [node])
assert(e == OK)
e = node.connect("mouse_exited", self, "mouse_exited", [node])
assert(e == OK)
# Recursing is necessary because https://github.com/godotengine/godot/issues/16854
for n in Util.get_children_recursive(node):
if n is Control:
e = n.connect("mouse_entered", self, "mouse_entered", [node])
assert(e == OK)
e = n.connect("mouse_exited", self, "mouse_exited", [node])
assert(e == OK)
node.set_meta("ui_audio_mouse_entered", false)
func mouse_entered(node: BaseButton) -> void:
assert(node != null)
if not node.get_meta("ui_audio_mouse_entered"):
audio.play()
node.call_deferred("set_meta", "ui_audio_mouse_entered", true)
func mouse_exited(node: BaseButton) -> void:
node.call_deferred("set_meta", "ui_audio_mouse_entered", false) When the mouse enters a node or any of it children, |
@Reneator Thank you for sharing this idea! Your solution didn't work in my implementation. One of my solutions involving TextureRect and PanelContainer (aka popup) nodes inside an hboxcontainer in my viewport corner is: var popup = get_node("PanelContainer")
fun _on_AudioPlayerIcon_mouse_entered():
popup.show()
fun _on_Popup_mouse_exited():
if !popup.get_rect().has_point(get_local_mouse_position()):
popup.hide() It doesn't hide the panel too soon, but it does stick around if the user doesn't press a button first. Instead, additional flat (camouflaged) buttons bordering the panel edges with mouse exited signals is an effective workaround. Fortunately, if you nest the border buttons, one inside an hboxcontainer, which is inside a vboxcontainer holding the second border button, both border buttons autofit to the panel edges and can share the same identifier while other panel contents fills the panel. func _on_AudioPlayerIcon_moused_entered():
popup.show()
func _on_BorderButton_moused_exited():
popup.hide()
And that works flawlessly. |
On 3.2.3 stable and I'm also having this issue. The mouse moves into the area and mouse_exited is called immediately after mouse_entered, even when the mouse remains within the area. I'm working around it by manually checking the boundaries but it's super clunky and I'd really love for this function to actually work :( |
Based on this if you make a control node scene and attach this script:
This creates a node type called "MouseIn". You add this node as a child to the UI element you want to check. The "mouse_in" and "mouse_out" signals behave as required. This means you don't have to rewrite this code for every element. Just make sure when attaching signals you use "mouse_in" and "mouse_out" rather than "mouse_entered" or "mouse_exited" which still exist as it inherits from control. Hope this helps. |
I'm having the same problem, using Godot 3.3.2 (stable at the moment of writing) and with a TextureProgress with no children. |
mouse_exit()
is emitted when the mouse is moved to another control inside that control
@dhAlcojor This sounds different from all the above reports so far, so please upload a minimal reproduction project to make this easier to troubleshoot. |
@Calinou Nevermind, I prepared the reproduction project and it wasn't happening. I tested the code in my project again and, somehow, it's working now. I don't understand it... |
Thanks so much for this, works perfectly. But a workaround shouldn't have to be used, should it? The intended use of mouse_entered and mouse_exit when combined with the right mouse filter settings should allow for this? |
Thanks for this, but be warned that this workaround will trigger the signals always, even if the control is overlapped by other controls. In some cases, this is not the expected behavior. |
I adapted to this restriction by making sure that the children of a Node either have mouse_filter.IGNORE or by having the parent hook up to signals of the children that have mouse_filter.PASS or STOP. Combining this with the manual check if the mouse is inside with: Makes it possible to cover all functionalities/fronts. But this also might make it necessary to check for special conditions, like when i have a big menu/overlay over the entire UI, the "has_point" fires, even though you might have a different control in front. As mouse_entered and mouse_exited respects the mouse_filter and visual overlay of control nodes. I am compensating this by having bools that get set when a control that covers the screen is shown, like my option_menu or a dialog with an npc with a switch like: "is_option_menu_open" or "is_dialogue_screen_open" which i can then manually check in the "is mouse inside" of the control/node |
I'm trying to avoid the
|
It is really weird, check this out. VXSC2SK3tm.mp4But the behaviour is completely different for labels and text boxes: R2VCszbmv8.mp4Tested on v4.0.dev.custom_build [8138280] btw Here is the code which calls mouse enter/exit notifications: Lines 1577 to 1587 in 8138280
You can see that |
The difference here is because the default "mouse filter" for label is "ignore" whereas the default filter for buttons and textedits is "stop". Filter "ignore" will never trip mouse_entered but will not trip mouse_exited for its parents Filter "pass" which is supposed to give the intended behaviour is much closer, it trips mouse_entered for the child and then trips mouse_exited and immediately mouse_entered for the parent. (Bug aside), the issue here is both that "mouse filter" is not intuitive for newcomers, and the intuitive outcome of "mouse_entered" and "mouse_exited" is different in different contexts. For a menu like this setting all the children's mouse filters to "ignore"(ONLY IF it is not an interactive element) or "pass" will give more intuitive behaviour. Because the default mouse_filter settings are different for everything are different, Complex menus which can't overlap it might be worth using my workaround above as it is probably easier than going through every child and changing the mouse_filters. |
I am also (on v3.4.stable.official [206ba70]) getting this issue. In my case, it arose because the tooltip itself was drawn over the control with the mouse_entered() signal. You can solve in this case by making sure the new tooltip control does not overlap the detecting control. It is not ideal but is a possible solution. |
I found one case where this issue could cause more trouble than just ignoring the exited signal: We have a parent control "A", with mouse filter PASS, and we have a child control "B", inside the node "A", with mouse filter PASS. In that case, when hovering B, the node A emits the exited signal, then B emits the signal entered, and then, the node B emits the signal entered too. If we print this in the console, we will see this:
So, to workaround this, we have to ignore the signal emitted by the child B, and also the signal emitted by the parent, but only if the mouse were inside the parent and still inside its rect size. |
Still having this issue in 2023 |
@trytryty1 Which version of Godot are you using? Can you upload a reproduction project that demonstrates that issue? |
4.1 project: Video: mousebug.mp4 |
@trytryty1 @RonYanDaik |
lol still not working properly |
@Epenko1337 I've tested the latest project above and it seems to be working as intended in beta 6. |
Still not working... Never will... Godot 4.2 :p |
Hi @BendySonic, |
Sadly but i'm using stable 3.5, so have no way to check out. Anyway i just fixed this on my own in my project with some workaround. |
The problem seems to be focus. When i turned off focus on my buttons, mouse_exited did not fire when clicking them. |
Godot version: 3.0 stable, mono 5.4.1.7
OS/device including version:
Win 8.1
Issue description:
The mouse_exited() signal has the following description in the docs:
http://docs.godotengine.org/en/3.0/classes/class_control.html
And for NOTIFICATION_MOUSE_EXIT:
Well, that's not the case. The signal and notification are also emitted from control A when the cursor goes to another control B inside and children of A. The behavior is the same whether B has MouseFilter set to Stop or more surprisingly to Pass.
I am guessing that the issue comes from the fact that an object with MouseFilter set to pass only transmit click events and not all mouse events. (according to MouseFilter Pass documentation).
My use case is a small menu that should disappear when the
mouse_exited[1].zip
cursor moves outside. Ideally I would just call queue_free when mouse_exited is received, but in reality this cause the menu to disappear when the mouse moves to a button of the menu.
I tried to add a check to the mouse_exited() event: call queue_free only when mouse_exited() is sent AND the mouse is actually outside of the menu's rect. This does not work either: moving quickly from a button to outside of the menu means that the mouse is never registered as having entered the menu's control, so mouse_exited() is never called.
The first workaround that I found is to check for the cursor's position relative to the control's rect inside _Process(delta) which is very very ugly.
Another one is to add a control that pass mouse clicks on top of the menu, but that's not really clean either.
The best solution would be to have control with mouseFilter set to Pass to pass all mouse info and not only clicks.
The less good solution would be to correct the docs of mouse_exited(), NOTIFICATION_MOUSE_EXIT, mouse_entered() and NOTIFICATION_MOUSE_ENTER.
Steps to reproduce:
Create any control (parentControl)
Create another control inside it (childControl)
set childControl's mouseFilter property to "Pass"
parentControl's mouse_exited() signal fires when the cursor is moved from parentControl to childControl
Minimal reproduction project:
mouse_exited.zip
The text was updated successfully, but these errors were encountered: