← All repositories

listless

A simple list app for Apple platforms

git clone https://code.inqk.net/listless.git


Listless/Helpers/AccentColor.swift (2466B)

1import SwiftUI
2
3enum ColorTheme: Int, CaseIterable, Identifiable {
4    case pilbara = 0
5    case collaroy = 1
6
7    var id: Int { rawValue }
8
9    static var displayOrder: [ColorTheme] {
10        allCases.sorted { $0.displayName < $1.displayName }
11    }
12
13    var displayName: String {
14        switch self {
15        case .pilbara: "Pilbara"
16        case .collaroy: "Collaroy"
17        }
18    }
19
20    fileprivate typealias HSB = (h: Double, s: Double, b: Double)
21
22    fileprivate var top: HSB {
23        switch self {
24        case .pilbara: (h: 0.98, s: 0.85, b: 1.00)
25        case .collaroy: (h: 0.58, s: 0.88, b: 1.00)
26        }
27    }
28
29    fileprivate var mid: HSB {
30        switch self {
31        case .pilbara: (h: 0.88, s: 0.75, b: 0.95)
32        case .collaroy: (h: 0.51, s: 0.69, b: 0.90)
33        }
34    }
35
36    fileprivate var bottom: HSB {
37        switch self {
38        case .pilbara: (h: 0.72, s: 0.65, b: 0.85)
39        case .collaroy: (h: 0.44, s: 0.50, b: 0.80)
40        }
41    }
42}
43
44private struct ItemAccentColorKey: Hashable {
45    let index: Int
46    let total: Int
47    let theme: ColorTheme
48}
49
50@MainActor
51private enum ItemAccentColorCache {
52    static var colors: [ItemAccentColorKey: Color] = [:]
53}
54
55func itemColor(forIndex index: Int, total: Int, theme: ColorTheme = .pilbara) -> Color {
56    let top = theme.top
57    guard total > 1 else { return Color(hue: top.h, saturation: top.s, brightness: top.b) }
58
59    let progress = Double(index) / Double(total - 1)
60    let mid = theme.mid
61    let bottom = theme.bottom
62
63    if progress < 0.5 {
64        return interpolateHSB(from: top, to: mid, progress: progress * 2.0)
65    } else {
66        return interpolateHSB(from: mid, to: bottom, progress: (progress - 0.5) * 2.0)
67    }
68}
69
70@MainActor
71func cachedItemColor(forIndex index: Int, total: Int, theme: ColorTheme = .pilbara) -> Color {
72    let key = ItemAccentColorKey(index: index, total: total, theme: theme)
73    if let cached = ItemAccentColorCache.colors[key] {
74        return cached
75    }
76
77    let computed = itemColor(forIndex: index, total: total, theme: theme)
78    ItemAccentColorCache.colors[key] = computed
79    return computed
80}
81
82private func interpolateHSB(
83    from: (h: Double, s: Double, b: Double),
84    to: (h: Double, s: Double, b: Double),
85    progress: Double
86) -> Color {
87    Color(
88        hue: from.h + (to.h - from.h) * progress,
89        saturation: from.s + (to.s - from.s) * progress,
90        brightness: from.b + (to.b - from.b) * progress
91    )
92}