2 Commits
Author SHA1 Message Date
TZGyn 61d2689cbc Reveal hidden tray icons while the panel is open
Pressing a tray tile did nothing visible because the item was hidden
off-screen, so its menu opened where it could not be seen. Temporarily
put the icons back on the menu bar while the panel is open, so clicking
a tile opens that app's real menu in view.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4.1-flash
2026-09-27 02:25:50 +08:00
TZGyn 8df3e2cd94 Keep menu bar tray icons at their natural aspect ratio
Forcing every icon into a square stretched wide symbols such as the
battery into a blocky shape, making them hard to recognise. Scale to fit
instead so wide icons stay long and short.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4.1-flash
2026-09-27 02:21:19 +08:00
4 changed files with 41 additions and 1 deletions
+6
View File
@@ -134,6 +134,12 @@ 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
`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.
- **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.
+16 -1
View File
@@ -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 { final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate {
private var statusItem: NSStatusItem? private var statusItem: NSStatusItem?
private var popover: NSPopover? private var popover: NSPopover?
@@ -103,6 +103,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
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,
@@ -113,6 +114,20 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
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 {
+16
View File
@@ -252,6 +252,22 @@ final class MenuBarHider: ObservableObject {
isHiding = hiding isHiding = hiding
} }
/// Temporarily reveal the items (e.g. while the panel is open) so pressing
/// a tray icon opens its menu at its real, on-screen position. Keeps the
/// user's `enabled` preference untouched.
func suspend() {
guard enabled else { return }
spacer?.length = 0
isHiding = false
}
/// Restore hiding after a `suspend()`.
func resume() {
guard enabled else { return }
spacer?.length = Self.hiddenLength
isHiding = true
}
/// Put the menu bar back the way we found it. Called on quit. Leaves the /// Put the menu bar back the way we found it. Called on quit. Leaves the
/// user's preference intact. /// user's preference intact.
func restore() { func restore() {
+3
View File
@@ -245,8 +245,11 @@ private struct MenuBarTile: View {
Button { Button {
menuBar.press(item) menuBar.press(item)
} label: { } label: {
// Preserve the symbol's aspect ratio: forcing wide symbols like the
// battery into a square stretches them into a blocky shape.
Image(nsImage: item.icon) Image(nsImage: item.icon)
.resizable() .resizable()
.scaledToFit()
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.padding(6) .padding(6)
.background( .background(