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

"mouse_entered" and "mouse_exited" signals are broken while holding down a mouse button #20881

Closed
PostPollux opened this issue Aug 10, 2018 · 42 comments

Comments

@PostPollux
Copy link

PostPollux commented Aug 10, 2018

Godot version:
fbb5ca4

OS/device including version:
Linux Manjaro

Issue description:
The "mouse_entered" and "mouse_exited" signals of control nodes don't get fired while holding any mouse button down when the click (any mouse button down) started on a control that does not ignore the mouse.

it's actually because in viewport.cpp in line 1843 to 1850 "over" get's set to the mouse_focus

if (gui.mouse_focus) {
	over = gui.mouse_focus;
	//recompute focus_inv_xform again here
} else {
	over = _gui_find_control(mpos);
}

and shortly after ( line 1884 to 1893 ) the mouse_exited and mouse_entered signals get not fired because the gui.mouse_focus and the gui.mouse_over are the same

if (over != gui.mouse_over) {

	if (gui.mouse_over)
		gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT);

	_gui_cancel_tooltip();

	if (over)
		over->notification(Control::NOTIFICATION_MOUSE_ENTER);
}

This may be a desired behavior, for a lot of cases.
But there are also cases where it is very useful to send those signals. (I'll update this post soon to show such a case)

Steps to reproduce:

  1. Connect the "mouse_entered" or "mouse_exited" signal of any control node
  2. add another control node, that does not ignore the mouse
  3. start your drag on the control element of point 2 and drag over the control element from point 1
  4. notice that no "mouse_entered" and no "mouse_exited" signals get fired
@spongeboburu
Copy link

Confirmed on my master build too. Note: if you set mouse filter to "ignore" on the parent control the color rectangle signals works as expected.

@jeremyz
Copy link
Contributor

jeremyz commented Feb 7, 2019

linked to #23296

@groud
Copy link
Member

groud commented Oct 10, 2019

Note from #32727, this does also happen for is_hovered()

@yuyubibibobibo

This comment has been minimized.

@swenner
Copy link
Contributor

swenner commented Apr 24, 2020

Still an issue in Godot 3.2.2beta ed27b7e

@MadDogMayCry0
Copy link

MadDogMayCry0 commented Feb 24, 2021

Still in 3.2.4RC2.
Try to click on bottom element. With out releasing it move cursor away from element!
New Game Project.zip
UPD
Works with some fixes from code.
#20881 (comment)

@MrJoshBowman
Copy link

MrJoshBowman commented Apr 14, 2021

managed to get around this by making my own _input(event:InputEventMouseButton) function.
I then had to make sure these two lines were executed as part of the code when the mouse is clicked down:

event.set_pressed(false)
set("mouse_filter", Control.MOUSE_FILTER_STOP)

I then reset the mouse_filter to ..._PASS on the mouse release.
This only worked with an _input() function, not a _gui_input() function

@MadDogMayCry0
Copy link

MadDogMayCry0 commented Apr 19, 2021

FIX :)
Mouse pressed signals bug.zip

@Banderi
Copy link

Banderi commented May 11, 2021

FIX :)
Mouse pressed signals bug.zip

Can not get this to work. The line get_global_rect().has_point(get_global_mouse_position()) does not take into account either scale or rotation of the node :(

@MadDogMayCry0
Copy link

FIX :)
Mouse pressed signals bug.zip

Can not get this to work. The line get_global_rect().has_point(get_global_mouse_position()) does not take into account either scale or rotation of the node :(

get_global_rect().has_point(get_local_mouse_position())

@Banderi
Copy link

Banderi commented May 12, 2021

FIX :)
Mouse pressed signals bug.zip

Can not get this to work. The line get_global_rect().has_point(get_global_mouse_position()) does not take into account either scale or rotation of the node :(

get_global_rect().has_point(get_local_mouse_position())

Both get_local_mouse_position or get_global_mouse_position do not work. As I said, the Rect2 that get_global_rect returns does not take into account rotation or scale.

EDIT:
Actually, here's what worked for me:

var rect_check = Rect2(Vector2(0,0), node.get_global_rect().size)
var local_mouse_pos = node.get_local_mouse_position()
if rect_check.has_point(local_mouse_pos):
	inside = true

get_local_mouse_position does indeed take into account rotation and scale, as it's in local space. Just had to check it against the local sizes of the Rect2 instead of the global ones.

@MadDogMayCry0
Copy link

MadDogMayCry0 commented May 15, 2021

@Banderi
Local mouse position, rotating, scaling and pivot :D
Mouse pressed signals bug.zip

@deanl42
Copy link

deanl42 commented Jun 22, 2021

Wasted hours thinking it was my code :-( v3.3.1
Appreciate the work around but hoping this gets a fix/resolution

@Zylann
Copy link
Contributor

Zylann commented Jun 23, 2021

This is probably related to a common UI behavior, which I saw called "mouse capture" on Windows. The idea is to make it easier to implement exclusive dragging logic, by making the control receive mouse events and appear pressed as long as the mouse was pressed inside and remains pressed even if it exits the area of the control. I would not be surprised if this wasnt documented because it's a subtle behavior happening with many widgets, and has no associated option.

For example, take a scrollbar. Press it, keep the mouse held, and move away from it. It will remain pressed, and will still respond to your movements, while every other control will ignore the mouse. You can reproduce this behavior on non-Godot UIs as well, even sometimes on controls that are not usually draggable (see how some buttons remain pressed when you do this too). Things can differ a bit though, because there are many UI systems around. Sometimes hover effects don't follow this, while pressed effects do. Godot happens to do both, including enter/exit.

About mouse_entered and mouse_exited: arguably, many widgets probably don't need mouse capture. But if these were changed to always fire when the mouse goes in and out of the control's area, regardless of what's actually happening, then likely a bunch of existing code might need to be fixed somehow (if they rely on it), without being able to rely on them. Or maybe controls can have an option to turn off mouse capture.

@deanl42
Copy link

deanl42 commented Jun 25, 2021

This is probably related to a common UI behavior, which I saw called "mouse capture" on Windows. The idea is to make it easier to implement exclusive dragging logic, by making the control receive mouse events and appear pressed as long as the mouse was pressed inside and remains pressed even if it exits the area of the control. I would not be surprised if this wasnt documented because it's a subtle behavior happening with many widgets, and has no associated option.

For example, take a scrollbar. Press it, keep the mouse held, and move away from it. It will remain pressed, and will still respond to your movements, while every other control will ignore the mouse. You can reproduce this behavior on non-Godot UIs as well, even sometimes on controls that are not usually draggable (see how some buttons remain pressed when you do this too). Things can differ a bit though, because there are many UI systems around. Sometimes hover effects don't follow this, while pressed effects do. Godot happens to do both, including enter/exit.

About mouse_entered and mouse_exited: arguably, many widgets probably don't need mouse capture. But if these were changed to always fire when the mouse goes in and out of the control's area, regardless of what's actually happening, then likely a bunch of existing code might need to be fixed somehow, without being able to rely on them. Or maybe controls can have an option to turn off mouse capture.

Thanks for that, makes sense. If this is the case then I would def upvote an option to switch it off for certain scenarios.

@Calinou
Copy link
Member

Calinou commented Jul 21, 2021

Out of curiosity, does this behavior happen with CollisionObject2D/3D's mouse_enter/mouse_exit signals too?

Thanks for that, makes sense. If this is the case then I would def upvote an option to switch it off for certain scenarios.

Feel free to open a pull request to do this 🙂

The property should be added to Control and the default behavior should be kept as it is now.

@MadDogMayCry0
Copy link

This has nothing to do with mouse behavior in Windows.

What do you mean? This behaviour is consistent across a whole lot of the programs I use on linux, and I imagine it is the same on Windows.

If I click something that does not have drag-and-drop functionality in a window (any application, not just godot ones) and then move the mouse elsewhere in the window (or even to other windows) with the button down, almost without exception there will be no indication the mouse hovers over anything until the button is released.

I mean exactly what I mean - the interpretation of the cursor behavior within the godot's engine logic has nothing to do with the interpretation and behavior of the mouse anywhere, even in Windows, even in Linux, even on an alien spaceship.

@golddotasksquestions
Copy link

@MadDogMayCry0
I can't get your workaround to work with my gui_input(event) example. I suppose it would be possible if I copy it line by line, use _input(event) instead of gui_input(event) and separate scripts like you did, but this is way too complicated for what I want to do.

@jeremyz
Copy link
Contributor

jeremyz commented Oct 18, 2021

@MadDogMayCry0 even though it works, what you do with
if event.is_pressed(): event.pressed = false
is to by pass the code that starts here : https://github.com/godotengine/godot/blob/3.x/scene/main/viewport.cpp#L1861
and thus break dragging modal windows and some yet unclear to me gui focus related code.

@MadDogMayCry0
Copy link

MadDogMayCry0 commented Oct 18, 2021

@MadDogMayCry0 I can't get your workaround to work with my gui_input(event) example. I suppose it would be possible if I copy it line by line, use _input(event) instead of gui_input(event) and separate scripts like you did, but this is way too complicated for what I want to do.

@MadDogMayCry0 even though it works, what you do with if event.is_pressed(): event.pressed = false is to by pass the code that starts here : https://github.com/godotengine/godot/blob/3.x/scene/main/viewport.cpp#L1861 and thus break dragging modal windows and some yet unclear to me gui focus related code.

Of course, you must adapt my logic to what you are trying to do with your code. My logic only explains and demonstrates how to fix this bug. However, it can be applied to anything, both modals and dialogs and absolutely any situation in general.

@jeremyz
Copy link
Contributor

jeremyz commented Oct 18, 2021

@MadDogMayCry0 it seems to me that we don't understand each other.
The single line trick you used event.pressed = false, fixes this issue, but creates at least 3 more issues.
Add an AcceptDialog in your test project, see that you can't drag the window anymore.
Add a TextEdit and see you can't click to focus in it anymore.
Add you trick to a drad'n drop test, see it breaks too.

@MadDogMayCry0
Copy link

@MadDogMayCry0 it seems to me that we don't understand each other. The single line trick you used event.pressed = false, fixes this issue, but creates at least 3 more issues. Add an AcceptDialog in your test project, see that you can't drag the window anymore. Add a TextEdit and see you can't click to focus in it anymore. Add you trick to a drad'n drop test, see it breaks too.

If you look at the code further than that piece you refer to all the time, you will probably see that there is a fork from the native functions and of course all the approaches you listed like drag and drop and other nonsense you need to add yourself. This works for about 10 minutes of time. I have not argued or asserted that my approach is universal, I will repeat myself once again - this is a demonstration of solving a problem, a demonstration that allows you to bypass native logic and build your own.

@RichardEllicott
Copy link

RichardEllicott commented Dec 6, 2022

any movement on this issue?

better yet could we have some access to some sort of function that could just query the screen at a certain position.... then people who rely on this unexpected behaviour wouldn't be affected

as it stands you need to come up with your own BSP/ReportingGrid... check every control over and over each frame etc... surely i cannot be the only person that wants to drag one control to another and detect i'm hovering over a different control.... if you reproduce windows you need to drag items to the recycle bin... if you create a drag drop equiping screen you need to drag inventory items to other ones

this limitation is highly impractical for what to me seems a common thing in video game UIs

@jeremyz
Copy link
Contributor

jeremyz commented Dec 7, 2022

see my 2 pull requests #54565 and #54552 and also #67791 from @Sauermann

@ondesic
Copy link

ondesic commented Jun 28, 2023

Can confirm this problem in 3.5.2 Stable

@KoBeWi
Copy link
Member

KoBeWi commented Jul 8, 2023

Can't reproduce in 4.1.
Can someone confirm?

@Sauermann
Copy link
Contributor

Sauermann commented Jul 8, 2023

Tested in v4.2.dev.custom_build [c3b0a92].
I can't reproduce the problem in the simple case, that starting Control and hovering Control are in the same Viewport. That seems to be fixed.

However, when starting Control and hovering Control are in different Viewports (SubViewport or Window), then I can reproduce the problem in many cases.
MRP for this is available in: #67791 (comment) (drag from any green icon and hover over a blue icon in a different Viewport)

@KoBeWi
Copy link
Member

KoBeWi commented Jul 8, 2023

Closing then. The more complex case is a separate issue.

@KoBeWi KoBeWi closed this as completed Jul 8, 2023
@KoBeWi KoBeWi added this to the 4.1 milestone Jul 8, 2023
@Sauermann
Copy link
Contributor

I didn't test it, but It is likely that this got fixed by #59555.

@kdserra
Copy link

kdserra commented Sep 23, 2023

Experiencing this on Godot 3.5.2

I am using using this to feature for drag & dropping items in my inventory system.

Move move over item slot -> Triggers MOUSE ENTERED (Good)
Hold down LMB -> Triggers DOWN (Good)
Move mouse outside item slot -> Triggers Nothing (Bad) Expected: MOUSE EXITED
Let go of LMB -> Triggers UP, and MOUSE EXITED (Half correct)

        public void _on_Button_button_down()
        {
            GD.Print("DOWN");
        }

        public void _on_Button_button_up()
        {
            GD.Print("UP");
        }

        public void _on_Button_mouse_entered()
        {
            GD.Print("MOUSE ENTERED");
        }

        public void _on_Button_mouse_exited()
        {
            GD.Print("MOUSE EXITED");
        }

Untitled

@KoBeWi
Copy link
Member

KoBeWi commented Sep 23, 2023

The issue is only fixed in Godot 4.1.

@NicolasVidal
Copy link

Does anyone know if it will be fixed in 3.5.x ?

@josepvalls
Copy link

Just hit this issue. Doesn't look fixed in 3.5.3 stable. Bummer.

@Poobslag
Copy link

Can confirm this bug is still not fixed in 3.5.3 stable.

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

Successfully merging a pull request may close this issue.