| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- //
- // AppAppearanceManager.swift
- // App for Indeed
- //
- import AppKit
- /// Persists and applies the user’s light / dark / system appearance preference.
- @MainActor
- final class AppAppearanceManager {
- static let shared = AppAppearanceManager()
- static let didChangeNotification = Notification.Name("AppAppearanceManager.didChange")
- enum Mode: String, CaseIterable {
- case light
- case dark
- case system
- var segmentIndex: Int {
- switch self {
- case .system: 0
- case .light: 1
- case .dark: 2
- }
- }
- init?(segmentIndex: Int) {
- switch segmentIndex {
- case 0: self = .system
- case 1: self = .light
- case 2: self = .dark
- default: return nil
- }
- }
- }
- private enum UserDefaultsKey {
- static let appearanceMode = "com.appforindeed.appearanceMode"
- }
- private var systemThemeObserver: NSObjectProtocol?
- private init() {
- systemThemeObserver = DistributedNotificationCenter.default().addObserver(
- forName: Notification.Name("AppleInterfaceThemeChangedNotification"),
- object: nil,
- queue: .main
- ) { [weak self] _ in
- guard let self, self.mode == .system else { return }
- self.updateWindowChrome()
- }
- }
- var mode: Mode {
- get {
- guard let raw = UserDefaults.standard.string(forKey: UserDefaultsKey.appearanceMode),
- let stored = Mode(rawValue: raw) else {
- return .light
- }
- return stored
- }
- set {
- guard newValue != mode else { return }
- UserDefaults.standard.set(newValue.rawValue, forKey: UserDefaultsKey.appearanceMode)
- apply()
- NotificationCenter.default.post(name: Self.didChangeNotification, object: self)
- }
- }
- /// Window backing color aligned with dashboard chrome for the active appearance.
- var windowChromeColor: NSColor {
- let isDark = NSApp.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
- if isDark {
- return NSColor(srgbRed: 28 / 255, green: 28 / 255, blue: 30 / 255, alpha: 1)
- }
- return NSColor(srgbRed: 247 / 255, green: 247 / 255, blue: 247 / 255, alpha: 1)
- }
- func apply() {
- switch mode {
- case .light:
- NSApp.appearance = NSAppearance(named: .aqua)
- case .dark:
- NSApp.appearance = NSAppearance(named: .darkAqua)
- case .system:
- NSApp.appearance = nil
- }
- updateWindowChrome()
- }
- private func updateWindowChrome() {
- let color = windowChromeColor
- for window in NSApp.windows where window.isVisible || window.canBecomeKey {
- window.backgroundColor = color
- }
- }
- }
|