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
|
||||
hidden too; above ~300 app icons (Now Playing, TextInput) leak back.
|
||||
- `debug.spacerPos` (Double) overrides the seed for tuning without rebuilding.
|
||||
- Pressing a tray tile calls `MenuBarItemManager.press` (`kAXPressAction`, then
|
||||
`kAXShowMenuAction`). Because a hidden item sits off-screen, its menu would
|
||||
open off-screen; `AppDelegate` therefore calls `MenuBarHider.suspend()`
|
||||
(`popoverWillShow`) to reveal items while the panel is open and `resume()`
|
||||
0.6s after `popoverDidClose`. `suspend`/`resume` do not change the user's
|
||||
`enabled` preference.
|
||||
- Pressing a tray tile calls `MenuBarItemManager.press`. A hidden item sits
|
||||
off-screen, and its menu would open off-screen too, so press briefly
|
||||
`suspend()`s the hider, waits ~0.18s for the layout to settle, performs the
|
||||
accessibility action (`kAXPressAction`, then `kAXShowMenuAction`), and
|
||||
`resume()`s ~1s later. The reveal is momentary, not for the whole time the
|
||||
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*
|
||||
`MenuBarHider.shared.prepare()`. If the hider contracts the bar first, the
|
||||
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;
|
||||
/// writing `NSStatusItem Preferred Position <name>` into defaults puts it in a
|
||||
/// 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 popover: NSPopover?
|
||||
|
||||
@@ -103,7 +103,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
||||
let popover = NSPopover()
|
||||
popover.behavior = .transient
|
||||
popover.animates = false
|
||||
popover.delegate = self
|
||||
popover.contentViewController = NSHostingController(
|
||||
rootView: TopBarView(
|
||||
model: AppListModel.shared,
|
||||
@@ -114,20 +113,6 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
|
||||
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() {
|
||||
guard let popover, let button = statusItem?.button else { return }
|
||||
if popover.isShown {
|
||||
|
||||
@@ -103,10 +103,30 @@ final class MenuBarItemManager: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens the real menu bar item by pressing its accessibility element, so
|
||||
/// its menu/popover appears at its true screen location.
|
||||
@discardableResult
|
||||
func press(_ item: MenuBarItem) -> Bool {
|
||||
/// Opens the real menu bar item, so its menu/popover appears.
|
||||
///
|
||||
/// A hidden item sits off-screen, and its menu would open off-screen too.
|
||||
/// 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 {
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user