From c5f0709e7ed9711bc21735cb5941d3303e0ae953 Mon Sep 17 00:00:00 2001 From: McNight Date: Sun, 16 Feb 2025 15:02:44 +0100 Subject: [PATCH] [macOS] feat: Add "quick-terminal" option to "macos-hidden" config --- macos/Sources/App/macOS/AppDelegate.swift | 12 ++++++++++++ .../Features/Terminal/TerminalManager.swift | 8 +++++++- macos/Sources/Ghostty/Ghostty.Config.swift | 1 + macos/Sources/Ghostty/Package.swift | 4 ++++ .../Sources/Helpers/NSApplication+Extension.swift | 15 +++++++++++++++ src/config/Config.zig | 1 + 6 files changed, 40 insertions(+), 1 deletion(-) diff --git a/macos/Sources/App/macOS/AppDelegate.swift b/macos/Sources/App/macOS/AppDelegate.swift index 12f8f8c31..e27bf2f8e 100644 --- a/macos/Sources/App/macOS/AppDelegate.swift +++ b/macos/Sources/App/macOS/AppDelegate.swift @@ -403,6 +403,12 @@ class AppDelegate: NSObject, reloadDockMenu() } + func applicationDidUpdate(_ notification: Notification) { + guard derivedConfig.shouldSwitchBetweenActivationPolicies else { return } + // Are we presenting any regular windows ? + NSApp.setActivationPolicy(NSApp.visibleRegularWindows.isEmpty ? .accessory : .regular) + } + /// Syncs a single menu shortcut for the given action. The action string is the same /// action string used for the Ghostty configuration. private func syncMenuShortcut(_ config: Ghostty.Config, action: String, menuItem: NSMenuItem?) { @@ -540,6 +546,9 @@ class AppDelegate: NSObject, case .always: NSApp.setActivationPolicy(.accessory) + + case .quick_terminal: + NSApp.setActivationPolicy(NSApp.visibleRegularWindows.isEmpty ? .accessory : .regular) } // If we have configuration errors, we need to show them. @@ -786,17 +795,20 @@ class AppDelegate: NSObject, let initialWindow: Bool let shouldQuitAfterLastWindowClosed: Bool let quickTerminalPosition: QuickTerminalPosition + let shouldSwitchBetweenActivationPolicies: Bool init() { self.initialWindow = true self.shouldQuitAfterLastWindowClosed = false self.quickTerminalPosition = .top + self.shouldSwitchBetweenActivationPolicies = false } init(_ config: Ghostty.Config) { self.initialWindow = config.initialWindow self.shouldQuitAfterLastWindowClosed = config.shouldQuitAfterLastWindowClosed self.quickTerminalPosition = config.quickTerminalPosition + self.shouldSwitchBetweenActivationPolicies = config.macosHidden == .quick_terminal } } diff --git a/macos/Sources/Features/Terminal/TerminalManager.swift b/macos/Sources/Features/Terminal/TerminalManager.swift index 2db29aec9..ba7076fef 100644 --- a/macos/Sources/Features/Terminal/TerminalManager.swift +++ b/macos/Sources/Features/Terminal/TerminalManager.swift @@ -17,7 +17,13 @@ class TerminalManager { var focusedSurface: Ghostty.SurfaceView? { mainWindow?.controller.focusedSurface } /// The set of windows we currently have. - var windows: [Window] = [] + private(set) var windows: [Window] = [] { + didSet { + let userInfo = [Notification.Name.GhosttyWindowsChangedKey: windows.count] + NotificationCenter.default + .post(name: .ghosttyWindowsChanged, object: nil, userInfo: userInfo) + } + } // Keep track of the last point that our window was launched at so that new // windows "cascade" over each other and don't just launch directly on top diff --git a/macos/Sources/Ghostty/Ghostty.Config.swift b/macos/Sources/Ghostty/Ghostty.Config.swift index ec23632f7..348036baf 100644 --- a/macos/Sources/Ghostty/Ghostty.Config.swift +++ b/macos/Sources/Ghostty/Ghostty.Config.swift @@ -529,6 +529,7 @@ extension Ghostty.Config { enum MacHidden : String { case never case always + case quick_terminal = "quick-terminal" } enum ResizeOverlay : String { diff --git a/macos/Sources/Ghostty/Package.swift b/macos/Sources/Ghostty/Package.swift index 18ef3f3a7..7071b8e09 100644 --- a/macos/Sources/Ghostty/Package.swift +++ b/macos/Sources/Ghostty/Package.swift @@ -247,6 +247,10 @@ extension Notification.Name { /// Close tab static let ghosttyCloseTab = Notification.Name("com.mitchellh.ghostty.closeTab") + + /// Managed windows did change. + static let ghosttyWindowsChanged = Notification.Name("com.mitchellh.ghostty.windowsChanged") + static let GhosttyWindowsChangedKey = ghosttyWindowsChanged.rawValue } // NOTE: I am moving all of these to Notification.Name extensions over time. This diff --git a/macos/Sources/Helpers/NSApplication+Extension.swift b/macos/Sources/Helpers/NSApplication+Extension.swift index 0580cd5fc..67f6b2e64 100644 --- a/macos/Sources/Helpers/NSApplication+Extension.swift +++ b/macos/Sources/Helpers/NSApplication+Extension.swift @@ -24,6 +24,21 @@ extension NSApplication { } } +extension NSApplication { + private static let nonRegularWindowsClassNames: [String] = [ + "NSStatusBarWindow", + "_NSPopoverWindow", + "TUINSWindow" + ] + + /// Windows that are visible and regular (such as terminal & update windows) + var visibleRegularWindows: [NSWindow] { + NSApp.windows + .filter { !Self.nonRegularWindowsClassNames.contains($0.className) } + .filter { $0.isVisible } + } +} + extension NSApplication.PresentationOptions.Element: @retroactive Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(rawValue) diff --git a/src/config/Config.zig b/src/config/Config.zig index 9e6f17ef4..d49883c1a 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -5762,6 +5762,7 @@ pub const MacTitlebarProxyIcon = enum { pub const MacHidden = enum { never, always, + @"quick-terminal", }; /// See macos-icon