fix: reload tint colors while running

This commit is contained in:
2026-10-22 16:07:22 +02:00
parent c48ef4c018
commit 1b171c7b52
18 changed files with 877 additions and 61 deletions
+107 -15
View File
@@ -2,39 +2,131 @@ import AppKit
@MainActor
enum ThemePalette {
private static let loaded: (scheme: ColorScheme, error: String?) = {
struct State {
let scheme: ColorScheme
var error: String?
var usesFallback: Bool
let background: NSColor
let selectionFill: NSColor
let selectionBorder: NSColor
let keyBackground: NSColor
init(scheme: ColorScheme, error: String? = nil, usesFallback: Bool = false) {
self.scheme = scheme
self.error = error
self.usesFallback = usesFallback
background = scheme.background.withAlphaComponent(0.55)
selectionFill = scheme.accent.withAlphaComponent(0.12)
selectionBorder = scheme.accent.withAlphaComponent(0.30)
keyBackground = scheme.keyBackground.withAlphaComponent(0.5)
}
}
static var loaded: State = {
do {
return (try ColorScheme.load(from: ColorScheme.configurationDirectory), nil)
return State(scheme: try ColorScheme.load(from: ColorScheme.configurationDirectory))
} catch {
return (.sunset, "Color configuration: \(error.localizedDescription)\nUsing the built-in sunset palette. Fix the file and restart Conjure.")
return State(scheme: .sunset,
error: "Color configuration: \(error.localizedDescription)\nUsing the built-in sunset palette. Fix the file to apply changes.",
usesFallback: true)
}
}()
static let configurationError = loaded.error
static let background = loaded.scheme.background.withAlphaComponent(0.55)
static let text = loaded.scheme.text
static let secondaryText = loaded.scheme.secondaryText
static let accent = loaded.scheme.accent
static let selectionFill = accent.withAlphaComponent(0.12)
static let selectionBorder = accent.withAlphaComponent(0.30)
static let glow = loaded.scheme.glow
static let keyBackground = loaded.scheme.keyBackground.withAlphaComponent(0.5)
static let error = loaded.scheme.error
private static var observation: ColorSchemeObservation?
static let didChangeNotification = Notification.Name("ConjureThemePaletteDidChange")
static var configurationError: String? { loaded.error }
static var background: NSColor { loaded.background }
static var text: NSColor { loaded.scheme.text }
static var secondaryText: NSColor { loaded.scheme.secondaryText }
static var accent: NSColor { loaded.scheme.accent }
static var selectionFill: NSColor { loaded.selectionFill }
static var selectionBorder: NSColor { loaded.selectionBorder }
static var glow: NSColor { loaded.scheme.glow }
static var keyBackground: NSColor { loaded.keyBackground }
static var error: NSColor { loaded.scheme.error }
static func startObserving() {
guard observation == nil else { return }
let directory = ColorScheme.configurationDirectory
observation = ColorSchemeObservation(directory: directory) {
let previousError = configurationError
reload(from: directory)
if let diagnostic = configurationError, diagnostic != previousError {
NSLog("%@", diagnostic)
}
}
reload(from: directory)
}
@discardableResult
static func reload(from directory: URL = ColorScheme.configurationDirectory) -> Bool {
do {
let scheme = try ColorScheme.load(from: directory)
let previous = loaded.scheme
guard scheme.background != previous.background || scheme.text != previous.text
|| scheme.secondaryText != previous.secondaryText || scheme.accent != previous.accent
|| scheme.glow != previous.glow || scheme.keyBackground != previous.keyBackground
|| scheme.error != previous.error else {
loaded.error = nil
loaded.usesFallback = false
return false
}
loaded = State(scheme: scheme)
NotificationCenter.default.post(name: didChangeNotification, object: nil)
return true
} catch {
let recovery = loaded.usesFallback
? "Using the built-in sunset palette."
: "Keeping the last valid palette."
loaded.error = "Color configuration: \(error.localizedDescription)\n\(recovery) Fix the file to apply changes."
return false
}
}
}
@MainActor
final class LauncherBackground: NSView {
weak var canvas: NSView?
private let amber = NSGradient(colors: [
private var amber = NSGradient(colors: [
ThemePalette.accent.withAlphaComponent(0.18),
ThemePalette.accent.withAlphaComponent(0.065),
ThemePalette.accent.withAlphaComponent(0),
])!
private let ember = NSGradient(colors: [
private var ember = NSGradient(colors: [
ThemePalette.glow.withAlphaComponent(0.26),
ThemePalette.glow.withAlphaComponent(0.10),
ThemePalette.glow.withAlphaComponent(0),
])!
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
observePalette()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
observePalette()
}
private func observePalette() {
NotificationCenter.default.addObserver(self, selector: #selector(paletteChanged),
name: ThemePalette.didChangeNotification, object: nil)
}
@objc private func paletteChanged() {
amber = NSGradient(colors: [
ThemePalette.accent.withAlphaComponent(0.18),
ThemePalette.accent.withAlphaComponent(0.065),
ThemePalette.accent.withAlphaComponent(0),
])!
ember = NSGradient(colors: [
ThemePalette.glow.withAlphaComponent(0.26),
ThemePalette.glow.withAlphaComponent(0.10),
ThemePalette.glow.withAlphaComponent(0),
])!
needsDisplay = true
}
override func hitTest(_ point: NSPoint) -> NSView? { nil }
override func draw(_ dirtyRect: NSRect) {