Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[macOS] feat: Add "quick-terminal" option to "macos-hidden" config #5803

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions macos/Sources/App/macOS/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,10 @@ class AppDelegate: NSObject,
reloadDockMenu()
}

func applicationDidUpdate(_ notification: Notification) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I tested the logic of this and imo this callback gets called way too much. On initial launch it's called 4 times and when I close the window once it gets called something like a dozen or more times (I didn't count, but it was a lot).

I think we need to do something different. Maybe "any open window" isn't the right logic and we need to do some more research into how iTerm2 behaves instead.

Copy link
Contributor Author

@McNight McNight Feb 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that's what I noticed as well :/
I saw this method from a blog post about the challenge to track managed windows...
With AppKit Notifications, you get one when a window is about to close, and when one is becoming key/main but that's it.

Maybe we could intercept (via swizzling?) when windows are about to be set to the NSApp.windows property; or even add KVO to it (because it is not unfortunately)...

Do you think that applicationDidUpdate(_:) may cause performance issues?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think that applicationDidUpdate(_:) may cause performance issues?

I'm not sure if it'll practically cause issues but I'm afraid it'll cause unnecessary CPU/power usage. I'm not sure how grounded in reality that is but for a feature this minor it doesn't seem worth it.

syncActivationPolicy()
}

/// 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?) {
Expand Down Expand Up @@ -534,13 +538,7 @@ class AppDelegate: NSObject,
DispatchQueue.main.async { self.syncAppearance(config: config) }

// Decide whether to hide/unhide app from dock and app switcher
switch (config.macosHidden) {
case .never:
NSApp.setActivationPolicy(.regular)

case .always:
NSApp.setActivationPolicy(.accessory)
}
syncActivationPolicy()

// If we have configuration errors, we need to show them.
let c = ConfigurationErrorsController.sharedInstance
Expand Down Expand Up @@ -616,6 +614,20 @@ class AppDelegate: NSObject,
NSApplication.shared.appearance = .init(ghosttyConfig: config)
}

/// Sync the app activation policy based on the config `MacHidden` value.
private func syncActivationPolicy() {
switch (derivedConfig.macosHidden) {
case .never:
NSApp.setActivationPolicy(.regular)

case .always:
NSApp.setActivationPolicy(.accessory)

case .quick_terminal:
NSApp.setActivationPolicy(NSApp.visibleRegularWindows.isEmpty ? .accessory : .regular)
}
}

//MARK: - Restorable State

/// We support NSSecureCoding for restorable state. Required as of macOS Sonoma (14) but a good idea anyways.
Expand Down Expand Up @@ -786,17 +798,20 @@ class AppDelegate: NSObject,
let initialWindow: Bool
let shouldQuitAfterLastWindowClosed: Bool
let quickTerminalPosition: QuickTerminalPosition
let macosHidden: Ghostty.Config.MacHidden

init() {
self.initialWindow = true
self.shouldQuitAfterLastWindowClosed = false
self.quickTerminalPosition = .top
self.macosHidden = .never
}

init(_ config: Ghostty.Config) {
self.initialWindow = config.initialWindow
self.shouldQuitAfterLastWindowClosed = config.shouldQuitAfterLastWindowClosed
self.quickTerminalPosition = config.quickTerminalPosition
self.macosHidden = config.macosHidden
}
}

Expand Down
2 changes: 1 addition & 1 deletion macos/Sources/Features/Terminal/TerminalManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class TerminalManager {
var focusedSurface: Ghostty.SurfaceView? { mainWindow?.controller.focusedSurface }

/// The set of windows we currently have.
var windows: [Window] = []
private(set) var windows: [Window] = []

// 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
Expand Down
1 change: 1 addition & 0 deletions macos/Sources/Ghostty/Ghostty.Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ extension Ghostty.Config {
enum MacHidden : String {
case never
case always
case quick_terminal = "quick-terminal"
}

enum ResizeOverlay : String {
Expand Down
17 changes: 17 additions & 0 deletions macos/Sources/Helpers/NSApplication+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,23 @@ extension NSApplication {
}
}

extension NSApplication {
private static let nonRegularWindowsClassNames: [String] = [
"NSStatusBarWindow",
"_NSPopoverWindow",
"TUINSWindow"
]

/// Windows that are visible and regular (such as terminal & update windows).
/// `QuickTerminalWindow` instances are omitted from this collection.
var visibleRegularWindows: [NSWindow] {
NSApp.windows
.filter { !($0 is QuickTerminalWindow) }
.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)
Expand Down
1 change: 1 addition & 0 deletions src/config/Config.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5762,6 +5762,7 @@ pub const MacTitlebarProxyIcon = enum {
pub const MacHidden = enum {
never,
always,
@"quick-terminal",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh we need to add docs for this to the config key in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment for this new case, but maybe all the related paragraphs above need to be updated as well?

};

/// See macos-icon
Expand Down