2 Commits
Author SHA1 Message Date
TZGyn 8d795d0a28 Give up focus when opening a tray menu
Closing the panel was not enough: SimpleTopBar stayed frontmost, so
Spotlight's search field never appeared. Deactivate the app when a tray
item is selected so transient panels can take focus, and allow a little
more time before the bar re-hides.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4.1-flash
2026-09-27 10:30:20 +08:00
TZGyn e465acba12 Close the panel when a tray item is selected
Selecting a tile opened the app's menu while SimpleTopBar's own panel
stayed on top of it, and could leave menus such as Spotlight stranded
behind it. Dismiss the panel first so the selected menu is unobstructed.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4.1-flash
2026-09-27 10:26:09 +08:00
4 changed files with 27 additions and 4 deletions
+3
View File
@@ -141,6 +141,9 @@ preferred position controls where the hidden/kept boundary lands:
(`kAXPressAction`, then `kAXShowMenuAction`), and `resume()`s ~0.1s later. The (`kAXPressAction`, then `kAXShowMenuAction`), and `resume()`s ~0.1s later. The
menu is its own window anchored at the item's on-screen spot, so it survives menu is its own window anchored at the item's on-screen spot, so it survives
re-hiding. re-hiding.
- `press` first calls `onWillPress`, which `AppDelegate` wires to close the
popover **and** `NSApp.deactivate()`. Closing alone left our app frontmost, so
transient items (Spotlight's search field) still would not appear.
- **Cannot reveal a single icon.** The spacer hides a contiguous block to its - **Cannot reveal a single icon.** The spacer hides a contiguous block to its
left, so a press can only reveal the whole block. Isolating one item would left, so a press can only reveal the whole block. Isolating one item would
need either per-item ordering control (not available) or covering the icons need either per-item ordering control (not available) or covering the icons
+13
View File
@@ -132,6 +132,19 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
) )
) )
self.popover = popover self.popover = popover
// Selecting a tray item must dismiss our panel and give up focus,
// otherwise the panel stays on top of the menu and our app stays
// frontmost, which stops transient items (Spotlight's search field)
// from appearing at all.
Task { @MainActor [weak self] in
MenuBarItemManager.shared.onWillPress = {
self?.popover?.performClose(nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
NSApp.deactivate()
}
}
}
} }
@objc private func togglePopover() { @objc private func togglePopover() {
+10 -3
View File
@@ -30,6 +30,9 @@ final class MenuBarItemManager: ObservableObject {
private var observers: [NSObjectProtocol] = [] private var observers: [NSObjectProtocol] = []
/// Called just before pressing an item so the owner can close its panel.
var onWillPress: (() -> Void)?
private init() {} private init() {}
func start() { func start() {
@@ -113,6 +116,10 @@ final class MenuBarItemManager: ObservableObject {
/// hide again shortly after. The reveal is momentary rather than lasting the /// hide again shortly after. The reveal is momentary rather than lasting the
/// whole time the panel is open. /// whole time the panel is open.
func press(_ item: MenuBarItem) { func press(_ item: MenuBarItem) {
// Close our own panel first, otherwise it stays open on top of the
// menu that is about to appear (and can leave menus behind it).
onWillPress?()
let hider = MenuBarHider.shared let hider = MenuBarHider.shared
let needsReveal = hider.isHiding let needsReveal = hider.isHiding
guard needsReveal else { guard needsReveal else {
@@ -120,11 +127,11 @@ final class MenuBarItemManager: ObservableObject {
return return
} }
hider.suspend() hider.suspend()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
_ = self.performPress(item) _ = self.performPress(item)
// The menu is its own window, positioned at the item's on-screen // The menu is its own window, positioned at the item's on-screen
// spot, so the bar can be re-hidden almost immediately. // spot, so the bar can be re-hidden shortly after.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
hider.resume() hider.resume()
} }
} }
+1 -1
View File
@@ -2,7 +2,7 @@
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")" cd "$(dirname "$0")"
VERSION="${1:-0.4.2}" VERSION="${1:-0.4.4}"
swift build -c release swift build -c release