SettingsItem.swift (7334B)
1 /* 2 * This file is part of GNU Taler, ©2022-25 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * @author Marc Stibane 7 */ 8 import SwiftUI 9 10 struct SettingsItem<Content: View>: View { 11 var name: String 12 var id1: String? 13 var imageName: String? 14 var description: String? 15 var content: () -> Content 16 17 init(name: String, id1: String, imageName: String, description: String? = nil, @ViewBuilder content: @escaping () -> Content) { 18 self.name = name 19 self.id1 = id1 20 self.imageName = imageName 21 self.description = description 22 self.content = content 23 } 24 25 init(name: String, id1: String, description: String? = nil, @ViewBuilder content: @escaping () -> Content) { 26 self.name = name 27 self.id1 = id1 28 self.imageName = nil 29 self.description = description 30 self.content = content 31 } 32 33 var body: some View { 34 HStack { 35 VStack { 36 let isWeb = id1?.hasPrefix("web") ?? false 37 let foreColor = isWeb ? Color.accentColor 38 : .primary 39 HStack(spacing: 8.0) { 40 if let imageName { 41 Image(systemName: imageName) 42 } 43 Text(name) 44 .id(id1) 45 } 46 .frame(maxWidth: .infinity, alignment: .leading) 47 .foregroundColor(foreColor) 48 .talerFont(.title2) 49 .padding([.bottom], 0.01) 50 if let desc = description { 51 Text(desc) 52 .id(id1 == nil ? nil : id1! + "_T") 53 .frame(maxWidth: .infinity, alignment: .leading) 54 .talerFont(.caption) 55 } 56 }.id(id1 == nil ? nil : id1! + "_V") 57 content() 58 .talerFont(.body) 59 }.id(id1 == nil ? nil : id1! + "_H") 60 .accessibilityElement(children: .combine) 61 .padding([.bottom], 4) 62 } 63 } 64 // MARK: - 65 struct SettingsToggle: View { 66 var name: String 67 @Binding var value: Bool 68 var id1: String? = nil 69 var description: String? 70 var action: () -> Void = {} 71 72 var body: some View { 73 let accLabel: String = if let description { 74 name + ", " + description 75 } else { 76 name 77 } 78 VStack { 79 Toggle(name, isOn: $value.animation()) 80 .id(id1) 81 // .accessibilityLabel(name) 82 .accessibility(sortPriority: 1) 83 .talerFont(.title2) 84 .onChange(of: value) { value in 85 action() 86 } 87 88 if let desc = description { 89 Text(desc) 90 .id(id1 == nil ? nil : id1! + "_T") 91 .frame(maxWidth: .infinity, alignment: .leading) 92 .accessibility(sortPriority: 0) 93 .talerFont(.caption) 94 } 95 } 96 .accessibilityElement(children: .combine) 97 .accessibilityLabel(accLabel) 98 .padding([.bottom], 4) 99 .id(id1 == nil ? nil : id1! + "_V") 100 } 101 } 102 // MARK: - 103 struct SettingsFont: View { 104 let title: String 105 let value: Int 106 let action: (Int) -> Void 107 108 @State private var selectedFont = 0 109 let fonts = [String(localized: "Standard iOS Font"), "Atkinson-Hyperlegible", "Nunito"] 110 111 var body: some View { 112 Picker(title, selection: $selectedFont, content: { 113 ForEach(0..<fonts.count, id: \.self, content: { index in 114 Text(fonts[index]).tag(index) 115 }) 116 }) 117 .talerFont(.title2) 118 .pickerStyle(.menu) 119 .onAppear() { 120 withAnimation { selectedFont = value } 121 } 122 .onChange(of: selectedFont) { selectedF in 123 action(selectedF) 124 } 125 } 126 } 127 // MARK: - 128 struct SettingsStyle: View { 129 let title: String 130 @Binding var myListStyle: MyListStyle 131 132 var body: some View { 133 HStack { 134 Text(title) 135 .talerFont(.title2) 136 Spacer() 137 Picker(selection: $myListStyle) { 138 ForEach(MyListStyle.allCases, id: \.self) { 139 Text($0.displayName.capitalized).tag($0) 140 .talerFont(.title2) 141 } 142 } label: {} 143 .pickerStyle(.menu) 144 // .frame(alignment: .trailing) 145 // .background(WalletColors().buttonBackColor(pressed: false, disabled: false)) TODO: RoundRect 146 } 147 .accessibilityElement(children: .combine) 148 } 149 } 150 // MARK: - 151 struct SettingsTriState: View { 152 var name: String 153 @Binding var value: Int 154 var description: String? 155 var action: (_ value: Int) -> Void = {value in } 156 157 func imageName(_ value: Int) -> (String, String) { 158 return (value == 0) ? ("eye.slash", "Off") 159 : (value == 1) ? ("eye", "Type only") 160 : ("eye.fill", "Type and JSON") 161 } 162 var body: some View { 163 let image = imageName(value) 164 165 VStack { 166 HStack { 167 Text(name) 168 .talerFont(.title2) 169 Spacer() 170 Text(verbatim: " ") 171 .talerFont(.largeTitle) 172 Button { 173 if value > 0 { 174 value = -1 175 action(value) 176 Controller.shared.playSound(1) 177 } else { 178 value = value + 1 179 action(value) 180 Controller.shared.playSound(value) 181 } 182 } label: { 183 Image(systemName: image.0) 184 .talerFont(.largeTitle) 185 } 186 } 187 188 if let desc = description { 189 Text(desc) 190 .frame(maxWidth: .infinity, alignment: .leading) 191 .talerFont(.caption) 192 } 193 } 194 .accessibilityElement(children: .combine) 195 .accessibilityLabel(name) 196 .accessibility(value: Text(image.1)) 197 .accessibilityHint(description ?? EMPTYSTRING) 198 .padding([.bottom], 4) 199 } 200 } 201 // MARK: - 202 #if DEBUG 203 struct SettingsItemPreview : View { 204 @State var developerMode: Bool = false 205 @State var observe: Int = 0 206 207 var body: some View { 208 VStack { 209 SettingsToggle(name: "Developer Preview", value: $developerMode, id1: "dev1", 210 description: "More information intended for debugging") 211 SettingsTriState(name: "Observe walletCore", value: $observe, 212 description: "on LocalConsole") 213 214 } 215 } 216 } 217 218 struct SettingsItem_Previews: PreviewProvider { 219 static var previews: some View { 220 List { 221 SettingsItem (name: "Exchanges", id1: "list", description: "Manage list of exchanges known to this wallet") {} 222 SettingsItemPreview() 223 SettingsItem(name: "Save Logfile", id1: "save", description: "Help debugging wallet-core") { 224 Button("Save") { 225 } 226 .buttonStyle(.bordered) 227 .disabled(true) 228 } 229 } 230 } 231 } 232 #endif