fix(next): menus opened inside a native popup keep the popup open (bookmark bubble's Folder picker)
Bug
The bookmark edit bubble ("Bookmark added" / "Edit bookmark", cmux.bookmarks.edit) closed as soon as its Folder picker opened.
The bubble is a NativePopupHost panel. The Folder picker is a ThemedPicker, a pop-up button whose menu is AppKit's own NSMenu, which runs in a window of its own. When that menu opened it took the keys from the bubble, and the panel's resignKey() called the host's onLostKeys, which treated the key loss as a dismiss. The host's click and Escape monitors had the same gap: they only knew the panel itself and the toggle control, so a menu opened from inside the popup counted as "outside".
Fix
NativePopupHost now treats a menu opened from inside its popup, and a window parented under its panel, as part of the popup:
- While the popup is shown it watches
NSMenu.didBeginTrackingNotificationanddidEndTrackingNotification. A menu counts as the popup's own when a view in the panel owns it (a pop-up button's menu, a context menu) or when the event that opened it was in the popup. The menu bar's menus never count. - While one of those menus is tracking, or a window parented under the panel is key, losing the keys does not dismiss the popup, a click outside does not dismiss it, and Escape closes only the menu.
- When the last such menu ends, the popup takes the keys back. An
ActiveAppKeyPanelstill only takes them while the app is active.
So a pick, a click elsewhere or Escape closes only the inner menu, and the bubble stays.
Unchanged:
- Opening. Menus still open on the click with no fade-in, and the close fade is untouched.
- The other
NativePopupHostcallers (Page Info) close themselves before opening children, so they never hit the new path. - With no menu of its own open, a popup that loses the keys to another window or to an unrelated menu still closes, as before.
The menu observers are added in watch and removed in unwatch, together with the existing monitors, so they live only while the popup is shown.
Tests
CmuxNextDesignTests/NativePopupHostChildMenuTests builds a visible window with a bubble-shaped keyed popup that holds a ThemedPopUpButton, the control ThemedPicker makes:
thePickersMenuTakingTheKeysLeavesTheBubbleOpen: the picker's menu begins tracking and the panel resigns key, then a pick ends the menu. The bubble stays open andonDismissnever fires. It fails before the fix.losingTheKeysToSomethingElseStillClosesTheBubble: an unrelated menu begins tracking and the panel resigns key. The bubble closes once. It passes before and after.
The first commit adds only the tests, so it shows the red; the second commit is the fix.
Risk: low. The change is contained in the shared native popup host, and nothing changes for popups that open no child menu.
š¤ Generated with Claude Code
Agent session: agent session