1 Commits
Author SHA1 Message Date
TZGyn dec8d60aa3 Hide other menu bar icons by default
The point of the app is a single hamburger that replaces the tray, so
hiding the other icons should be the out-of-the-box state. Remember the
user's choice, and create the hamburger before the hider so it is not
itself pushed off-screen.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4.1-flash
2026-09-27 02:15:01 +08:00
4 changed files with 26 additions and 12 deletions
+5 -1
View File
@@ -119,7 +119,8 @@ no control over this, so the app creates its own `NSStatusItem` and:
## Hiding other menu bar items
`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. 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:
- Seed **250** (default) puts the boundary just left of Wi-Fi, so every
@@ -129,6 +130,9 @@ 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.
- **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
+3 -2
View File
@@ -28,8 +28,9 @@ still works.
macOS has no public API to move another app's status item. The **Hide items from
the menu bar** toggle uses the spacer technique: a zero-width status item is
created at launch whose length is expanded to push the other icons off-screen so
they only live in this panel. It is off by default and always undone on quit; if
the app dies, macOS removes the spacer automatically.
they only live in this panel. It is **on by default** (the choice is
remembered), always undone on quit, and if the app dies macOS removes the spacer
automatically.
What it can and cannot hide:
+3 -1
View File
@@ -43,8 +43,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
AppListModel.shared.start()
MenuBarItemManager.shared.start()
MenuBarHider.shared.prepare()
// Create the hamburger before the hider so it is placed in the visible
// part of the bar; the hider then contracts the space around it.
setupStatusItem()
MenuBarHider.shared.prepare()
if CommandLine.arguments.contains("--diag") {
DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
+15 -8
View File
@@ -151,10 +151,10 @@ final class MenuBarItemManager: ObservableObject {
}
/// Best-effort menu bar item hiding. A zero-width spacer status item is created
/// at launch; expanding its length to a huge value pushes the items to its left
/// at launch; expanding its length to a huge value pushes the other apps' icons
/// off the visible menu bar, so they can only be reached through SimpleTopBar's
/// panel. Reliable only when SimpleTopBar starts at login (its spacer then sits
/// to the right of later-launched apps' items). Always restored on quit.
/// panel. On by default; the choice is persisted. Always restored on quit, and
/// a crash cleans up automatically because status items die with the process.
@MainActor
final class MenuBarHider: ObservableObject {
static let shared = MenuBarHider()
@@ -162,17 +162,24 @@ final class MenuBarHider: ObservableObject {
/// Width that pushes the neighbouring items off the visible menu bar.
static let hiddenLength: CGFloat = 10_000
private static let defaultsKey = "hideMenuBarItems"
@Published private(set) var isHiding = false
@Published var enabled = false {
@Published var enabled: Bool {
didSet {
guard enabled != oldValue else { return }
UserDefaults.standard.set(enabled, forKey: Self.defaultsKey)
apply(enabled)
}
}
private var spacer: NSStatusItem?
private init() {}
private init() {
// Hide by default: the menu bar then shows only SimpleTopBar's hamburger
// plus the Apple items macOS will not let us move.
enabled = UserDefaults.standard.object(forKey: Self.defaultsKey) as? Bool ?? true
}
/// Create the spacer early (at launch) so it lands as far right as possible.
func prepare() {
@@ -191,19 +198,19 @@ final class MenuBarHider: ObservableObject {
item.autosaveName = name
item.button?.isHidden = true
spacer = item
apply(enabled)
}
private func apply(_ hiding: Bool) {
prepare()
spacer?.length = hiding ? Self.hiddenLength : 0
isHiding = hiding
}
/// Put the menu bar back the way we found it. Called on quit.
/// Put the menu bar back the way we found it. Called on quit. Leaves the
/// user's preference intact.
func restore() {
spacer?.length = 0
isHiding = false
enabled = false
}
}