krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Sources/Orgstar/ThemeColors.swift

16086b4cf2caff5328774b2cd5ae3ffe1ab65ca4
orgstar/Sources/Orgstar/ThemeColors.swift history · blame · raw

51 lines · 2089 bytes

 1import AppKit
 2import OrgPresentation
 3import SwiftUI
 4
 5extension EnvironmentValues {
 6    /// The theme's colors, for the views around the editor.
 7    @Entry var orgTheme: ThemeSpec = .default
 8}
 9
10extension ThemeSpec {
11    /// A color that follows the appearance, or nil when the theme leaves `key` to the system.
12    /// A key set for one appearance only leaves the other to the system.
13    func color(_ key: String) -> Color? {
14        guard light[key] != nil || dark[key] != nil else { return nil }
15        let (light, dark) = (light[key], dark[key])
16        let fallback = Self.systemColor(key)
17        return Color(nsColor: NSColor(name: nil) { appearance in
18            let isDark = appearance.bestMatch(from: [.aqua, .darkAqua]) == .darkAqua
19            guard let c = isDark ? dark : light else { return fallback }
20            return NSColor(srgbRed: c.red, green: c.green, blue: c.blue, alpha: c.alpha)
21        })
22    }
23
24    /// What the system shows for a key the theme leaves unset in one appearance.
25    static func systemColor(_ key: String) -> NSColor {
26        switch key {
27        case "sidebar-background", "modeline-background": .windowBackgroundColor
28        case "agenda-background", "background": .textBackgroundColor
29        case "sidebar-header", "modeline-foreground", "agenda-time", "agenda-category", "agenda-scheduled", "tags": .secondaryLabelColor
30        default: .labelColor
31        }
32    }
33
34    /// A TODO keyword's color: its own from `[theme.todo]`, else the theme's TODO or DONE.
35    func keywordColor(_ keyword: String, done: Bool) -> Color {
36        if let c = todo[keyword] { return Color(.sRGB, red: c.red, green: c.green, blue: c.blue, opacity: c.alpha) }
37        return color(done ? "done" : "todo") ?? (done ? .green : .red)
38    }
39}
40
41extension View {
42    /// A list's background from the theme, or the system's when the theme has none.
43    @ViewBuilder
44    func themedListBackground(_ color: Color?) -> some View {
45        if let color {
46            scrollContentBackground(.hidden).background(color)
47        } else {
48            self
49        }
50    }
51}