macOS: fix theme reloading
### 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](pull/9360/head83104ff27a)) - [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](d39cc6d478)) - [x] Using `theme=light:3024 Day,dark:3024 Night`; Switching the system's appearance should change Ghostty's appearance (break on [#d39cc6d](d39cc6d478)) - [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 # Conflicts: # macos/Sources/Features/Terminal/BaseTerminalController.swift
parent
08c9661683
commit
0c9082eb72
|
|
@ -566,6 +566,7 @@ class QuickTerminalController: BaseTerminalController {
|
|||
private func syncAppearance() {
|
||||
guard let window else { return }
|
||||
|
||||
defer { updateColorSchemeForSurfaceTree() }
|
||||
// Change the collection behavior of the window depending on the configuration.
|
||||
window.collectionBehavior = derivedConfig.quickTerminalSpaceBehavior.collectionBehavior
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,9 @@ class BaseTerminalController: NSWindowController,
|
|||
/// The previous frame information from the window
|
||||
private var savedFrame: SavedFrame? = nil
|
||||
|
||||
/// Cache previously applied appearance to avoid unnecessary updates
|
||||
private var appliedColorScheme: ghostty_color_scheme_e?
|
||||
|
||||
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
||||
private var derivedConfig: DerivedConfig
|
||||
|
||||
|
|
@ -1163,4 +1166,35 @@ extension BaseTerminalController: NSMenuItemValidation {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Surface Color Scheme
|
||||
|
||||
/// Update the surface tree's color scheme only when it actually changes.
|
||||
///
|
||||
/// Calling ``ghostty_surface_set_color_scheme`` triggers
|
||||
/// ``syncAppearance(_:)`` via notification,
|
||||
/// so we avoid redundant calls.
|
||||
func updateColorSchemeForSurfaceTree() {
|
||||
/// Derive the target scheme from `window-theme` or system appearance.
|
||||
/// We set the scheme on surfaces so they pick the correct theme
|
||||
/// and let ``syncAppearance(_:)`` update the window accordingly.
|
||||
///
|
||||
/// Using App's effectiveAppearance here to prevent incorrect updates.
|
||||
let themeAppearance = NSApplication.shared.effectiveAppearance
|
||||
let scheme: ghostty_color_scheme_e
|
||||
if themeAppearance.isDark {
|
||||
scheme = GHOSTTY_COLOR_SCHEME_DARK
|
||||
} else {
|
||||
scheme = GHOSTTY_COLOR_SCHEME_LIGHT
|
||||
}
|
||||
guard scheme != appliedColorScheme else {
|
||||
return
|
||||
}
|
||||
for surfaceView in surfaceTree {
|
||||
if let surface = surfaceView.surface {
|
||||
ghostty_surface_set_color_scheme(surface, scheme)
|
||||
}
|
||||
}
|
||||
appliedColorScheme = scheme
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -425,15 +425,9 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
// This is a surface-level config update. If we have the surface, we
|
||||
// update our appearance based on it.
|
||||
guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard surfaceTree.contains(surfaceView) else { return }
|
||||
|
||||
// We can't use surfaceView.derivedConfig because it may not be updated
|
||||
// yet since it also responds to notifications.
|
||||
syncAppearance(.init(config))
|
||||
/// Surface-level config will be updated in
|
||||
/// ``Ghostty/Ghostty/SurfaceView/derivedConfig`` then
|
||||
/// ``TerminalController/focusedSurfaceDidChange(to:)``
|
||||
}
|
||||
|
||||
/// Update the accessory view of each tab according to the keyboard
|
||||
|
|
|
|||
|
|
@ -419,6 +419,7 @@ class TerminalWindow: NSWindow {
|
|||
// have no effect if the window is not visible. Ultimately, we'll have this called
|
||||
// at some point when a surface becomes focused.
|
||||
guard isVisible else { return }
|
||||
defer { updateColorSchemeForSurfaceTree() }
|
||||
|
||||
// Basic properties
|
||||
appearance = surfaceConfig.windowAppearance
|
||||
|
|
@ -481,6 +482,10 @@ class TerminalWindow: NSWindow {
|
|||
return derivedConfig.backgroundColor.withAlphaComponent(alpha)
|
||||
}
|
||||
|
||||
func updateColorSchemeForSurfaceTree() {
|
||||
terminalController?.updateColorSchemeForSurfaceTree()
|
||||
}
|
||||
|
||||
private func setInitialWindowPosition(x: Int16?, y: Int16?) {
|
||||
// If we don't have an X/Y then we try to use the previously saved window pos.
|
||||
guard let x, let y else {
|
||||
|
|
|
|||
|
|
@ -369,26 +369,6 @@ extension Ghostty {
|
|||
// Setup our tracking area so we get mouse moved events
|
||||
updateTrackingAreas()
|
||||
|
||||
// Observe our appearance so we can report the correct value to libghostty.
|
||||
// This is the best way I know of to get appearance change notifications.
|
||||
self.appearanceObserver = observe(\.effectiveAppearance, options: [.new, .initial]) { view, change in
|
||||
guard let appearance = change.newValue else { return }
|
||||
guard let surface = view.surface else { return }
|
||||
let scheme: ghostty_color_scheme_e
|
||||
switch (appearance.name) {
|
||||
case .aqua, .vibrantLight:
|
||||
scheme = GHOSTTY_COLOR_SCHEME_LIGHT
|
||||
|
||||
case .darkAqua, .vibrantDark:
|
||||
scheme = GHOSTTY_COLOR_SCHEME_DARK
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
ghostty_surface_set_color_scheme(surface, scheme)
|
||||
}
|
||||
|
||||
// The UTTypes that can be dragged onto this view.
|
||||
registerForDraggedTypes(Array(Self.dropTypes))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue