Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c5416d27e |
@@ -134,12 +134,13 @@ preferred position controls where the hidden/kept boundary lands:
|
|||||||
- Verified with `--diag-hide` across positions 100–400. Below ~200 Wi-Fi gets
|
- Verified with `--diag-hide` across positions 100–400. Below ~200 Wi-Fi gets
|
||||||
hidden too; above ~300 app icons (Now Playing, TextInput) leak back.
|
hidden too; above ~300 app icons (Now Playing, TextInput) leak back.
|
||||||
- `debug.spacerPos` (Double) overrides the seed for tuning without rebuilding.
|
- `debug.spacerPos` (Double) overrides the seed for tuning without rebuilding.
|
||||||
- Pressing a tray tile calls `MenuBarItemManager.press` (`kAXPressAction`, then
|
- Pressing a tray tile calls `MenuBarItemManager.press`. A hidden item sits
|
||||||
`kAXShowMenuAction`). Because a hidden item sits off-screen, its menu would
|
off-screen, and its menu would open off-screen too, so press briefly
|
||||||
open off-screen; `AppDelegate` therefore calls `MenuBarHider.suspend()`
|
`suspend()`s the hider, waits ~0.18s for the layout to settle, performs the
|
||||||
(`popoverWillShow`) to reveal items while the panel is open and `resume()`
|
accessibility action (`kAXPressAction`, then `kAXShowMenuAction`), and
|
||||||
0.6s after `popoverDidClose`. `suspend`/`resume` do not change the user's
|
`resume()`s ~1s later. The reveal is momentary, not for the whole time the
|
||||||
`enabled` preference.
|
panel is open. macOS does not let us place or anchor another app's menu
|
||||||
|
anywhere else, so a momentary reveal is the only way to show it in view.
|
||||||
- **Order matters**: `AppDelegate` must call `setupStatusItem()` *before*
|
- **Order matters**: `AppDelegate` must call `setupStatusItem()` *before*
|
||||||
`MenuBarHider.shared.prepare()`. If the hider contracts the bar first, the
|
`MenuBarHider.shared.prepare()`. If the hider contracts the bar first, the
|
||||||
hamburger is created into the collapsed space and lands off-screen.
|
hamburger is created into the collapsed space and lands off-screen.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ struct SimpleTopBarApp: App {
|
|||||||
/// full menu bar, a brand new item lands under the notch and is invisible;
|
/// full menu bar, a brand new item lands under the notch and is invisible;
|
||||||
/// writing `NSStatusItem Preferred Position <name>` into defaults puts it in a
|
/// writing `NSStatusItem Preferred Position <name>` into defaults puts it in a
|
||||||
/// visible slot, and the autosave name lets the user's own drag persist.
|
/// visible slot, and the autosave name lets the user's own drag persist.
|
||||||
final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
private var statusItem: NSStatusItem?
|
private var statusItem: NSStatusItem?
|
||||||
private var popover: NSPopover?
|
private var popover: NSPopover?
|
||||||
|
|
||||||
@@ -103,7 +103,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||||||
let popover = NSPopover()
|
let popover = NSPopover()
|
||||||
popover.behavior = .transient
|
popover.behavior = .transient
|
||||||
popover.animates = false
|
popover.animates = false
|
||||||
popover.delegate = self
|
|
||||||
popover.contentViewController = NSHostingController(
|
popover.contentViewController = NSHostingController(
|
||||||
rootView: TopBarView(
|
rootView: TopBarView(
|
||||||
model: AppListModel.shared,
|
model: AppListModel.shared,
|
||||||
@@ -114,20 +113,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
|||||||
self.popover = popover
|
self.popover = popover
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reveal the hidden menu bar items while the panel is open so pressing a
|
|
||||||
// tray icon opens that app's menu at its real, on-screen position.
|
|
||||||
func popoverWillShow(_ notification: Notification) {
|
|
||||||
MenuBarHider.shared.suspend()
|
|
||||||
}
|
|
||||||
|
|
||||||
func popoverDidClose(_ notification: Notification) {
|
|
||||||
// Small delay so a menu just opened from a tray tile is fully on screen
|
|
||||||
// before the bar contracts again.
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
|
|
||||||
MenuBarHider.shared.resume()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func togglePopover() {
|
@objc private func togglePopover() {
|
||||||
guard let popover, let button = statusItem?.button else { return }
|
guard let popover, let button = statusItem?.button else { return }
|
||||||
if popover.isShown {
|
if popover.isShown {
|
||||||
|
|||||||
@@ -103,10 +103,30 @@ final class MenuBarItemManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Opens the real menu bar item by pressing its accessibility element, so
|
/// Opens the real menu bar item, so its menu/popover appears.
|
||||||
/// its menu/popover appears at its true screen location.
|
///
|
||||||
@discardableResult
|
/// A hidden item sits off-screen, and its menu would open off-screen too.
|
||||||
func press(_ item: MenuBarItem) -> Bool {
|
/// So briefly reveal the items, let the layout settle, press the item, then
|
||||||
|
/// hide again shortly after. The reveal is momentary rather than lasting the
|
||||||
|
/// whole time the panel is open.
|
||||||
|
func press(_ item: MenuBarItem) {
|
||||||
|
let hider = MenuBarHider.shared
|
||||||
|
let needsReveal = hider.isHiding
|
||||||
|
guard needsReveal else {
|
||||||
|
_ = performPress(item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hider.suspend()
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.18) {
|
||||||
|
_ = self.performPress(item)
|
||||||
|
// Let the menu finish appearing before the bar contracts again.
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||||
|
hider.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func performPress(_ item: MenuBarItem) -> Bool {
|
||||||
if AXUIElementPerformAction(item.element, kAXPressAction as CFString) == .success {
|
if AXUIElementPerformAction(item.element, kAXPressAction as CFString) == .success {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user