fix(toolbar): open the quick switcher panel on macOS 15#1806
Conversation
f29edaf to
631a76c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f29edaf273
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| func windowWillCloseClearsPresentedState() { | ||
| let controller = QuickSwitcherPanelController() | ||
| controller.present(Text(verbatim: "content"), over: nil) | ||
| controller.windowWillClose(Notification(name: NSWindow.willCloseNotification)) |
There was a problem hiding this comment.
Close the panel instead of faking willClose
Because present has just ordered the panel front, calling the delegate method directly only drops the controller's reference; it never closes or orders out the NSPanel. In this test target AppKit keeps ordered-in windows alive, so this test (and the similar no-op test below) can leave an orphan key panel around to affect later tests; exercise the real close path instead of invoking windowWillClose by hand.
Useful? React with 👍 / 👎.
Problem
On macOS 15 the Quick Switcher never opens. Clicking the toolbar button or pressing the shortcut does nothing. The same build works on macOS 26.
Root cause
The 0.51.0 floating panel rewrite (#1644) sized the panel from
NSHostingView.fittingSize, measured before any layout pass, and relied on SwiftUI's automatic window sizing to correct it afterwards. Apple removed that eager sizing in macOS 15 (release note issue 118586136), so on Sequoia the borderless panel stays at zero size: invisible. The invisible panel never becomes key, so the resign-key dismissal never clearsisPresented, and every later trigger hits the toggle's "dismiss" branch. macOS 26 only worked because the init-time measurement happened to land on the right size there.The automatic sizing machinery is also unsafe in the other direction: when the window size and SwiftUI's ideal size disagree,
NSHostingView.updateAnimatedWindowSizerecurses until the stack overflows (reproduced twice in the test host, confirmed by crash report stacks).Fix
QuickSwitcherPanel.swiftonly. The panel no longer depends on automatic window sizing in either direction:NSHostingController.sizeThatFits(in:)against the screen's visible frame and applied before the panel is ordered front.sizingOptionsis empty.onGeometryChange; the controller applies it withsetContentSize, which flows through the existingwindowDidResizere-anchoring..nonactivatingPanel, the panel closes itself inresignKey()and on Escape viaclose(), and the controller clears its state only inwindowWillClose(which also detaches the content so SwiftUI tears it down and the key event monitor is removed, fixing a pre-existing monitor leak). The panel stays non-main so the document window keeps its active appearance. A stale "presented" flag can no longer occur.Entry points, panel content, and the search field are unchanged.
Tests
QuickSwitcherPanelControllerTestsgrew from 6 to 10 cases, including a non-zero-frame assertion after construction that fails against the old code. All pass locally. No UI automation: floating panel key-window ordering is not deterministic in headless CI, and CI has no macOS 15 runner.Manual verification on a macOS 15 machine still needed: toolbar button opens, shortcut opens, second trigger closes, Escape closes, click outside closes, panel stays top-centered as results load.