krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.1.11: 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}