godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.29k stars 20.23k forks source link

Editor: The popups selecting menu options on mouse move #88324

Closed EmrysMyrddin closed 7 months ago

EmrysMyrddin commented 7 months ago

Tested versions

Bug introduced by this commit: 06c2cda848b4bca8ee2f54a09417a97a7c1384d3

System information

macOS 14.1, Apple M3 Pro

Issue description

~When editing an exported Dictionary property, trying to change the type of a property opens a popup to chose the new type.~

In fact, this seems to be the case of every popup menus. The right click menus for example have the same problem, if you maintain the right click and drag over a menu item, this menu item will be clicked once you start moving the mouse again after releasing the mouse button.

If the mouse doesn't immediatly move after clicking of the edit button, the popup will close itself when the mouse begins to move, using whatever type is under the mouse as the selected type.

Steps to reproduce

https://github.com/godotengine/godot/assets/3855602/f460ea12-4b91-4a24-89ef-cf220229216e

Minimal reproduction project (MRP)

N/A

EmrysMyrddin commented 7 months ago

I also notice a weird warning in the console, but this one was already showing up before the linked commit.

 scene/main/window.cpp:712 - Entering a window while a window is hovered should never happen in DisplayServer.
KoBeWi commented 7 months ago

Can't reproduce it on Windows.

EmrysMyrddin commented 7 months ago

After some tests:

The 2 last items are very confusing since it's not how every native menus are working. It breaks users assumption about how to interact with the UI

KoBeWi commented 7 months ago

The first 2 are a bug. Items should activate only on button release, not mouse movement.

The last one is expected behavior. I don't know if it's OS difference, but that's how most apps work (e.g. VS Code, GIMP, Audacity etc.).

AThousandShips commented 7 months ago

If the popup opens under the mouse (which is arguably a bad UI pattern but it exists in multiple places in Godot)

See:

EmrysMyrddin commented 7 months ago

@KoBeWi Hummm not sure I follow you ?

On Windows too, in a menu, if you maintain a mouse click on a menu item, you can either drag over another menu item, or outside the menu popup. Depending on the application, dragging outside cancel the click or closes the popup (I've seen both in multiple common app)

ryevdokimov commented 7 months ago

I just checked with a friend who uses mac and he says that this kind of functionality is used there as well, so this might have to do specifically with how Godot handles inputs on macOS. At least with the native mac menus this is how they work.

KoBeWi commented 7 months ago

Ah yes, dragging outside not closing menu is also a bug technically. I meant that activating the menu (opening popup) on button down is intended, because that's what enables drag activation in the first place.

EmrysMyrddin commented 7 months ago

@KoBeWi Ah ok I see what you mean. But then this is only for buttons that opens a popup right ? Because other buttons, you can drag outside of the button to cancel click too.

EmrysMyrddin commented 7 months ago

I will try later if I can find out what differs between the macos and windows behavior. I have both at home, not at the office, so will check tonight or later this week.

EmrysMyrddin commented 7 months ago

If someone has a Linux to test if the bug is also there ?

akien-mga commented 7 months ago

I can confirm a regression / unexpected change in behavior on Linux with KDE Plasma on Wayland, using both touchpad and mouse.

When I right-click an element e.g. in the FileSystem dock (with the touchpad button), moving the touchpad triggers the selection of one of the item under the cursor, even though I did not tap/click. This is not the intended behavior for me when using a touchpad with tap-to-click enabled.

This is also reproducible with a mouse similarly: Right click on a folder, move the mouse to the right to attempt selecting an item, there's a high probability that one will be selected automatically.

It works if I hold right-click, but that's not feasible on a touchpad, and unconventional with a mouse, at least for me.

In either case, the item under the cursor is not selected on mouse release, but on the first movement after mouse release.

ryevdokimov commented 7 months ago

Strange. I just tested with a touchpad as well, and it works fine.

This will be difficult for me to debug since I don't have a mac or linux machine, although my theories based off this section of code in the PR: https://github.com/godotengine/godot/pull/86952

https://github.com/godotengine/godot/blob/06c2cda848b4bca8ee2f54a09417a97a7c1384d3/scene/gui/popup_menu.cpp#L574-L576

Is that on macOS/linux something is different about how these flags or masks work. Maybe these flags are returning false when the cursor enters the popup menu element on these OS? I'd be curious at what point a breakpoint placed at line 575 would be triggered.

For me, it's only when left/right mouse is released regardless of where the cursor is (unless it's outside Godot, and which point it gets triggered immediately when the mouse reenters)

Edit: This is confirmed. When the breakpoint is triggered depends on the OS. Would need someone more familiar with the Input code to look at this if we want it resolved quickly.

EmrysMyrddin commented 7 months ago

Ok, I managed to compile Godot on my Windows machine, and I confirm that the original bug that I described in the issue is not reproduced for me.

The "one cycle click" feature is working both with menu opened via a button or via right click.

But I have the same behavior than on macOS concerning the trigger of action on click press instead of click release. Perhaps I should update the body of the issue ? I think that those 2 bugs are tightly related.

ryevdokimov commented 7 months ago

I'm not sure I understand the 2nd issue. What is the undesired behavior found in the windows build?

EmrysMyrddin commented 7 months ago

@ryevdokimov Good catch, on Windows, we go into the if when the mouse button is released wherever the mouse is, as you said.

On macOS, we don't go into the if when the mouse button is released, but when go into it whenever the mouse enters the menu area.

ryevdokimov commented 7 months ago

Interesting.

Would need someone more familiar with the Input code to take a look at this if we want to solve this quickly. I am unfamiliar with that part of the code and couldn't guess why the discrepancy between OS would exist, and whether or not it's by design.

Did you see my question about the 2nd issue? Is there any behavior in the Windows build that is undesirable?

EmrysMyrddin commented 7 months ago

I'm not sure I understand the 2nd issue. What is the undesired behavior found in the windows build?

Since I'm not a native english person, it's a bit difficult for me to explain it clearly by text :-(

Let me try to make a video:

https://github.com/godotengine/godot/assets/3855602/ce3d7e9b-6455-4b9e-83ee-74352396c167

Here, you can see the menu action is selected when I push the mouse button down.

The expected behavior is to trigger the action only on button up. And the selected menu action should then be the one under the mouse cursor when the button is released. If the cursor is out of the menu area when the button is released, it should close the menu (not every app do this, but I think it's actually a good idea).

Here an example of how VSCode menu is working:

https://github.com/godotengine/godot/assets/3855602/c0501ae7-39fd-4b08-a1e8-b729bc0fe7fc

ryevdokimov commented 7 months ago

Ah I see. After you you've already clicked (but haven't selected an item), the behavior has regressed so that items are selected on mouse-down, which I agree is undesirable. This I can fix. Good catch.

ryevdokimov commented 7 months ago

The issue in your last comment will be resolved in this above-mentioned PR.

Overall, it is unrelated to the original post.

The bigger issue is something to do with this: https://github.com/godotengine/godot/issues/88324#issuecomment-1944309848

EmrysMyrddin commented 7 months ago

I've added some print statement to try to understand what is happening.

The problem is that we land in this if

So the origin of the bug is that for some reason, we don't get mouse up/down events on macos in this function.

EmrysMyrddin commented 7 months ago

Ok, I've just understood that the m variable is valid when it's a mouse movement event while b is valid on mouse button events ><'

So from what I see, on macOS, we get mouse mouvement events only when the mous is over the menu popup, while on windows we get those event wherever the mouse is in the window.

This is why we get this bug: on macOS we can't detect that the mouse button have released outside the popup since we don't have events from outside the popup.

akien-mga commented 7 months ago

CC @bruvzg

bruvzg commented 7 months ago

This is why we get this bug: on macOS we can't detect that the mouse button have released outside the popup since we don't have events from outside the popup.

This is a normal behavior, windows should not receive mouse buttons events outside the window, if it is received on Windows it looks like Windows bug.

Closing of the native popups (which require press events outside the window) are done in DisplayServer using global mouse event filter: https://github.com/godotengine/godot/blob/907db8eebcecb97d527edcaff77a1c87a6c068f5/platform/macos/godot_application.mm#L114 in case of macOS, currently it's only handling mouse down events.

EmrysMyrddin commented 7 months ago

It makes sense.

After some thoughts, I'm not sure why we have to do all this dragging and button release tracking ?

Why can't we just react to mouse button up? It should work for all cases ?

ryevdokimov commented 7 months ago

If I'm understanding you correctly, then I committed a change to the PR that fixed the other issue to use the DisplayServer to check for mouse state: https://github.com/godotengine/godot/pull/88342

The behavior seems the same in Windows (which is good), could someone check on linux/macOS?

EmrysMyrddin commented 7 months ago

No, it's not working :/

But I feel that we are actually trying to workaround a problem: we don't get mouse button up event. It seems that we don't get mouse button up events (even if it occurs over the popup) if the button down event occured outside the popup menu.

This is a bit unexpected to me, that's the root cause of this convoluted code. Shouldn't be better to make mouse button up events fire in the popup instead ?

ryevdokimov commented 7 months ago

Just to confirm, it works for you on macOS? (Nevermind)

The issue is it seems that once I initiate a click to make the popup visible (which is initiated outside the context of the popup), the following mouse up will not register in the popup. I can't comment if this is normal behavior.

EmrysMyrddin commented 7 months ago

I though it was, but no, it's not.

The problem is not the detection of the mouse state, it's that we don't get mouse movement events. Which means this code is not called for mouse mouvement outside of the popup. So the problem is still there.

EmrysMyrddin commented 7 months ago

the following mouse up will not register in the popup

Yes I do agree that this is the actual issue. I don't know if it's intended, but all this drag and press detection code is here only because of this :-(

ryevdokimov commented 7 months ago

https://github.com/godotengine/godot/blob/076a9f8179eebc9f1a50889794d3f8dd56e2638f/scene/gui/popup_menu.cpp#L591-L594

Is this not getting the mouse state regardless of what context the cursor is in? I'm still confused why this would work in Windows and not on other OS. If there is a bug in Windows that allows it to work that I'm accidently exploiting, I may need to wait for that to get fixed so that I can properly investigate how to make this functionality work.

ryevdokimov commented 7 months ago

I just updated the PR to use the DisplayServer to check the position of the cursor as well, maybe this will fix it: https://github.com/godotengine/godot/pull/88342

bruvzg commented 7 months ago

I think I see what the root of the issue, there's some extra code to redirect MouseMotion event to the popup on Windows, but not on other platforms.

bruvzg commented 7 months ago

Also, popup input handling seems to be extremely overcomplicated, not sure what it's trying to do at all, should not it just activate item on release?

bruvzg commented 7 months ago

I think I see what the root of the issue, there's some extra code to redirect MouseMotion event to the popup on Windows, but not on other platforms.

Part of the https://github.com/godotengine/godot/pull/67903 (not sure why it was added only for Windows).

ryevdokimov commented 7 months ago

Also, popup input handling seems to be extremely overcomplicated, not sure what it's trying to do at all, should not it just activate item on release?

Mouse up is not registered in the popup when mouse down is initiated to make the popup visible and then subsequently held. I wrote the code within that framework, but it now sounds like a Band-Aid to a bigger issue.

bruvzg commented 7 months ago

I guess the same code should be applied to mouse button events, since if mouse was pressed all events will be sent (at least by macOS) to the window that have received the press event, until its released. Otherwise, activation on release will never work.

bruvzg commented 7 months ago

A quick fix for macOS (and probably X11 as well, have not tested it) - https://github.com/godotengine/godot/pull/88366

I'll evaluate other event behavior on all desktop platforms tomorrow, it might be better to do the same redirection to all events.

EmrysMyrddin commented 7 months ago

In fact I do think that it makes sense to not get event happening outside of the pop up window.

But for me, the root problem is that the mouse button release event should be forwarded to the pop up window, even if the button down happened outside it.

Forwarding mouse mouvement events is just a workaround to make our feature work as it is, but we should not rely on mouse movement for this in the first place.

kitbdev commented 7 months ago

Mouse up is not registered in the popup when mouse down is initiated

This happens with all controls too, not just multi window stuff.

ryevdokimov commented 7 months ago

This happens with all controls too, not just multi window stuff.

Good to know there is a rational for it at least.

I still don't fully understand why my PR doesn't fix this popup issue: https://github.com/godotengine/godot/pull/88342

Doesn't using DisplayServer check state regardless of what context the cursor is in? I hate to repeat this question, but trying to understand if there is another separate issue.

According to this: https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-get-mouse-button-mask

image

This method in the Input class is equivalent, and the input class says that it checks global input state.

image

The reason I use DisplayServer in that PR is because I also check global position of the cursor using the same object, but yeah it shouldn't make a difference.

EmrysMyrddin commented 7 months ago

@ryevdokimov The problem is not the detection of left/right button state detection. This was already working fine actually :-)

The problem is that the mouse mouvement event are not received when the happen outside of the popup. So the problem is that the _input_from_window_internal function is not called until the mouse enters the popup area.

So what happens is:

Is it more clear ? I can try to make a video with some logs if it can help you visualize what happens in macOS :-) I know it's difficult to imagine this just from text

akien-mga commented 7 months ago

Please test https://github.com/godotengine/godot/pull/88392 and see if it works as expected on your platforms.

ryevdokimov commented 7 months ago

Please test https://github.com/godotengine/godot/pull/88392 and see if it works as expected on your platforms.

Works for me. All the issues it says it will fix seem to be fixed on Windows.

The problem is that the mouse mouvement event are not received when the happen outside of the popup. So the problem is that the _input_from_window_internal function is not called until the mouse enters the popup area.

The PR I was referring to doesn't check against mouse movement anymore to receive button state. It checks against the DisplayServer which I thought will allow capturing state globally from within the popup regardless of where the click was initiated. Since the problem seems to be resolved though I can ask about it separate from this post.

EmrysMyrddin commented 7 months ago

@ryevdokimov Yes, you don't rely on the event object to get the mouse state, but this doesn't change how the function _input_from_window_internal is called. It is called for any input events, including mouse mouvement events. And your were are relying on the fact the function is called on each mouse mouvement, regardless of how you detect mouse state during the event handling.

EmrysMyrddin commented 7 months ago

@akien-mga It is working on macOS :-)

But as I said, I'm not sure if it's a good fix? Is it expected to receive events that doesn't belongs to the window your control is in?

I do think that fixing https://github.com/godotengine/godot/issues/84466 is a better fix, it's a more expected behavior and would simplify the code of the "one cycle click" feature a lot :-)

kitbdev commented 7 months ago

I do think that fixing https://github.com/godotengine/godot/issues/84466 is a better fix, it's a more expected behavior and would simplify the code of the "one cycle click" feature a lot :-)

I tried to change mouse focus and fix #84466 for this, but it wouldn't work when single window mode was false. When holding the mouse down and moving it over the popup, it wouldn't receive the inputs. Mouse events just weren't being sent to the correct window from the DisplayServer, at least on Windows. See https://github.com/kitbdev/godot/commit/34a2f1846d1f4111c685041cbd2e9db13472d8f0

DanielSnd commented 7 months ago

Huh, I came here searching to see if there was already an issue for this, I see that other people aren't experiencing that on windows but I'm on Windows 11 and I'm experiencing it.

If I right click while moving my mouse to the right/down I reproduce it every time. Only way to properly use right clicks now is if I right click while moving my mouse to the left.

rightclickissue

Can't reproduce on 4.2. I'm compiled from master as of 2 days ago.

EmrysMyrddin commented 7 months ago

It seems from the screen record you have that the popup menu takes a bit of time to be actually initialiased (it stays grey for a bit of time) It think that if your mouse enters the popup area before it is fully initialised, you are experiencing the exact same problem we have on macOS. It just have to take more than 250ms to fully initialised (before that, the activation of menus are disabled to avoid missclicks)

This is why the proper fix to this is to rely on mouse up event and to not rely on mouse mouvement for this :-)

And i suppose @DanielSnd that https://github.com/godotengine/godot/pull/88392 doesn't fix your problem ?

DanielSnd commented 7 months ago

And i suppose @DanielSnd that #88392 doesn't fix your problem ?

I hadn't tried before, I have now tried #88392 and I can confirm it does indeed fix my problem :)

Thank you!