import SwiftUI /// Hält die gewählte Akzentfarbe und sorgt dafür, dass sie lesbar bleibt. /// /// Nach außen gibt es nur `accent` — schon kontrastkorrigiert. Views sollen /// sich nie fragen müssen, ob die eingestellte Farbe dunkel genug ist, um /// darauf zu achten. @MainActor @Observable public final class FlixyTheme { private let defaults: UserDefaults private enum Key { static let selectedIdentifier = "flixy.accent.identifier" static let customHex = "flixy.accent.customHex" } /// Was in den Einstellungen ausgewählt ist — unkorrigiert, damit die /// Farbkachel genau das zeigt, was angetippt wurde. public private(set) var selection: AccentOption public init(defaults: UserDefaults = .standard) { self.defaults = defaults self.selection = Self.restore(from: defaults) } /// Die Farbe, die tatsächlich gezeichnet wird. /// /// Bei den kuratierten Werten ändert die Korrektur nichts — sie erfüllen /// den Grenzwert bereits. Sie greift nur bei eigenen Farben. public var accent: RGB { Contrast.legible(selection.color, on: FlixyColor.canvas) } public var accentColor: Color { accent.swiftUIColor } /// Ob die Korrektur bei der aktuellen Auswahl eingegriffen hat. Der /// Einstellungsdialog kann darauf hinweisen, statt den Nutzer rätseln zu /// lassen, warum seine Farbe heller aussieht als gewählt. public var accentWasAdjusted: Bool { accent != selection.color } public func select(_ option: AccentOption) { selection = option defaults.set(option.id, forKey: Key.selectedIdentifier) if option.id == AccentOption.customIdentifier { defaults.set(option.color.hexString, forKey: Key.customHex) } } private static func restore(from defaults: UserDefaults) -> AccentOption { guard let identifier = defaults.string(forKey: Key.selectedIdentifier) else { return .default } if identifier == AccentOption.customIdentifier { // Fällt die gespeicherte Zeichenkette aus (etwa weil jemand die // Voreinstellungen von Hand bearbeitet hat), ist der Standard // besser als eine Farbe, die niemand gewählt hat. guard let hex = defaults.string(forKey: Key.customHex), let color = RGB(string: hex) else { return .default } return .custom(color) } return AccentOption.palette.first { $0.id == identifier } ?? .default } }