From 59683ee8a937c65fef69f019337acd6cb7e1829a Mon Sep 17 00:00:00 2001 From: maxsoch Date: Tue, 7 Jul 2026 05:58:23 +0200 Subject: [PATCH] fix: bring windows to front and into Cmd-Tab while open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accessory apps never appear in the Cmd-Tab switcher and macOS does not activate them when they open windows — the log window opened behind other apps (settings only worked via a manual activate call). presentWindow(of:) now switches the activation policy to .regular while a BrewBar window is open, giving a Dock icon and a Cmd-Tab entry; a willClose observer drops back to .accessory once the last window is gone. Used by both showLogs and showSettings. Co-Authored-By: Claude Fable 5 --- BrewBar/BrewBarApp.swift | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/BrewBar/BrewBarApp.swift b/BrewBar/BrewBarApp.swift index 0499a28..81c3f89 100644 --- a/BrewBar/BrewBarApp.swift +++ b/BrewBar/BrewBarApp.swift @@ -48,6 +48,13 @@ class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_: Notification) { Settings.registerDefaults() + NotificationCenter.default.addObserver( + self, + selector: #selector(windowWillClose(_:)), + name: NSWindow.willCloseNotification, + object: nil + ) + statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) statusItem.button?.imagePosition = .imageLeft // icon stays visible next to the count setMenuBarIcon("brewbar-uptodate") @@ -117,10 +124,31 @@ class AppDelegate: NSObject, NSApplicationDelegate { settingsWindow = SettingsWindowController(appDelegate: self) } settingsWindow?.syncUI() - settingsWindow?.showWindow(nil) - // accessory app: without activation the window can open behind others + presentWindow(of: settingsWindow) + } + + // MARK: - Window presentation + + /// Accessory apps never appear in Cmd-Tab and their windows can open + /// behind other apps. While one of our windows is open we temporarily + /// become a regular app (Dock icon + Cmd-Tab entry); windowWillClose + /// drops back to accessory once the last window is gone. + func presentWindow(of controller: NSWindowController?) { + NSApp.setActivationPolicy(.regular) + controller?.showWindow(nil) NSApp.activate(ignoringOtherApps: true) - settingsWindow?.window?.makeKeyAndOrderFront(nil) + controller?.window?.makeKeyAndOrderFront(nil) + } + + @objc func windowWillClose(_: Notification) { + // async so the closing window's isVisible has flipped to false + DispatchQueue.main.async { + let ourWindows = [self.logWindow?.window, self.settingsWindow?.window] + let stillOpen = ourWindows.compactMap { $0 }.contains { $0.isVisible } + if !stillOpen { + NSApp.setActivationPolicy(.accessory) + } + } } func resolveBrewPath() -> String { @@ -433,7 +461,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { if logWindow == nil { logWindow = LogWindowController() } - logWindow?.showWindow(nil) logWindow?.update(text: logBuffer) + presentWindow(of: logWindow) } }