X Tutup
Skip to content

[PopupMenu] Fix submenu item not popping on mouse enter#110256

Merged
Repiteo merged 1 commit intogodotengine:masterfrom
Koyper:fix_popup_menu_item_wont_open
Nov 18, 2025
Merged

[PopupMenu] Fix submenu item not popping on mouse enter#110256
Repiteo merged 1 commit intogodotengine:masterfrom
Koyper:fix_popup_menu_item_wont_open

Conversation

@Koyper
Copy link
Contributor

@Koyper Koyper commented Sep 4, 2025

Fixes #77246, Fixes #102081 and Fixes #70361.

This PR fixes the issue where a submenu would not open on hover until the mouse was jiggled. It also adds an additional delay to close a submenu when the mouse is moving to the right and goes across another submenu item. This provides time to reach a long submenu without it being closed by touching another submenu item.

[Edit: The issue below went away after the push eliminating minimum_lifetime_timer. See comments below regarding probable source of the issue.]

There were several things confounding the issue, one of which is mysterious. In _activate_submenu() calling submenu_pum->popup() would intermittently fail, leaving the submenu invisible, even when submenu_pum->is_visible() shows as true. This is fixed by a process_frame, so I added a hack to do this, but I'm sure there is a better way. There should be a fix maybe in Window that would detect the incomplete rendering?

Here's the line that can be improved:

submenu_pum->popup();
// Unpredictably, some submenus won't show even after popup without a process_frame.
get_tree()->connect("process_frame", callable_mp(this, &PopupMenu::_activate_submenu).bind(p_over, p_by_keyboard), CONNECT_ONE_SHOT);
```</s>

@Koyper Koyper requested a review from a team as a code owner September 4, 2025 15:56
@AThousandShips AThousandShips added bug topic:gui cherrypick:4.4 Considered for cherry-picking into a future 4.4.x release cherrypick:4.5 Considered for cherry-picking into a future 4.5.x release labels Sep 4, 2025
@AThousandShips AThousandShips added this to the 4.6 milestone Sep 4, 2025
@bruvzg bruvzg self-requested a review September 4, 2025 16:04
@Koyper
Copy link
Contributor Author

Koyper commented Sep 4, 2025

This const doesn't seem to be used anywhere - can it be removed from popup_menu.h? It also shows up in option_button.h.

	// ATTENTION: This is used by the POT generator's scene parser. If the number of properties returned by `_get_items()` ever changes,
	// this value should be updated to reflect the new size.
	static const int ITEM_PROPERTY_SIZE = 10;

@Koyper Koyper force-pushed the fix_popup_menu_item_wont_open branch 2 times, most recently from 88a0428 to a251137 Compare September 5, 2025 23:30
@KoBeWi
Copy link
Member

KoBeWi commented Sep 20, 2025

Doesn't seem to fix the issue

godot.windows.editor.dev.x86_64_Kdv2xEbSfA.mp4

@WhalesState
Copy link
Contributor

WhalesState commented Sep 20, 2025

It can be fixed by storing the next submenu that should show and once the timer timeouts we show it here after we hide the previous submenu.

void PopupMenu::_minimum_lifetime_timeout() {
	close_allowed = true;
	// If the mouse still isn't in this popup after timer expires, close.
	if (!activated_by_keyboard && !get_visible_rect().has_point(get_mouse_position())) {
		_close_pressed();
	}
}

Or by having smaller wait time for both timers, I have tried 0.01 and it's more user friendly than 0.3.

	submenu_timer = memnew(Timer);
	submenu_timer->set_wait_time(0.3);
	submenu_timer->set_one_shot(true);
	submenu_timer->connect("timeout", callable_mp(this, &PopupMenu::_submenu_timeout));
	add_child(submenu_timer, false, INTERNAL_MODE_FRONT);

	minimum_lifetime_timer = memnew(Timer);
	minimum_lifetime_timer->set_wait_time(0.3);
	minimum_lifetime_timer->set_one_shot(true);
	minimum_lifetime_timer->connect("timeout", callable_mp(this, &PopupMenu::_minimum_lifetime_timeout));
	add_child(minimum_lifetime_timer, false, INTERNAL_MODE_FRONT);

Edit: minimum_lifetime_timer is the issue, but submenu_timer can be changed to 0.1 instead of 0.01 to avoid showing it while moving the mouse over the items fast.

@WhalesState
Copy link
Contributor

WhalesState commented Sep 20, 2025

Also this may be related to why the popup menu didn't receive the motion event when the mouse entered.

@Koyper
Copy link
Contributor Author

Koyper commented Sep 20, 2025

Doesn't seem to fix the issue

The whack-a-mole continues - thanks for finding that. If you set the submenu timer to 0.1 does it still act the same per @WhalesState?

Is there a movement sequence you can replicate this on an Editor menu? I will set this up for myself and go back on the hunt.

Edit: minimum_lifetime_timer is the issue, but submenu_timer can be changed to 0.1 instead of 0.01 to avoid showing it while moving the mouse over the items fast.

I agree that 0.1 feels better - any reason we can't set the default to 0.1 instead of 0.3?

@KoBeWi
Copy link
Member

KoBeWi commented Sep 20, 2025

thanks for finding that

I just tested with the MRP from the issue you linked 🙃

If you set the submenu timer to 0.1 does it still act the same

Yes.

@Koyper
Copy link
Contributor Author

Koyper commented Sep 20, 2025

I just tested with the MRP from the issue you linked 🙃

That's pretty funny! My notoriety must be in reinventing the wheel. I've been testing with a much fancier menu that doesn't exhibit the issues as obviously for an as yet unknown reason.

@Koyper
Copy link
Contributor Author

Koyper commented Sep 20, 2025

I just tested with the MRP from the issue you linked 🙃

Okay, go to project settings and disable embed_subwindows and everything works perfectly. I should have tested this properly with embedded subwindows. This might indeed be related to something like #110093.

Also this may be related to why the popup menu didn't receive the motion event when the mouse entered.

There seems to be an issue regarding incorrect determination of the mouse leaving the safe area.

@WhalesState
Copy link
Contributor

There seems to be an issue regarding incorrect determination of the mouse leaving the safe area.

Yes, The issue is that the parent window only process one event while another window has focus, but when you disable embedding sub-windows they become just fake windows extending Control node so the mouse events works fine.

@WhalesState
Copy link
Contributor

WhalesState commented Sep 20, 2025

Okay, go to project settings and disable embed_subwindows and everything works perfectly. I should have tested this properly with embedded subwindows. This might indeed be related to something like #110093.

Also this issue is in both embedded and non-embedded windows so maybe you forgot to revert back the timer changes while testing or you test with the changes from this PR, i have tested with editor submenus in master and the issue also exists there by default and the editor is not embedding subwindows.

Screencast.From.2025-09-20.23-54-36.mp4

@WhalesState
Copy link
Contributor

WhalesState commented Sep 20, 2025

Also check what happens here, the root window receives one mouse event when another window is focused but when you focus it no other window receives any input, the settings window is exclusive so the parent window shouldn't receive any input at all.

Screencast.From.2025-09-20.23-45-59.mp4

@Koyper
Copy link
Contributor Author

Koyper commented Sep 20, 2025

i have tested with editor submenus in master and the issue also exists there by default and the editor is not embedding subwindows.

The PR fixed the issue with non-embedded windows and the Editor, though I found a couple of bugs there that I'm taking care of. What I've got working now for embedded widows is to emit the popup_hide signal when a submenu closes, which then creates an InputEventMouseMotion event that forces the parent menu to process a mouse movement over the currently hovered item. This seems to offer a solution for the time being. I will set the default submenu wait time to 0.1 and we can test it out to see if it's satisfactory. I think the ergonomics are better with the faster response.

What you demonstrated regarded no window receiving input, might certainly be related to the closed submenu blocking input to the parent menu, which is the source of the blocked submenu issue. This is a deeper problem with Window - fixing that might also take care of the "hung" activation of submenus that requires that callable loop to fix.

@Koyper Koyper force-pushed the fix_popup_menu_item_wont_open branch from a251137 to b51c6d2 Compare September 24, 2025 16:56
@Koyper Koyper requested a review from a team as a code owner September 24, 2025 16:56
@Koyper Koyper force-pushed the fix_popup_menu_item_wont_open branch from b51c6d2 to 49b8f45 Compare September 24, 2025 17:12
@Koyper
Copy link
Contributor Author

Koyper commented Sep 24, 2025

After a lot of frustration, I finally had to get rid of the minimum_lifetime_timer as it was the source of many of the issues. I replaced it with close_suspended_timer, which is only set in the parent menu, whereas minimum_lifetime_timer was being set in each submenu. The code now enforces submenus only being activated after any open submenus are closed. This eliminated the need for the hack double-calling _activate_submenu(), which I believe was being caused by a race condition where a prior submenu was in a closing state while the next was being activated.

The other major problem was the fixes for issues with embedded vs. non-embedded subwindows were completely different. The only way to get embedded subwindows to work was connecting popup_hide to spoof a mouse input on the hovered menu item that's triggered by the submenu closing.

When the mouse moves out of a submenu item, it's last relative motion state inside its menu item is preserved, and if it's moving into the lower right quadrant (or lower left if right-to-left layout), it will start the close_suspended_timer which runs for 0.5 seconds to allow the mouse to sweep into the open submenu across other menu items without triggering them to open or close the open submenu. This timing is modeled after the behavior of MacOS system menus, which seems reasonable.

Finally, I reduced the default submenu delay time from 0.3 to 0.1 seconds, as that feels more natural and responsive, and the fixes above make it unnecessary to prolong the opening of the submenu.

@akien-mga akien-mga removed the cherrypick:4.4 Considered for cherry-picking into a future 4.4.x release label Oct 7, 2025
@Koyper Koyper force-pushed the fix_popup_menu_item_wont_open branch from 49b8f45 to fd98bf6 Compare October 7, 2025 14:13
@Koyper
Copy link
Contributor Author

Koyper commented Oct 7, 2025

This push adds support for right-to-left layouts for the direction testing of mouse movements toward an open submenu.

Copy link
Member

@YeldhamDev YeldhamDev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like everything is in order. Fantastic work!

One final recommendation, however: I think this behavior of the cursor's arc deciding if the submenu stays open or not should be documented somewhere.

Copy link
Member

@bruvzg bruvzg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be working fine, I have not found any issues with it. Code looks good.

@akien-mga akien-mga removed the cherrypick:4.5 Considered for cherry-picking into a future 4.5.x release label Nov 6, 2025
@Koyper
Copy link
Contributor Author

Koyper commented Nov 6, 2025

One final recommendation, however: I think this behavior of the cursor's arc deciding if the submenu stays open or not should be documented somewhere.

@YeldhamDev Are you thinking in the class doc? I suppose it could go with submenu_timer_popup_delay?

@YeldhamDev
Copy link
Member

Yeah, that sounds like a good place.

@Koyper Koyper force-pushed the fix_popup_menu_item_wont_open branch from e77a391 to a024388 Compare November 6, 2025 16:43
@Koyper
Copy link
Contributor Author

Koyper commented Nov 6, 2025

This push includes all suggested changes.

Copy link
Contributor

@kitbdev kitbdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the mouse is in between two items with submenus, it can flicker:
System Info: Windows 11 (build 26100) - Multi-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 2070 (NVIDIA; 32.0.15.8108) - Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz (12 threads) - 15.82 GiB memory

Warning

Flickering

popupmenu_flicker.mp4

I could not get it to happen in single-window mode, its probably only multi-window mode. On master, trying this results in the submenu hiding.

@Koyper
Copy link
Contributor Author

Koyper commented Nov 10, 2025

I worked out a fix for this very pesky flickering issue, but ran into another DisplayServer bug that's thwarting wrapping this up. See #112620.

@Koyper
Copy link
Contributor Author

Koyper commented Nov 18, 2025

This push fixes the flickering issue and ensures proper function with per_pixel_transparency and content_scale_factor settings. Lots of work.

I also fixed a tricky bug that intermittently caused a submenu to close unexpectedly while moving the mouse toward it. This was caused by an unpredictable delay between when a timer is started and when is_stopped will return a valid result. This can unexpectedly be longer than 30 msecs. The result makes the mouse direction detection really reliable and the menu performance feels solid.

Everything seems to be functioning properly for both embedded and non-embedded windows, so give it a whirl.

@Repiteo Repiteo requested a review from kitbdev November 18, 2025 01:23
Copy link
Contributor

@kitbdev kitbdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flickering issue is fixed.
Looks good on both single-window mode and multi-window mode.

@Koyper Koyper force-pushed the fix_popup_menu_item_wont_open branch from 86edf21 to 70e6ac5 Compare November 18, 2025 14:35
@Koyper
Copy link
Contributor Author

Koyper commented Nov 18, 2025

Final push. Also nabbed the last source of the occasional error message.

@Repiteo Repiteo merged commit 1e1f044 into godotengine:master Nov 18, 2025
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Nov 18, 2025

Thanks!

@Koyper
Copy link
Contributor Author

Koyper commented Nov 18, 2025

Yahoo!

@Koyper Koyper deleted the fix_popup_menu_item_wont_open branch November 18, 2025 20:05
@CycloneRing
Copy link
Contributor

This PR broke popup menus on Windows in editor, Was this PR tested on Windows at all before merging??
Recently, each update seems to introduce new regressions.
On Windows simply hovering a pop up menu such as Project > Tools triggers this error :

ERR_FAIL_COND_MSG(submenu_popup->is_visible(), vformat("_activate_submenu should not be called on an open submenu - index: %d.", p_over));

@Koyper
Copy link
Contributor Author

Koyper commented Nov 19, 2025

The error message does not necessarily indicate any instability, but I'll see about tracking down what's happening. How are the popups working otherwise? Is there any sequence of movement that repeatably triggers the error message?

@CycloneRing
Copy link
Contributor

The error message does not necessarily indicate any instability, but I'll see about tracking down what's happening. How are the popups working otherwise? Is there any sequence of movement that repeatably triggers the error message?

Simply hovering on the popup menus in editor triggers the error, I cloned current upstream and built it without any changes. I'm on Windows 10 x64...

Replacing back the error block with

	if (submenu_popup->is_visible()) {
		return; // Already visible.
	}

Will fix it but I'm not sure if that's good.

@Koyper
Copy link
Contributor Author

Koyper commented Nov 19, 2025

Are you using single-window mode or default?

I can't replicate this on Win 10 at the moment, but since I believe it is being handled cleaning by the return I can look at reducing the error spam here and in a few other places. Is anyone else observing this error?

I also have a fix ready for error messages releated to global and native system menus.

@KoBeWi
Copy link
Member

KoBeWi commented Nov 19, 2025

I managed to replicate it once (in multi window mode). Seems like rather random error.

@Koyper
Copy link
Contributor Author

Koyper commented Nov 19, 2025

My best guess at the moment is there is a random and short delay between the submenu hiding and when submenu_popup->is_visible returns false. I think it's safe not to show the error.

@Koyper
Copy link
Contributor Author

Koyper commented Nov 19, 2025

Should be fixed by #112967

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

Projects

None yet

10 participants

X Tutup