diff options
| author | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-07-07 13:09:13 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-07-07 13:09:13 +0200 |
| commit | 3fdaeab6a7fa19b0c5424de487efc37c0994c86e (patch) | |
| tree | 09af4c1d54924350b4e6c89fb4f284170e89b945 /Map | |
| parent | 933078c10e99002c8a5f647e476819f0f1706a14 (diff) | |
More localization and map preferences
Diffstat (limited to 'Map')
14 files changed, 583 insertions, 182 deletions
diff --git a/Map/Localizable.xcstrings b/Map/Localizable.xcstrings index c40d6ce..69b9423 100644 --- a/Map/Localizable.xcstrings +++ b/Map/Localizable.xcstrings @@ -2,7 +2,7 @@ "sourceLanguage" : "en", "strings" : { "" : { - + "shouldTranslate" : false }, "Appearance Preferences" : { @@ -10,8 +10,16 @@ "Appearance settings will be available here in a future version." : { }, - "Check for Updates…" : { - + "commands.application.check_for_updates" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Check for Updates…" + } + } + } }, "commands.edit.find" : { "extractionState" : "manual", @@ -112,12 +120,6 @@ } } }, - "Done" : { - - }, - "Done (⎋)" : { - - }, "evolution_picker.label" : { "extractionState" : "manual", "localizations" : { @@ -129,30 +131,12 @@ } } }, - "Find Next (⌘G)" : { - - }, - "Find Previous (⇧⌘G)" : { - - }, "General Preferences" : { }, "General settings will be available here in a future version." : { }, - "Industrialised" : { - - }, - "Invisible" : { - - }, - "Map Preferences" : { - - }, - "Map settings will be available here in a future version." : { - - }, "map_editor.zoom_in" : { "extractionState" : "manual", "localizations" : { @@ -208,8 +192,203 @@ } } }, - "Name" : { - + "map.axis.x.leading" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Uncharted" + } + } + } + }, + "map.axis.x.trailing" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Industrialised" + } + } + } + }, + "map.axis.y.bottom" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Invisible" + } + } + } + }, + "map.axis.y.top" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Visible" + } + } + } + }, + "preferences.map.editor.title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Editor:" + } + } + } + }, + "preferences.map.editor.use_smart_editor" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Use Smart Editor" + } + } + } + }, + "preferences.map.map_style.show_background" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Show Background" + } + } + } + }, + "preferences.map.map_style.title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Map Style:" + } + } + } + }, + "preferences.menu.appearance" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appearance" + } + } + } + }, + "preferences.menu.general" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "General" + } + } + } + }, + "preferences.menu.map" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Map" + } + } + } + }, + "preferences.menu.stages" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stages" + } + } + } + }, + "preferences.menu.templates" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Templates" + } + } + } + }, + "preferences.stages.column.name" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Name" + } + } + } + }, + "preferences.stages.column.stage_i" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage I" + } + } + } + }, + "preferences.stages.column.stage_ii" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage II" + } + } + } + }, + "preferences.stages.column.stage_iii" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage III" + } + } + } + }, + "preferences.stages.column.stage_iv" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage IV" + } + } + } }, "preferences.stages.explanation" : { "extractionState" : "manual", @@ -222,6 +401,61 @@ } } }, + "preferences.stages.placeholder.name" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage Name" + } + } + } + }, + "preferences.stages.placeholder.stage_i" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage I" + } + } + } + }, + "preferences.stages.placeholder.stage_ii" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage II" + } + } + } + }, + "preferences.stages.placeholder.stage_iii" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage III" + } + } + } + }, + "preferences.stages.placeholder.stage_iv" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage IV" + } + } + } + }, "preferences.stages.title" : { "extractionState" : "manual", "localizations" : { @@ -354,23 +588,60 @@ } } }, - "Search" : { - - }, - "Stage I" : { - + "search_bar.done" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Done" + } + } + } }, - "Stage II" : { - + "search_bar.done.help" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Done (⎋)" + } + } + } }, - "Stage III" : { - + "search_bar.label" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Search" + } + } + } }, - "Stage IV" : { - + "search_bar.next.help" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Find Next (⌘G)" + } + } + } }, - "Stage Name" : { - + "search_bar.previous.help" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Find Previous (⇧⌘G)" + } + } + } }, "stage_type.behavior" : { "extractionState" : "manual", @@ -744,12 +1015,6 @@ } } } - }, - "Uncharted" : { - - }, - "Visible" : { - } }, "version" : "1.0" diff --git a/Map/Presentation/Base Components/EvolutionPicker/EvolutionPickerMenu.swift b/Map/Presentation/Base Components/EvolutionPicker/EvolutionPickerMenu.swift index 94a87d7..73837a0 100644 --- a/Map/Presentation/Base Components/EvolutionPicker/EvolutionPickerMenu.swift +++ b/Map/Presentation/Base Components/EvolutionPicker/EvolutionPickerMenu.swift @@ -55,30 +55,3 @@ struct EvolutionPickerMenu: View { #Preview { EvolutionPickerMenu(selectedEvolution: .constant(.ubiquity)) } - -/* -Picker(selection: $selectedEvolution) { - Divider() - ForEach(StageType.characteristics) { stage in - Text(Stage.title(stage)) - .font(selectedEvolution == stage ? .Theme.Body.emphasized : .Theme.Body.regular) - .tag(stage) - } - Divider() - ForEach(StageType.properties) { stage in - Text(Stage.title(stage)) - .font(selectedEvolution == stage ? .Theme.Body.emphasized : .Theme.Body.regular) - .tag(stage) - } - Divider() - ForEach(StageType.custom) { stage in - Text(Stage.title(stage)) - .font(selectedEvolution == stage ? .Theme.Body.emphasized : .Theme.Body.regular) - .tag(stage) - } -} label: { - Label("evolution_picker.label", image: "Icons/Evolution") - .font(.Theme.Body.regular) -}.padding(.horizontal, Dimensions.Spacing.regular) - .padding(.vertical, Dimensions.Spacing.cozy) - */ diff --git a/Map/Presentation/Base Components/MapRender/MapAxes.swift b/Map/Presentation/Base Components/MapRender/MapAxes.swift index 3a94a13..d9fe189 100644 --- a/Map/Presentation/Base Components/MapRender/MapAxes.swift +++ b/Map/Presentation/Base Components/MapRender/MapAxes.swift @@ -36,12 +36,12 @@ struct MapAxes: View { }.stroke(Color.Theme.Map.axisColor, lineWidth: lineWidth * 2) // Y Labels - Text("Visible").font(.Theme.Map.axisLabel).foregroundColor(.Theme.Map.labelColor) + Text("map.axis.y.top").font(.Theme.Map.axisLabel).foregroundColor(.Theme.Map.labelColor) .rotationEffect( Angle(degrees: -90.0) ) .offset(CGSize(width: -35.0, height: 0.0)) - Text("Invisible").font(.Theme.Map.axisLabel).foregroundColor(.Theme.Map.labelColor) + Text("map.axis.y.bottom").font(.Theme.Map.axisLabel).foregroundColor(.Theme.Map.labelColor) .rotationEffect( Angle(degrees: -90.0) ) @@ -49,12 +49,12 @@ struct MapAxes: View { // X Labels - Text("Uncharted") + Text("map.axis.x.leading") .font(.Theme.Map.axisLabel) .foregroundColor(.Theme.Map.labelColor) .frame(width: mapSize.width / 4, height: stageHeight / 2.0, alignment: .topLeading) .offset(CGSize(width: 0.0, height: -stageHeight / 4.0)) - Text("Industrialised") + Text("map.axis.x.trailing") .font(.Theme.Map.axisLabel) .foregroundColor(.Theme.Map.labelColor) .frame(width: mapSize.width / 4, height: stageHeight / 2.0, alignment: .topLeading) diff --git a/Map/Presentation/Base Components/MapRender/MapBackground.swift b/Map/Presentation/Base Components/MapRender/MapBackground.swift new file mode 100644 index 0000000..b33e791 --- /dev/null +++ b/Map/Presentation/Base Components/MapRender/MapBackground.swift @@ -0,0 +1,62 @@ +// Copyright (C) 2024 Rubén Beltrán del Río + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see https://map.tranquil.systems. +import Patterns +import SwiftUI + +struct MapBackground: View { + + let mapSize: CGSize + let lineWidth: CGFloat + let stages: [CGFloat] + let opacity = 0.1 + + var body: some View { + ZStack(alignment: .topLeading) { + PatternView( + design: .constant(.stitch), pixelSize: 1.0, foregroundColor: .Theme.Map.stageForeground, + backgroundColor: .Theme.Map.stageBackground + ) + .frame(width: w(stages[0]), height: mapSize.height) + PatternView( + design: .constant(.shingles), pixelSize: 1.0, foregroundColor: .Theme.Map.stageForeground, + backgroundColor: .Theme.Map.stageBackground + ) + .offset(CGSize(width: w(stages[0]), height: 0)) + .frame(width: w(stages[1]) - w(stages[0]), height: mapSize.height) + PatternView( + design: .constant(.shadowGrid), pixelSize: 1.0, foregroundColor: .Theme.Map.stageForeground, + backgroundColor: .Theme.Map.stageBackground + ) + .offset(CGSize(width: w(stages[1]), height: 0)) + .frame(width: w(stages[2]) - w(stages[1]), height: mapSize.height) + PatternView( + design: .constant(.wicker), pixelSize: 1.0, foregroundColor: .Theme.Map.stageForeground, + backgroundColor: .Theme.Map.stageBackground + ) + .offset(CGSize(width: w(stages[2]), height: 0)) + .frame(width: mapSize.width - w(stages[2]), height: mapSize.height) + } + } + + func w(_ dimension: CGFloat) -> CGFloat { + max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0)) + } +} + +#Preview { + MapBackground( + mapSize: CGSize(width: 200.0, height: 200.0), lineWidth: CGFloat(0.5), + stages: [25.0, 50.0, 75.0]) +} diff --git a/Map/Presentation/Base Components/MapRender/MapMask.swift b/Map/Presentation/Base Components/MapRender/MapMask.swift new file mode 100644 index 0000000..807310c --- /dev/null +++ b/Map/Presentation/Base Components/MapRender/MapMask.swift @@ -0,0 +1,67 @@ +// Copyright (C) 2024 Rubén Beltrán del Río + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see https://map.tranquil.systems. +import SwiftUI + +struct MapMask: View { + + let mapSize: CGSize + let vertexSize: CGSize + let vertices: [Vertex] + let padding = CGFloat(5.0) + + var body: some View { + ZStack(alignment: .topLeading) { + ForEach(vertices, id: \.id) { vertex in + ZStack(alignment: .topLeading) { + Text(vertex.label.replacingOccurrences(of: "\\n", with: "\n")) + .font(.Theme.Map.vertexLabel) + .lineSpacing( + Dimensions.LineHeight.Map.vertexLabel - Dimensions.FontSize.Map.vertexLabel + ) + .foregroundColor(.white) + .background(.white) + .padding(.horizontal, 1.0) + .clipShape(Capsule()) + .offset( + CGSize( + width: w(vertex.position.x) + vertexSize.width + padding, + height: h(vertex.position.y) + 7.0)) + .blendMode(.destinationOut) + }.zIndex(1) + } + Rectangle() + .fill(.black) + }.drawingGroup() + } + + func h(_ dimension: CGFloat) -> CGFloat { + max(0.0, min(mapSize.height, dimension * mapSize.height / 100.0)) + } + + func w(_ dimension: CGFloat) -> CGFloat { + max(0.0, min(mapSize.width, dimension * mapSize.width / 100.0)) + } +} + +#Preview { + MapMask( + mapSize: CGSize(width: 400.0, height: 400.0), vertexSize: CGSize(width: 25.0, height: 25.0), + vertices: [ + Vertex(id: 0, label: "A Circle", position: CGPoint(x: 50.0, y: 50.0)), + Vertex(id: 1, label: "A Square", position: CGPoint(x: 10.0, y: 20.0), shape: .square), + Vertex(id: 2, label: "A triangle", position: CGPoint(x: 25, y: 32.0), shape: .triangle), + Vertex(id: 3, label: "An X", position: CGPoint(x: 70.0, y: 70.0), shape: .x), + ]) +} diff --git a/Map/Presentation/Base Components/MapRender/MapStages.swift b/Map/Presentation/Base Components/MapRender/MapStages.swift index 08250f7..7d7e51e 100644 --- a/Map/Presentation/Base Components/MapRender/MapStages.swift +++ b/Map/Presentation/Base Components/MapRender/MapStages.swift @@ -24,30 +24,6 @@ struct MapStages: View { var body: some View { ZStack(alignment: .topLeading) { - PatternView( - design: .constant(.stitch), pixelSize: 1.0, foregroundColor: .Theme.Map.stageForeground, - backgroundColor: .Theme.Map.stageBackground - ) - .frame(width: w(stages[0]), height: mapSize.height) - PatternView( - design: .constant(.shingles), pixelSize: 1.0, foregroundColor: .Theme.Map.stageForeground, - backgroundColor: .Theme.Map.stageBackground - ) - .offset(CGSize(width: w(stages[0]), height: 0)) - .frame(width: w(stages[1]) - w(stages[0]), height: mapSize.height) - PatternView( - design: .constant(.shadowGrid), pixelSize: 1.0, foregroundColor: .Theme.Map.stageForeground, - backgroundColor: .Theme.Map.stageBackground - ) - .offset(CGSize(width: w(stages[1]), height: 0)) - .frame(width: w(stages[2]) - w(stages[1]), height: mapSize.height) - PatternView( - design: .constant(.wicker), pixelSize: 1.0, foregroundColor: .Theme.Map.stageForeground, - backgroundColor: .Theme.Map.stageBackground - ) - .offset(CGSize(width: w(stages[2]), height: 0)) - .frame(width: mapSize.width - w(stages[2]), height: mapSize.height) - Path { path in path.move(to: CGPoint(x: w(stages[0]), y: 0)) path.addLine(to: CGPoint(x: w(stages[0]), y: mapSize.height)) diff --git a/Map/Presentation/Base Components/MapRender/MapVertices.swift b/Map/Presentation/Base Components/MapRender/MapVertices.swift index 4f130c6..f03bb79 100644 --- a/Map/Presentation/Base Components/MapRender/MapVertices.swift +++ b/Map/Presentation/Base Components/MapRender/MapVertices.swift @@ -34,8 +34,6 @@ struct MapVertices: View { Dimensions.LineHeight.Map.vertexLabel - Dimensions.FontSize.Map.vertexLabel ) .foregroundColor(.Theme.Map.labelColor) - .shadow(color: .white, radius: 0, x: -0.5, y: -0.5) - .shadow(color: .white, radius: 0, x: 0.5, y: 0.5) .offset( CGSize( width: w(vertex.position.x) + vertexSize.width + padding, diff --git a/Map/Presentation/Base Components/SearchBar.swift b/Map/Presentation/Base Components/SearchBar.swift index cd2c378..cf19ddd 100644 --- a/Map/Presentation/Base Components/SearchBar.swift +++ b/Map/Presentation/Base Components/SearchBar.swift @@ -26,7 +26,7 @@ struct SearchBar: View { var body: some View { HStack(spacing: 2.0) { ZStack { - TextField("Search", text: $term) + TextField("search_bar.label", text: $term) .textFieldStyle(.roundedBorder) .padding(.trailing, 16.0) .focused($isSearchFocused) @@ -51,18 +51,18 @@ struct SearchBar: View { .font(.Theme.SmallControl.regular) }.keyboardShortcut( "g", modifiers: EventModifiers([.command, .shift]) - ).help("Find Previous (⇧⌘G)") + ).help("search_bar.previous.help") Button(action: onNext) { Image(systemName: "chevron.right") .font(.Theme.SmallControl.regular) }.keyboardShortcut( "g", modifiers: EventModifiers([.command]) - ).help("Find Next (⌘G)") + ).help("search_bar.next.help") Button(action: onDismiss) { - Text("Done") + Text("search_bar.done") .font(.Theme.SmallControl.regular) }.keyboardShortcut(.escape, modifiers: EventModifiers()) - .help("Done (⎋)") + .help("search_bar.done.help") }.padding(4.0) } } diff --git a/Map/Presentation/Commands/UpdateCommands.swift b/Map/Presentation/Commands/UpdateCommands.swift index bfe7078..2a8e654 100644 --- a/Map/Presentation/Commands/UpdateCommands.swift +++ b/Map/Presentation/Commands/UpdateCommands.swift @@ -37,7 +37,7 @@ struct CheckForUpdatesView: View { } var body: some View { - Button("Check for Updates…", action: updater.checkForUpdates) + Button("commands.application.check_for_updates", action: updater.checkForUpdates) .disabled(!checkForUpdatesViewModel.canCheckForUpdates) } } diff --git a/Map/Presentation/Complex Components/MapRender/MapRenderView.swift b/Map/Presentation/Complex Components/MapRender/MapRenderView.swift index 780290b..2357013 100644 --- a/Map/Presentation/Complex Components/MapRender/MapRenderView.swift +++ b/Map/Presentation/Complex Components/MapRender/MapRenderView.swift @@ -21,6 +21,7 @@ struct MapRenderView: View { @Binding var document: MapDocument @Binding var evolution: StageType + @AppStorage("showMapBackground") var showMapBackground: Bool = true var stage: Stage { Stage.stages(evolution) @@ -47,25 +48,36 @@ struct MapRenderView: View { x: -padding, y: -padding, width: mapSize.width + padding * 2, height: mapSize.height + padding * 4)) }.fill(.white) + + // The order Matters. Think of this as layers. - MapStages(mapSize: mapSize, lineWidth: lineWidth, stages: parsedMap.stages) + if showMapBackground { + MapBackground(mapSize: mapSize, lineWidth: lineWidth, stages: parsedMap.stages) + } MapAxes( mapSize: mapSize, lineWidth: lineWidth, evolution: stage, stages: parsedMap.stages) - MapEdges( - mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize, edges: parsedMap.edges) - MapBlockers(mapSize: mapSize, vertexSize: vertexSize, blockers: parsedMap.blockers) - MapVertices( - mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices, - onDragVertex: onDragVertex) - MapOpportunities( - mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize, - opportunities: parsedMap.opportunities) + Group { + MapStages(mapSize: mapSize, lineWidth: lineWidth, stages: parsedMap.stages) + MapEdges( + mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize, edges: parsedMap.edges) + MapOpportunities( + mapSize: mapSize, lineWidth: lineWidth, vertexSize: vertexSize, + opportunities: parsedMap.opportunities) + MapBlockers(mapSize: mapSize, vertexSize: vertexSize, blockers: parsedMap.blockers) + }.mask { + MapMask( + mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices) + } + MapVertices( + mapSize: mapSize, vertexSize: vertexSize, vertices: parsedMap.vertices, + onDragVertex: onDragVertex) MapGroups(mapSize: mapSize, vertexSize: vertexSize, groups: parsedMap.groups).drawingGroup( opaque: true ).opacity(0.1) MapNotes( mapSize: mapSize, lineWidth: lineWidth, notes: parsedMap.notes) - }.offset(x: padding, y: padding).frame( + } + .offset(x: padding, y: padding).frame( width: mapSize.width + 2 * padding, height: mapSize.height + 2 * padding, alignment: .topLeading ) diff --git a/Map/Presentation/Preferences/MapPreferencesView.swift b/Map/Presentation/Preferences/MapPreferencesView.swift index fb5f8c2..f8c976e 100644 --- a/Map/Presentation/Preferences/MapPreferencesView.swift +++ b/Map/Presentation/Preferences/MapPreferencesView.swift @@ -16,14 +16,36 @@ import SwiftUI struct MapPreferencesView: View { + @AppStorage("showMapBackground") private var showBackground = true + @AppStorage("useSmartEditor") private var useSmartEditor = false + var body: some View { - VStack { - Text("Map Preferences") - .font(.largeTitle) - .padding() - - Text("Map settings will be available here in a future version.") - .foregroundColor(.secondary) + VStack(alignment: .center, spacing: Dimensions.Spacing.loose) { + HStack(alignment: .firstTextBaseline, spacing: Dimensions.Spacing.regular) { + Text("preferences.map.map_style.title") + .font(.Theme.Body.emphasized) + .frame(maxWidth: .infinity, alignment: .trailing) + + VStack(alignment: .leading, spacing: Dimensions.Spacing.cozy) { + Toggle(isOn: $showBackground, label: { Text("preferences.map.map_style.show_background") + .font(.Theme.Body.regular) + }) + } + .frame(maxWidth: .infinity, alignment: .leading) + } + + Divider() + + HStack(alignment: .firstTextBaseline, spacing: Dimensions.Spacing.regular) { + Text("preferences.map.editor.title") + .font(.Theme.Body.emphasized) + .frame(maxWidth: .infinity, alignment: .trailing) + + VStack(alignment: .leading, spacing: Dimensions.Spacing.cozy) { + Toggle(String(localized: "preferences.map.editor.use_smart_editor"), isOn: $useSmartEditor) + } + .frame(maxWidth: .infinity, alignment: .leading) + } Spacer() } diff --git a/Map/Presentation/Preferences/StagesPreferencesView.swift b/Map/Presentation/Preferences/StagesPreferencesView.swift index cd347bf..6d2154c 100644 --- a/Map/Presentation/Preferences/StagesPreferencesView.swift +++ b/Map/Presentation/Preferences/StagesPreferencesView.swift @@ -18,23 +18,14 @@ import SwiftUI struct StagesPreferencesView: View { @AppStorage("customStages") private var customStagesData: Data = Data() @State private var customStages: [CustomStage] = [] + @State private var showingHelpPopover = false var body: some View { VStack(alignment: .leading, spacing: Dimensions.Spacing.regular) { - Text("preferences.stages.title") - .font(.Theme.Title.emphasized) - .lineSpacing(Dimensions.LineHeight.title - Dimensions.FontSize.title) - Text("preferences.stages.explanation") - .font(.Theme.Body.regular) - .kerning(Dimensions.Kerning.body) - .lineSpacing(Dimensions.LineHeight.body - Dimensions.FontSize.body) - .padding(.top, Dimensions.Spacing.loose) - .padding(.bottom, Dimensions.Spacing.regular) - Table(customStages) { - TableColumn("Name") { customStage in + TableColumn("preferences.stages.column.name") { customStage in TextField( - "Stage Name", + "preferences.stages.placeholder.name", text: Binding( get: { customStage.name }, set: { newValue in @@ -46,9 +37,9 @@ struct StagesPreferencesView: View { } .width(120) - TableColumn("Stage I") { customStage in + TableColumn("preferences.stages.column.stage_i") { customStage in TextField( - "Stage I", + "preferences.stages.placeholder.stage_i", text: Binding( get: { customStage.stage.i }, set: { newValue in @@ -59,9 +50,9 @@ struct StagesPreferencesView: View { .textFieldStyle(.plain) } - TableColumn("Stage II") { customStage in + TableColumn("preferences.stages.column.stage_ii") { customStage in TextField( - "Stage II", + "preferences.stages.placeholder.stage_ii", text: Binding( get: { customStage.stage.ii }, set: { newValue in @@ -72,9 +63,9 @@ struct StagesPreferencesView: View { .textFieldStyle(.plain) } - TableColumn("Stage III") { customStage in + TableColumn("preferences.stages.column.stage_iii") { customStage in TextField( - "Stage III", + "preferences.stages.placeholder.stage_iii", text: Binding( get: { customStage.stage.iii }, set: { newValue in @@ -85,9 +76,9 @@ struct StagesPreferencesView: View { .textFieldStyle(.plain) } - TableColumn("Stage IV") { customStage in + TableColumn("preferences.stages.column.stage_iv") { customStage in TextField( - "Stage IV", + "preferences.stages.placeholder.stage_iv", text: Binding( get: { customStage.stage.iv }, set: { newValue in @@ -108,8 +99,20 @@ struct StagesPreferencesView: View { } .tableStyle(.bordered(alternatesRowBackgrounds: true)) - HStack { + HStack(spacing: Dimensions.Spacing.regular) { Spacer() + Button(action: { showingHelpPopover = true }) { + Image(systemName: "questionmark.circle") + } + .buttonStyle(.borderless) + .popover(isPresented: $showingHelpPopover, arrowEdge: .bottom) { + Text("preferences.stages.explanation") + .font(.Theme.Body.regular) + .kerning(Dimensions.Kerning.body) + .lineSpacing(Dimensions.LineHeight.body - Dimensions.FontSize.body) + .frame(width: 300) + .padding(Dimensions.Spacing.regular) + } Button(action: addStage) { Image(systemName: "plus") } diff --git a/Map/Presentation/Preferences/TemplatesPreferencesView.swift b/Map/Presentation/Preferences/TemplatesPreferencesView.swift index 92b9b09..2b70b67 100644 --- a/Map/Presentation/Preferences/TemplatesPreferencesView.swift +++ b/Map/Presentation/Preferences/TemplatesPreferencesView.swift @@ -41,6 +41,7 @@ struct TemplatesPreferencesView: View { @State private var selectedTemplate: Template? @State private var showingAddSheet = false @State private var newTemplateName = "" + @State private var showingHelpPopover = false private var templates: Binding<[Template]> { Binding( @@ -80,15 +81,6 @@ struct TemplatesPreferencesView: View { var body: some View { VStack(alignment: .leading, spacing: Dimensions.Spacing.regular) { - Text("preferences.templates.title") - .font(.Theme.Title.emphasized) - .lineSpacing(Dimensions.LineHeight.title - Dimensions.FontSize.title) - Text("preferences.templates.explanation") - .font(.Theme.Body.regular) - .kerning(Dimensions.Kerning.body) - .lineSpacing(Dimensions.LineHeight.body - Dimensions.FontSize.body) - .padding(.top, Dimensions.Spacing.loose) - .padding(.bottom, Dimensions.Spacing.regular) HStack(spacing: Dimensions.Spacing.cozy) { VStack(alignment: .leading, spacing: Dimensions.Spacing.regular) { List(templates.wrappedValue) { template in @@ -135,24 +127,39 @@ struct TemplatesPreferencesView: View { .listStyle(.plain) .border(Color.Theme.UI.foreground.opacity(0.2), width: 1) - HStack(spacing: Dimensions.Spacing.coziest) { - + HStack(spacing: Dimensions.Spacing.regular) { + Spacer() - - Button(action: { showingAddSheet = true }) { - Image(systemName: "plus") + + Button(action: { showingHelpPopover = true }) { + Image(systemName: "questionmark.circle") } - .keyboardShortcut("t", modifiers: [.command]) - .help("preferences.templates.help.add") - - Button(action: { - removeTemplate(selectedTemplate) - }) { - Image(systemName: "minus") + .buttonStyle(.borderless) + .popover(isPresented: $showingHelpPopover, arrowEdge: .bottom) { + Text("preferences.templates.explanation") + .font(.Theme.Body.regular) + .kerning(Dimensions.Kerning.body) + .lineSpacing(Dimensions.LineHeight.body - Dimensions.FontSize.body) + .frame(width: 300) + .padding(Dimensions.Spacing.regular) + } + + HStack(spacing: Dimensions.Spacing.coziest) { + Button(action: { showingAddSheet = true }) { + Image(systemName: "plus") + } + .keyboardShortcut("t", modifiers: [.command]) + .help("preferences.templates.help.add") + + Button(action: { + removeTemplate(selectedTemplate) + }) { + Image(systemName: "minus") + } + .keyboardShortcut(.delete, modifiers: [.command]) + .help("preferences.templates.help.remove \(selectedTemplate?.name ?? "")") + .disabled(selectedTemplate == nil) } - .keyboardShortcut(.delete, modifiers: [.command]) - .help("preferences.templates.help.remove \(selectedTemplate?.name ?? "")") - .disabled(selectedTemplate == nil) } } .frame(width: Dimensions.Preferences.Sidebar.width) diff --git a/Map/Presentation/PreferencesWindow.swift b/Map/Presentation/PreferencesWindow.swift index 2471711..202a637 100644 --- a/Map/Presentation/PreferencesWindow.swift +++ b/Map/Presentation/PreferencesWindow.swift @@ -15,12 +15,28 @@ import SwiftUI -enum PreferencesTab: String, CaseIterable { - case general = "General" - case appearance = "Appearance" - case map = "Map" - case stages = "Stages" - case templates = "Templates" +enum PreferencesTab: CaseIterable { + + case general + case appearance + case map + case stages + case templates + + var localizedStringKey: LocalizedStringKey { + switch self { + case .general: + return "preferences.menu.general" + case .appearance: + return "preferences.menu.appearance" + case .map: + return "preferences.menu.map" + case .stages: + return "preferences.menu.stages" + case .templates: + return "preferences.menu.templates" + } + } var systemImage: String { switch self { @@ -65,14 +81,14 @@ struct PreferencesWindow: View { VStack(spacing: Dimensions.Spacing.coziest) { Image(systemName: tab.systemImage) .font(.system(size: Dimensions.Preferences.Toolbar.size)) - Text(tab.rawValue) + Text(tab.localizedStringKey) .font(.Theme.SmallControl.regular) } .frame( width: Dimensions.Preferences.Toolbar.width, height: Dimensions.Preferences.Toolbar.height ) - .padding(.horizontal, Dimensions.Spacing.coziest) + .padding(.horizontal, Dimensions.Spacing.cozy) .background( selectedTab == tab ? Color.Theme.UI.accent : Color.background.opacity(0.01) ) |