Skip to content

Commit 260e6de

Browse files
authored
macOS: fix theme reloading (#9360)
### Background After #9344, the Ghostty theme won't change after switching systems', and reverting #9344 will bring back the issue it fixed. The reason these two issues are related is because the scheme change is based on changes of `effectiveAppearance`, which is also affected by setting the window's `appearance` or changing `NSAppearance.currentDrawing()`. ### Changes Instead of observing `effectiveAppearance`, we now explicitly update the color scheme of surfaces, so that we can control when it happens to avoid callback loops and redundant updates. ### Regression Tests - [x] #8282 - [x] Reloading with `window-theme = light` should update Ghostty with the default dark theme with a dark window theme (break before [#83104ff](83104ff)) - [x] `window-theme = light \n macos-titlebar-style = native` should update Ghostty with the default dark theme with a light window theme - [x] Reloading from the default config to `theme=light:3024 Day,dark:3024 Night \n window-theme = light`, should update Ghostty with the theme `3024 Day` with a light window theme (break on [#d39cc6d](d39cc6d)) - [x] Using `theme=light:3024 Day,dark:3024 Night`; Switching the system's appearance should change Ghostty's appearance (break on [#d39cc6d](d39cc6d)) - [x] Reloading from `theme=light:3024 Day,dark:3024 Night` with a light window theme to the default config, should update Ghostty with the default dark theme with a dark window theme - [x] Reloading from the default config to `theme=light:3024 Day,dark:3024 Night \n window-theme=dark`, should update Ghostty with the theme `3024 Night` with a dark window theme - [x] Reloading from `theme=light:3024 Day,dark:3024 Night \n window-theme=dark` to `theme=light:3024 Day,dark:3024 Night` with light system appearance, should update Ghostty from dark to light - [x] Reload with quick terminal open
2 parents 581ed72 + 0c9082e commit 260e6de

File tree

5 files changed

+43
-29
lines changed

5 files changed

+43
-29
lines changed

macos/Sources/Features/QuickTerminal/QuickTerminalController.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ class QuickTerminalController: BaseTerminalController {
566566
private func syncAppearance() {
567567
guard let window else { return }
568568

569+
defer { updateColorSchemeForSurfaceTree() }
569570
// Change the collection behavior of the window depending on the configuration.
570571
window.collectionBehavior = derivedConfig.quickTerminalSpaceBehavior.collectionBehavior
571572

macos/Sources/Features/Terminal/BaseTerminalController.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ class BaseTerminalController: NSWindowController,
7272
/// The previous frame information from the window
7373
private var savedFrame: SavedFrame? = nil
7474

75+
/// Cache previously applied appearance to avoid unnecessary updates
76+
private var appliedColorScheme: ghostty_color_scheme_e?
77+
7578
/// The configuration derived from the Ghostty config so we don't need to rely on references.
7679
private var derivedConfig: DerivedConfig
7780

@@ -1163,4 +1166,35 @@ extension BaseTerminalController: NSMenuItemValidation {
11631166
return true
11641167
}
11651168
}
1169+
1170+
// MARK: - Surface Color Scheme
1171+
1172+
/// Update the surface tree's color scheme only when it actually changes.
1173+
///
1174+
/// Calling ``ghostty_surface_set_color_scheme`` triggers
1175+
/// ``syncAppearance(_:)`` via notification,
1176+
/// so we avoid redundant calls.
1177+
func updateColorSchemeForSurfaceTree() {
1178+
/// Derive the target scheme from `window-theme` or system appearance.
1179+
/// We set the scheme on surfaces so they pick the correct theme
1180+
/// and let ``syncAppearance(_:)`` update the window accordingly.
1181+
///
1182+
/// Using App's effectiveAppearance here to prevent incorrect updates.
1183+
let themeAppearance = NSApplication.shared.effectiveAppearance
1184+
let scheme: ghostty_color_scheme_e
1185+
if themeAppearance.isDark {
1186+
scheme = GHOSTTY_COLOR_SCHEME_DARK
1187+
} else {
1188+
scheme = GHOSTTY_COLOR_SCHEME_LIGHT
1189+
}
1190+
guard scheme != appliedColorScheme else {
1191+
return
1192+
}
1193+
for surfaceView in surfaceTree {
1194+
if let surface = surfaceView.surface {
1195+
ghostty_surface_set_color_scheme(surface, scheme)
1196+
}
1197+
}
1198+
appliedColorScheme = scheme
1199+
}
11661200
}

macos/Sources/Features/Terminal/TerminalController.swift

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -425,15 +425,9 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
425425

426426
return
427427
}
428-
429-
// This is a surface-level config update. If we have the surface, we
430-
// update our appearance based on it.
431-
guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return }
432-
guard surfaceTree.contains(surfaceView) else { return }
433-
434-
// We can't use surfaceView.derivedConfig because it may not be updated
435-
// yet since it also responds to notifications.
436-
syncAppearance(.init(config))
428+
/// Surface-level config will be updated in
429+
/// ``Ghostty/Ghostty/SurfaceView/derivedConfig`` then
430+
/// ``TerminalController/focusedSurfaceDidChange(to:)``
437431
}
438432

439433
/// Update the accessory view of each tab according to the keyboard

macos/Sources/Features/Terminal/Window Styles/TerminalWindow.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ class TerminalWindow: NSWindow {
419419
// have no effect if the window is not visible. Ultimately, we'll have this called
420420
// at some point when a surface becomes focused.
421421
guard isVisible else { return }
422+
defer { updateColorSchemeForSurfaceTree() }
422423

423424
// Basic properties
424425
appearance = surfaceConfig.windowAppearance
@@ -481,6 +482,10 @@ class TerminalWindow: NSWindow {
481482
return derivedConfig.backgroundColor.withAlphaComponent(alpha)
482483
}
483484

485+
func updateColorSchemeForSurfaceTree() {
486+
terminalController?.updateColorSchemeForSurfaceTree()
487+
}
488+
484489
private func setInitialWindowPosition(x: Int16?, y: Int16?) {
485490
// If we don't have an X/Y then we try to use the previously saved window pos.
486491
guard let x, let y else {

macos/Sources/Ghostty/SurfaceView_AppKit.swift

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -369,26 +369,6 @@ extension Ghostty {
369369
// Setup our tracking area so we get mouse moved events
370370
updateTrackingAreas()
371371

372-
// Observe our appearance so we can report the correct value to libghostty.
373-
// This is the best way I know of to get appearance change notifications.
374-
self.appearanceObserver = observe(\.effectiveAppearance, options: [.new, .initial]) { view, change in
375-
guard let appearance = change.newValue else { return }
376-
guard let surface = view.surface else { return }
377-
let scheme: ghostty_color_scheme_e
378-
switch (appearance.name) {
379-
case .aqua, .vibrantLight:
380-
scheme = GHOSTTY_COLOR_SCHEME_LIGHT
381-
382-
case .darkAqua, .vibrantDark:
383-
scheme = GHOSTTY_COLOR_SCHEME_DARK
384-
385-
default:
386-
return
387-
}
388-
389-
ghostty_surface_set_color_scheme(surface, scheme)
390-
}
391-
392372
// The UTTypes that can be dragged onto this view.
393373
registerForDraggedTypes(Array(Self.dropTypes))
394374
}

0 commit comments

Comments
 (0)