macos: set NSAppearance on windowDidLoad (#3077)

Fixes #3072

Previously, when `window-theme = auto`, the appearance was delayed
enough on the DispatchQueue that the window was already visible. This
would result in the window appearing with the wrong appearance before
switching to the correct one.

For annoying reasons, we can't set the NSApplication.shared.appearance
in `applicationDidFinishLaunching` because it results in a deadlock with
AppKit.

This commit moves to set the `NSWindow.appearance` in `windowDidLoad`
(and any config sync) to ensure that the appearance is set before the
window is visible.

This is probably the right solution anyways because this allows windows
with different background colors to each have their own distinct
appearance.
This commit is contained in:
Mitchell Hashimoto
2024-12-22 19:41:43 -08:00
committed by GitHub
4 changed files with 30 additions and 18 deletions

View File

@ -546,24 +546,7 @@ class AppDelegate: NSObject,
/// Sync the appearance of our app with the theme specified in the config.
private func syncAppearance(config: Ghostty.Config) {
guard let theme = config.windowTheme else { return }
switch (theme) {
case "dark":
let appearance = NSAppearance(named: .darkAqua)
NSApplication.shared.appearance = appearance
case "light":
let appearance = NSAppearance(named: .aqua)
NSApplication.shared.appearance = appearance
case "auto":
let color = OSColor(config.backgroundColor)
let appearance = NSAppearance(named: color.isLightColor ? .aqua : .darkAqua)
NSApplication.shared.appearance = appearance
default:
NSApplication.shared.appearance = nil
}
NSApplication.shared.appearance = .init(ghosttyConfig: config)
}
//MARK: - Restorable State

View File

@ -201,6 +201,9 @@ class TerminalController: BaseTerminalController {
private func syncAppearance(_ surfaceConfig: Ghostty.SurfaceView.DerivedConfig) {
guard let window = self.window as? TerminalWindow else { return }
// Set our explicit appearance if we need to based on the configuration.
window.appearance = surfaceConfig.windowAppearance
// If our window is not visible, then we do nothing. Some things such as blurring
// have no effect if the window is not visible. Ultimately, we'll have this called
// at some point when a surface becomes focused.

View File

@ -1097,12 +1097,14 @@ extension Ghostty {
let backgroundOpacity: Double
let macosWindowShadow: Bool
let windowTitleFontFamily: String?
let windowAppearance: NSAppearance?
init() {
self.backgroundColor = Color(NSColor.windowBackgroundColor)
self.backgroundOpacity = 1
self.macosWindowShadow = true
self.windowTitleFontFamily = nil
self.windowAppearance = nil
}
init(_ config: Ghostty.Config) {
@ -1110,6 +1112,7 @@ extension Ghostty {
self.backgroundOpacity = config.backgroundOpacity
self.macosWindowShadow = config.macosWindowShadow
self.windowTitleFontFamily = config.windowTitleFontFamily
self.windowAppearance = .init(ghosttyConfig: config)
}
}
}

View File

@ -5,4 +5,27 @@ extension NSAppearance {
var isDark: Bool {
return name.rawValue.lowercased().contains("dark")
}
/// Initialize a desired NSAppearance for the Ghostty configuration.
convenience init?(ghosttyConfig config: Ghostty.Config) {
guard let theme = config.windowTheme else { return nil }
switch (theme) {
case "dark":
self.init(named: .darkAqua)
case "light":
self.init(named: .aqua)
case "auto":
let color = OSColor(config.backgroundColor)
if color.isLightColor {
self.init(named: .aqua)
} else {
self.init(named: .darkAqua)
}
default:
return nil
}
}
}