Keep all system icons visible and click Spotlight properly

The spacer boundary cut off Now Playing and the input source, which
should stay like Wi-Fi, so move it left of every Apple item. Spotlight's
status item ignores the accessibility press action, so click it at its
real position instead.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4.1-flash
This commit is contained in:
2026-09-27 10:35:20 +08:00
parent 8d795d0a28
commit abb0b548bf
4 changed files with 44 additions and 7 deletions
+39 -2
View File
@@ -11,6 +11,8 @@ struct MenuBarItem: Identifiable, Equatable {
let icon: NSImage let icon: NSImage
/// True for Apple's own items (Control Center, Spotlight, …). /// True for Apple's own items (Control Center, Spotlight, …).
let isSystem: Bool let isSystem: Bool
/// Frame in Accessibility coordinates (origin top-left of the main screen).
let frame: CGRect?
let element: AXUIElement let element: AXUIElement
static func == (lhs: MenuBarItem, rhs: MenuBarItem) -> Bool { static func == (lhs: MenuBarItem, rhs: MenuBarItem) -> Bool {
@@ -75,10 +77,11 @@ final class MenuBarItemManager: ObservableObject {
var seenTitles = Set<String>() var seenTitles = Set<String>()
for (index, child) in children.enumerated() { for (index, child) in children.enumerated() {
let position = pointAttribute(child, kAXPositionAttribute)
// Control Center reports every module it owns, including the // Control Center reports every module it owns, including the
// ones not in the menu bar, which are parked off-screen around // ones not in the menu bar, which are parked off-screen around
// y=982. Keep only items actually sitting in the bar. // y=982. Keep only items actually sitting in the bar.
if let position = pointAttribute(child, kAXPositionAttribute), position.y > 60 { if let position, position.y > 60 {
continue continue
} }
@@ -90,6 +93,14 @@ final class MenuBarItemManager: ObservableObject {
// "Control Center"; show each distinct title once per app. // "Control Center"; show each distinct title once per app.
if !seenTitles.insert(title).inserted { continue } if !seenTitles.insert(title).inserted { continue }
let size = sizeAttribute(child, kAXSizeAttribute)
let frame: CGRect?
if let position, let size {
frame = CGRect(origin: position, size: size)
} else {
frame = nil
}
found.append(MenuBarItem( found.append(MenuBarItem(
id: "\(pid)-\(index)", id: "\(pid)-\(index)",
pid: pid, pid: pid,
@@ -97,6 +108,7 @@ final class MenuBarItemManager: ObservableObject {
title: title, title: title,
icon: Self.icon(forTitle: title, app: app), icon: Self.icon(forTitle: title, app: app),
isSystem: Self.isSystemBundle(app.bundleIdentifier), isSystem: Self.isSystemBundle(app.bundleIdentifier),
frame: frame,
element: child element: child
)) ))
} }
@@ -138,12 +150,27 @@ final class MenuBarItemManager: ObservableObject {
} }
private func performPress(_ item: MenuBarItem) -> Bool { private func performPress(_ item: MenuBarItem) -> Bool {
// Spotlight's status item accepts the accessibility press action but
// does nothing with it; it needs a genuine click.
if item.appName.lowercased().contains("spotlight"), let frame = item.frame {
postClick(at: CGPoint(x: frame.midX, y: frame.midY))
return true
}
if AXUIElementPerformAction(item.element, kAXPressAction as CFString) == .success { if AXUIElementPerformAction(item.element, kAXPressAction as CFString) == .success {
return true return true
} }
return AXUIElementPerformAction(item.element, kAXShowMenuAction as CFString) == .success return AXUIElementPerformAction(item.element, kAXShowMenuAction as CFString) == .success
} }
/// Posts a real left click at a point in Accessibility/global coordinates.
private func postClick(at point: CGPoint) {
let source = CGEventSource(stateID: .hidSystemState)
let down = CGEvent(mouseEventSource: source, mouseType: .leftMouseDown, mouseCursorPosition: point, mouseButton: .left)
let up = CGEvent(mouseEventSource: source, mouseType: .leftMouseUp, mouseCursorPosition: point, mouseButton: .left)
down?.post(tap: .cghidEventTap)
up?.post(tap: .cghidEventTap)
}
// MARK: - Classification // MARK: - Classification
/// Apple's own menu bar items (Control Center, Spotlight, the input-source /// Apple's own menu bar items (Control Center, Spotlight, the input-source
@@ -235,6 +262,16 @@ final class MenuBarItemManager: ObservableObject {
guard AXValueGetValue(value as! AXValue, .cgPoint, &point) else { return nil } guard AXValueGetValue(value as! AXValue, .cgPoint, &point) else { return nil }
return point return point
} }
nonisolated static func sizeAttribute(_ element: AXUIElement, _ attribute: String) -> CGSize? {
var value: CFTypeRef?
guard AXUIElementCopyAttributeValue(element, attribute as CFString, &value) == .success,
let value,
CFGetTypeID(value) == AXValueGetTypeID() else { return nil }
var size = CGSize.zero
guard AXValueGetValue(value as! AXValue, .cgSize, &size) else { return nil }
return size
}
} }
/// Best-effort menu bar item hiding. A zero-width spacer status item is created /// Best-effort menu bar item hiding. A zero-width spacer status item is created
@@ -279,7 +316,7 @@ final class MenuBarHider: ObservableObject {
if let override = UserDefaults.standard.object(forKey: "debug.spacerPos") as? Double { if let override = UserDefaults.standard.object(forKey: "debug.spacerPos") as? Double {
UserDefaults.standard.set(override, forKey: key) UserDefaults.standard.set(override, forKey: key)
} else if UserDefaults.standard.object(forKey: key) == nil { } else if UserDefaults.standard.object(forKey: key) == nil {
UserDefaults.standard.set(250.0, forKey: key) UserDefaults.standard.set(350.0, forKey: key)
} }
let item = NSStatusBar.system.statusItem(withLength: 0) let item = NSStatusBar.system.statusItem(withLength: 0)
item.autosaveName = name item.autosaveName = name
+3 -3
View File
@@ -103,9 +103,9 @@ func runSelfTest() {
check(MenuBarItemManager.children(of: systemWide) == nil, "no children on a non-app element") check(MenuBarItemManager.children(of: systemWide) == nil, "no children on a non-app element")
let element = AXUIElementCreateSystemWide() let element = AXUIElementCreateSystemWide()
let first = MenuBarItem(id: "1-0", pid: 1, appName: "A", title: "T", icon: NSImage(), isSystem: false, element: element) let first = MenuBarItem(id: "1-0", pid: 1, appName: "A", title: "T", icon: NSImage(), isSystem: false, frame: nil, element: element)
let same = MenuBarItem(id: "1-0", pid: 1, appName: "A", title: "T", icon: NSImage(), isSystem: false, element: element) let same = MenuBarItem(id: "1-0", pid: 1, appName: "A", title: "T", icon: NSImage(), isSystem: false, frame: nil, element: element)
let different = MenuBarItem(id: "1-1", pid: 1, appName: "A", title: "T", icon: NSImage(), isSystem: false, element: element) let different = MenuBarItem(id: "1-1", pid: 1, appName: "A", title: "T", icon: NSImage(), isSystem: false, frame: nil, element: element)
check(first == same, "menu bar items compare by id and title") check(first == same, "menu bar items compare by id and title")
check(first != different, "different ids are not equal") check(first != different, "different ids are not equal")
+1 -1
View File
@@ -95,7 +95,7 @@ struct TopBarView: View {
.controlSize(.mini) .controlSize(.mini)
.padding(.horizontal, 12) .padding(.horizontal, 12)
.padding(.top, 6) .padding(.top, 6)
.help("Best-effort: pushes other apps' icons off the menu bar so they only live here. Apple's Wi-Fi, Spotlight, Clock, Battery and Control Center stay visible; macOS won't let any third-party app move them.") .help("Best-effort: pushes other apps' icons off the menu bar so they only live here. Apple's system items (Wi-Fi, Now Playing, Clock, Battery, Control Center, Spotlight) stay visible; macOS won't let any third-party app move them.")
} }
} }
} }
+1 -1
View File
@@ -2,7 +2,7 @@
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")" cd "$(dirname "$0")"
VERSION="${1:-0.4.4}" VERSION="${1:-0.4.5}"
swift build -c release swift build -c release