krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v3.8.0: Hutch/App/ThemeManager.swift · raw
1import SwiftUI
2
3// MARK: - Theme & Density Types
4
5enum AppTheme: String, CaseIterable, Identifiable {
6 case system
7 case light
8 case dark
9 case amoled
10
11 var id: String { rawValue }
12
13 var label: String {
14 switch self {
15 case .system: "System"
16 case .light: "Light"
17 case .dark: "Dark"
18 case .amoled: "AMOLED"
19 }
20 }
21
22 var colorScheme: ColorScheme? {
23 switch self {
24 case .system: nil
25 case .light: .light
26 case .dark: .dark
27 case .amoled: .dark
28 }
29 }
30}
31
32enum DisplayDensity: String, CaseIterable, Identifiable {
33 case standard
34 case compact
35
36 var id: String { rawValue }
37
38 var label: String {
39 switch self {
40 case .standard: "Standard"
41 case .compact: "Compact"
42 }
43 }
44}
45
46// MARK: - Environment Keys
47
48private struct DisplayDensityKey: EnvironmentKey {
49 static let defaultValue: DisplayDensity = .standard
50}
51
52private struct IsAMOLEDThemeKey: EnvironmentKey {
53 static let defaultValue: Bool = false
54}
55
56extension EnvironmentValues {
57 var displayDensity: DisplayDensity {
58 get { self[DisplayDensityKey.self] }
59 set { self[DisplayDensityKey.self] = newValue }
60 }
61
62 var isAMOLEDTheme: Bool {
63 get { self[IsAMOLEDThemeKey.self] }
64 set { self[IsAMOLEDThemeKey.self] = newValue }
65 }
66}
67
68// MARK: - Themed List Modifier
69
70/// Combined modifier for List/Form that applies compact section spacing and AMOLED black backgrounds.
71/// Apply once per List or Form.
72struct ThemedListStyle: ViewModifier {
73 @Environment(\.displayDensity) private var density
74 @Environment(\.isAMOLEDTheme) private var isAMOLED
75
76 func body(content: Content) -> some View {
77 content
78 .listSectionSpacing(density == .compact ? .compact : .default)
79 .contentMargins(.vertical, density == .compact ? 2 : 8, for: .scrollContent)
80 .controlSize(density == .compact ? .small : .regular)
81 .modifier(AMOLEDListBackground(isAMOLED: isAMOLED))
82 }
83}
84
85/// Hides the default grouped list background and replaces it with true black for OLED screens.
86private struct AMOLEDListBackground: ViewModifier {
87 let isAMOLED: Bool
88
89 func body(content: Content) -> some View {
90 if isAMOLED {
91 content
92 .scrollContentBackground(.hidden)
93 .background(Color.black)
94 } else {
95 content
96 }
97 }
98}
99
100extension View {
101 /// Apply themed appearance (density spacing) to a List or Form.
102 func themedList() -> some View {
103 modifier(ThemedListStyle())
104 }
105}
106
107// MARK: - Themed Row Modifier
108
109struct ThemedRowStyle: ViewModifier {
110 @Environment(\.isAMOLEDTheme) private var isAMOLED
111
112 func body(content: Content) -> some View {
113 content
114 .listRowBackground(isAMOLED ? Color.black : nil)
115 }
116}
117
118extension View {
119 /// Apply to each row view inside a List or Form section to get a true-black background in AMOLED mode.
120 func themedRow() -> some View {
121 modifier(ThemedRowStyle())
122 }
123}