fix(contextmenu): dismiss the flyout when the owning window loses focus - #572
Merged
Merged
Conversation
Right-clicking to open the OS-looking context menu and then clicking into another application left the menu on screen. Each backend detects outside clicks by watching its own process only. On Windows the popup HWND uses a thread-local WH_MOUSE hook (deliberately, to replace a SetCapture monitor that made the parent receive WM_KILLFOCUS and flipped WindowInfo.isWindowFocused false). On Linux the scene layer is told about outside presses by the parent window's own pointer input, so a press that never lands on our surface is never reported — on X11 and Wayland alike, and Wayland forbids watching another surface's clicks by design. The global XI2 root monitor belongs to the standalone popup host (tray popups), not to this flyout. No backend dismissed on a focus change with no click behind it either (Alt+Tab, the taskbar, a notification stealing activation). Window focus is the one signal every backend delivers, so dismiss on the focused -> unfocused transition. LocalWindowInfo is read from the parent scene: inside Popup the layer publishes its own WindowInfo with isWindowFocused pinned to true. A leading unfocused run is dropped so a backend that has not reported focus yet when the menu opens cannot dismiss it immediately -- the flyout counterpart of the not-yet-active accessory app fixed for NSMenu in a62349e. macOS is unaffected: that path is a real NSMenu and already dismisses itself.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Right-click to open the OS-looking context menu, then click into another application — the menu stays on screen. It should dismiss, like every native menu does.
Why
Each backend detects outside clicks by watching its own process only:
NSMenuWH_MOUSEhook on the popup HWNDTaoPopupHostLinux.registerOutsidePressListener)The Windows hook is thread-local deliberately — it replaced a
SetCapturemonitor that forced the popup foreground, made the parent receiveWM_KILLFOCUSand flippedWindowInfo.isWindowFocusedfalse (see the comment innucleus_tao_windows_popup.c).On Linux the flyout is a scene layer, and a press that never lands on our surface is never reported — X11 and Wayland alike. Wayland forbids watching another surface's clicks by design. The global XI2 root-window monitor people may think of belongs to the standalone popup host (tray popups), not to this flyout.
On top of that, no backend dismissed on a focus change with no click behind it: Alt+Tab, the taskbar, a notification stealing activation.
Fix
Window focus is the one signal every backend does deliver (
WindowEvent::Focusedfrom tao — GDK focus-out on X11,wl_keyboard.leaveon Wayland). Dismiss the flyout on the focused → unfocused transition.Two details:
LocalWindowInfois read from the parent scene. InsidePopupthe layer publishes its ownWindowInfowithisWindowFocusedpinned totrue.NSMenuin a62349e.No native code touched. macOS is unaffected.
Test
ContextMenuFocusDismissTestcovers the transition logic: dismiss on focus loss, no dismiss while focused, no dismiss on a leading unfocused run, re-dismiss after a regain.:nucleus-application:test,ktlintCheckanddetektpass.Not verified on screen — reproducing needs a manual click into another app, which does not automate. A Wayland session in particular is reasoning-from-code, not observed. Residual gap either way: clicking a surface that takes no keyboard focus (some layer-shell docks, the desktop background on some compositors) produces no focus-out and leaves the menu open — not a regression.