Revert "Hide menu bar icons with a cover instead of moving them off screen"

This reverts commit 3e57efe312.
This commit is contained in:
2026-09-27 10:13:10 +08:00
parent 3e57efe312
commit 569abe967f
6 changed files with 143 additions and 246 deletions
+32 -26
View File
@@ -120,32 +120,38 @@ no control over this, so the app creates its own `NSStatusItem` and:
`/tmp/simpletopbar-items.txt`; use it to verify the icon is not under the notch
(x should be ~1200, not ~765).
## Hiding other menu bar items (cover)
## Hiding other menu bar items
`MenuBarHider` drives `MenuBarCover`, which hides other apps' icons by drawing
our own frosted window ("cover") over them instead of moving them off-screen.
Because the icons keep their real positions, `press` opens their menus in place
with no reveal. Verified with `--diag-cover` (cover windows at level 26) and
`--diag-press` (the WireGuard menu opens at a positive x, not off-screen).
`MenuBarHider` creates a zero-width spacer `NSStatusItem` at launch and grows its
length to `hiddenLength` (10_000) to push the icons on its left off-screen. It is
**on by default** (`hideMenuBarItems` defaults to true, then persisted). Its
preferred position controls where the hidden/kept boundary lands:
- `MenuBarCover.shouldHide` covers everything except the Apple keep-list
(Wi-Fi, Battery, Clock, Control Center, Spotlight/Search).
- `runs(from:)` merges adjacent item frames into contiguous cover windows;
`subtracting(_:from:)` carves out the hamburger so it is never covered
(`MenuBarItemManager.refresh` passes `avoid:` = our own item's frame).
- Frames come from AX (`kAXPositionAttribute` + `kAXSizeAttribute`) with a
**top-left origin**; `windowFrame(for:primaryHeight:)` flips Y for NSWindow.
- The cover window level is 26 (just above status items at 25, below menus at
~101), so opened menus draw on top of it.
- `ignoresMouseEvents = false` makes the covered strip swallow clicks so the
hidden icons cannot be clicked through.
### Legacy: off-screen spacer
An earlier approach used a zero-width spacer `NSStatusItem` grown to width
10_000 to push icons off the left edge. It is no longer used because it required
a visible reveal to open a menu. Findings if it is ever revived: preferred
position ~250 kept Wi-Fi while hiding apps, and `debug.spacerPos` overrode it.
- Seed **250** (default) puts the boundary just left of Wi-Fi, so every
third-party app icon goes to x < 0 (cmux, Kapture, moomoo, WireGuard,
TextInputMenuAgent, Now Playing) while **Wi-Fi (x≈1173), Spotlight, Clock,
Battery, Control Center and our hamburger stay visible**.
- 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`. A hidden item sits
off-screen, and its menu opens off-screen too (verified with `--diag-press`:
the menu window lands at negative x). So press briefly `suspend()`s the hider,
waits ~0.2s for the layout to settle, performs the accessibility action
(`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
re-hiding.
- **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
need either per-item ordering control (not available) or covering the icons
with an overlay window instead of moving them off-screen.
- **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.
- **Wi-Fi/Spotlight/Clock/Battery/Control Center cannot be hidden**: macOS pins
them to the far right and a third-party item can never be inserted to their
right, so the spacer has nothing to push them with. This is a macOS
constraint, not a bug.
## Gotchas
@@ -156,7 +162,7 @@ position ~250 kept Wi-Fi while hiding apps, and `debug.spacerPos` overrode it.
- Reading `activationPolicy` is reliable but must happen on the main thread;
keep all `NSWorkspace` access inside the model.
- The menu bar tray needs Accessibility permission; do not add Screen Recording
or Apple Events usage. `MenuBarHider` is restored on quit, and because the
cover windows belong to the process, a crash removes them automatically.
or Apple Events usage. `MenuBarHider` must always be restored on quit, but
because a status item dies with its process, a crash cleans up on its own.
- `.gitignore` covers `.build/`, `SimpleTopBar.app/`, `dist/` — build artifacts
should never be committed.