feat: added custom fonts
cc233f3d
3 file(s) · +49 −8
| 6 | 6 | import SwiftUI |
|
| 7 | 7 | import Combine |
|
| 8 | 8 | ||
| 9 | + | /// Available font design options for the browser |
|
| 10 | + | enum FontDesignOption: String, CaseIterable, Identifiable { |
|
| 11 | + | case system = "System" |
|
| 12 | + | case monospaced = "Monospaced" |
|
| 13 | + | case serif = "Serif" |
|
| 14 | + | case rounded = "Rounded" |
|
| 15 | + | ||
| 16 | + | var id: String { rawValue } |
|
| 17 | + | ||
| 18 | + | var fontDesign: Font.Design { |
|
| 19 | + | switch self { |
|
| 20 | + | case .system: return .default |
|
| 21 | + | case .monospaced: return .monospaced |
|
| 22 | + | case .serif: return .serif |
|
| 23 | + | case .rounded: return .rounded |
|
| 24 | + | } |
|
| 25 | + | } |
|
| 26 | + | } |
|
| 27 | + | ||
| 9 | 28 | /// Observable object that manages theme customization settings. |
|
| 10 | 29 | /// This provides centralized accent color management that views can subscribe to. |
|
| 11 | 30 | class ThemeSettings: ObservableObject { |
|
| 30 | 49 | /// The text color for content |
|
| 31 | 50 | @Published var textColor: Color = Color(UIColor.label) |
|
| 32 | 51 | ||
| 52 | + | /// The font design for content |
|
| 53 | + | @Published var fontDesign: FontDesignOption = .monospaced |
|
| 54 | + | ||
| 33 | 55 | /// The home page URL that the browser navigates to on launch and when pressing Home |
|
| 34 | 56 | @AppStorage("homePage") var homePage: String = "gemini://geminiprotocol.net/" |
|
| 35 | 57 | ||
| 37 | 59 | private static let accentColorKey = "accentColorHex" |
|
| 38 | 60 | private static let backgroundColorKey = "backgroundColorHex" |
|
| 39 | 61 | private static let textColorKey = "textColorHex" |
|
| 62 | + | private static let fontDesignKey = "fontDesign" |
|
| 40 | 63 | ||
| 41 | 64 | init() { |
|
| 42 | 65 | if let hex = UserDefaults.standard.string(forKey: Self.accentColorKey), |
|
| 51 | 74 | let color = Color(hex: hex) { |
|
| 52 | 75 | textColor = color |
|
| 53 | 76 | } |
|
| 77 | + | if let fontRaw = UserDefaults.standard.string(forKey: Self.fontDesignKey), |
|
| 78 | + | let font = FontDesignOption(rawValue: fontRaw) { |
|
| 79 | + | fontDesign = font |
|
| 80 | + | } |
|
| 54 | 81 | } |
|
| 55 | 82 | ||
| 56 | 83 | /// Sets all accent colors to the given color and persists the choice |
|
| 80 | 107 | if let hex = color.toHex() { |
|
| 81 | 108 | UserDefaults.standard.set(hex, forKey: Self.textColorKey) |
|
| 82 | 109 | } |
|
| 110 | + | } |
|
| 111 | + | ||
| 112 | + | /// Sets the font design and persists the choice |
|
| 113 | + | func setFontDesign(_ font: FontDesignOption) { |
|
| 114 | + | fontDesign = font |
|
| 115 | + | UserDefaults.standard.set(font.rawValue, forKey: Self.fontDesignKey) |
|
| 83 | 116 | } |
|
| 84 | 117 | } |
|
| 85 | 118 | ||
| 13 | 13 | @State private var selectedAccentColor: Color = .blue |
|
| 14 | 14 | @State private var selectedBackgroundColor: Color = Color(UIColor.systemBackground) |
|
| 15 | 15 | @State private var selectedTextColor: Color = Color(UIColor.label) |
|
| 16 | + | @State private var selectedFontDesign: FontDesignOption = .monospaced |
|
| 16 | 17 | ||
| 17 | 18 | var body: some View { |
|
| 18 | 19 | NavigationStack { |
|
| 32 | 33 | ColorPicker("Accent Color", selection: $selectedAccentColor, supportsOpacity: false) |
|
| 33 | 34 | ColorPicker("Background Color", selection: $selectedBackgroundColor, supportsOpacity: false) |
|
| 34 | 35 | ColorPicker("Text Color", selection: $selectedTextColor, supportsOpacity: false) |
|
| 36 | + | Picker("Font", selection: $selectedFontDesign) { |
|
| 37 | + | ForEach(FontDesignOption.allCases) { option in |
|
| 38 | + | Text(option.rawValue).tag(option) |
|
| 39 | + | } |
|
| 40 | + | } |
|
| 35 | 41 | } header: { |
|
| 36 | 42 | Text("Appearance") |
|
| 37 | 43 | } footer: { |
|
| 38 | - | Text("Customize the colors of your browser interface.") |
|
| 44 | + | Text("Customize the look of your browser interface.") |
|
| 39 | 45 | } |
|
| 40 | 46 | } |
|
| 41 | 47 | .navigationTitle("Settings") |
|
| 52 | 58 | themeSettings.setAllAccentColors(selectedAccentColor) |
|
| 53 | 59 | themeSettings.setBackgroundColor(selectedBackgroundColor) |
|
| 54 | 60 | themeSettings.setTextColor(selectedTextColor) |
|
| 61 | + | themeSettings.setFontDesign(selectedFontDesign) |
|
| 55 | 62 | dismiss() |
|
| 56 | 63 | } |
|
| 57 | 64 | } |
|
| 61 | 68 | selectedAccentColor = themeSettings.accentColor |
|
| 62 | 69 | selectedBackgroundColor = themeSettings.backgroundColor |
|
| 63 | 70 | selectedTextColor = themeSettings.textColor |
|
| 71 | + | selectedFontDesign = themeSettings.fontDesign |
|
| 64 | 72 | } |
|
| 65 | 73 | } |
|
| 66 | 74 | } |
|
| 76 | 76 | switch line { |
|
| 77 | 77 | case .text(let text): |
|
| 78 | 78 | Text(text) |
|
| 79 | - | .font(.system(.body, design: .monospaced)) |
|
| 79 | + | .font(.system(.body, design: themeSettings.fontDesign.fontDesign)) |
|
| 80 | 80 | .foregroundColor(themeSettings.textColor) |
|
| 81 | 81 | ||
| 82 | 82 | case .link(let url, let label): |
|
| 84 | 84 | HStack(alignment:.top, spacing: 4) { |
|
| 85 | 85 | Text(label) |
|
| 86 | 86 | .multilineTextAlignment(.leading) |
|
| 87 | - | .font(.system(size: 14, design: .monospaced)) |
|
| 87 | + | .font(.system(size: 14, design: themeSettings.fontDesign.fontDesign)) |
|
| 88 | 88 | } |
|
| 89 | 89 | } |
|
| 90 | 90 | .foregroundColor(themeSettings.linkColor) |
|
| 92 | 92 | ||
| 93 | 93 | case .heading1(let text): |
|
| 94 | 94 | Text(text) |
|
| 95 | - | .font(.system(.title, design: .monospaced)) |
|
| 95 | + | .font(.system(.title, design: themeSettings.fontDesign.fontDesign)) |
|
| 96 | 96 | .fontWeight(.bold) |
|
| 97 | 97 | .foregroundColor(themeSettings.textColor) |
|
| 98 | 98 | .padding(.top, 8) |
|
| 99 | 99 | ||
| 100 | 100 | case .heading2(let text): |
|
| 101 | 101 | Text(text) |
|
| 102 | - | .font(.system(.title2, design: .monospaced)) |
|
| 102 | + | .font(.system(.title2, design: themeSettings.fontDesign.fontDesign)) |
|
| 103 | 103 | .fontWeight(.semibold) |
|
| 104 | 104 | .foregroundColor(themeSettings.textColor) |
|
| 105 | 105 | .padding(.top, 6) |
|
| 106 | 106 | ||
| 107 | 107 | case .heading3(let text): |
|
| 108 | 108 | Text(text) |
|
| 109 | - | .font(.system(.title3, design: .monospaced)) |
|
| 109 | + | .font(.system(.title3, design: themeSettings.fontDesign.fontDesign)) |
|
| 110 | 110 | .fontWeight(.medium) |
|
| 111 | 111 | .foregroundColor(themeSettings.textColor) |
|
| 112 | 112 | .padding(.top, 4) |
|
| 116 | 116 | Text("\u{2022}") |
|
| 117 | 117 | Text(text) |
|
| 118 | 118 | } |
|
| 119 | - | .font(.system(.body, design: .monospaced)) |
|
| 119 | + | .font(.system(.body, design: themeSettings.fontDesign.fontDesign)) |
|
| 120 | 120 | .foregroundColor(themeSettings.textColor) |
|
| 121 | 121 | ||
| 122 | 122 | case .quote(let text): |
|
| 123 | 123 | Text(text) |
|
| 124 | - | .font(.system(.body, design: .monospaced)) |
|
| 124 | + | .font(.system(.body, design: themeSettings.fontDesign.fontDesign)) |
|
| 125 | 125 | .italic() |
|
| 126 | 126 | .foregroundColor(.secondary) |
|
| 127 | 127 | .padding(.leading, 12) |
|