From 3cd8f1f7200109d505b5e105d5ebe53f0c93d337 Mon Sep 17 00:00:00 2001 From: Ruben Beltran del Rio Date: Sat, 5 Jul 2025 23:40:26 +0200 Subject: Improve defaults and style for stages --- Map/Business/CustomStage.swift | 24 +++ Map/Localizable.xcstrings | 132 +++++++++++++++++ .../Preferences/StagesPreferencesView.swift | 46 +++--- .../Preferences/TemplatesPreferencesView.swift | 165 +++++++++++---------- Map/Presentation/PreferencesWindow.swift | 18 +-- Map/Presentation/Theme/Dimensions.swift | 26 +++- 6 files changed, 301 insertions(+), 110 deletions(-) (limited to 'Map') diff --git a/Map/Business/CustomStage.swift b/Map/Business/CustomStage.swift index 32d4c2c..388531b 100644 --- a/Map/Business/CustomStage.swift +++ b/Map/Business/CustomStage.swift @@ -24,4 +24,28 @@ struct CustomStage: Identifiable, Codable { self.name = name self.stage = stage } + + static func cynefinDefault() -> CustomStage { + return CustomStage( + name: String(localized: "stage_type.cynefin"), + stage: Stage( + i: String(localized: "stage_type.cynefin.i"), + ii: String(localized: "stage_type.cynefin.ii"), + iii: String(localized: "stage_type.cynefin.iii"), + iv: String(localized: "stage_type.cynefin.iv") + ) + ) + } + + static func placeholderDefault() -> CustomStage { + return CustomStage( + name: String(localized: "stage_type.placeholder"), + stage: Stage( + i: String(localized: "stage_type.placeholder.i"), + ii: String(localized: "stage_type.placeholder.ii"), + iii: String(localized: "stage_type.placeholder.iii"), + iv: String(localized: "stage_type.placeholder.iv") + ) + ) + } } \ No newline at end of file diff --git a/Map/Localizable.xcstrings b/Map/Localizable.xcstrings index f6446eb..f5fd775 100644 --- a/Map/Localizable.xcstrings +++ b/Map/Localizable.xcstrings @@ -97,6 +97,28 @@ } } }, + "preferences.templates.explanation" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Templates let you create starting points for similar or recurring map types. You can use them from the file menu." + } + } + } + }, + "preferences.templates.title" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Map Templates" + } + } + } + }, "Search" : { }, @@ -151,6 +173,61 @@ } } }, + "stage_type.cynefin" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cynefin" + } + } + } + }, + "stage_type.cynefin.i" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chaotic" + } + } + } + }, + "stage_type.cynefin.ii" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Complex" + } + } + } + }, + "stage_type.cynefin.iii" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Complicated" + } + } + } + }, + "stage_type.cynefin.iv" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Clear" + } + } + } + }, "stage_type.data" : { "extractionState" : "manual", "localizations" : { @@ -283,6 +360,61 @@ } } }, + "stage_type.placeholder" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Evolution Stage" + } + } + } + }, + "stage_type.placeholder.i" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage I" + } + } + } + }, + "stage_type.placeholder.ii" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage II" + } + } + } + }, + "stage_type.placeholder.iii" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage III" + } + } + } + }, + "stage_type.placeholder.iv" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stage IV" + } + } + } + }, "stage_type.practice" : { "extractionState" : "manual", "localizations" : { diff --git a/Map/Presentation/Preferences/StagesPreferencesView.swift b/Map/Presentation/Preferences/StagesPreferencesView.swift index 7131034..1e57cbf 100644 --- a/Map/Presentation/Preferences/StagesPreferencesView.swift +++ b/Map/Presentation/Preferences/StagesPreferencesView.swift @@ -20,21 +20,16 @@ struct StagesPreferencesView: View { @State private var customStages: [CustomStage] = [] var body: some View { - VStack(alignment: .leading, spacing: 16) { + VStack(alignment: .leading, spacing: Dimensions.Spacing.regular) { Text("preferences.stages.title") .font(.Theme.Title.emphasized) - .lineSpacing(Dimensions.FontSize.title - Dimensions.FontSize.title) + .lineSpacing(Dimensions.LineHeight.title - Dimensions.FontSize.title) Text("preferences.stages.explanation") - .font(.Theme.Body.emphasized) - .lineSpacing(Dimensions.FontSize.body - Dimensions.FontSize.body) - - HStack { - Spacer() - Button(action: addStage) { - Image(systemName: "plus") - } - .buttonStyle(.borderless) - } + .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 @@ -47,7 +42,7 @@ struct StagesPreferencesView: View { } ) ) - .textFieldStyle(.roundedBorder) + .textFieldStyle(.plain) } .width(120) @@ -61,7 +56,7 @@ struct StagesPreferencesView: View { } ) ) - .textFieldStyle(.roundedBorder) + .textFieldStyle(.plain) } TableColumn("Stage II") { customStage in @@ -74,7 +69,7 @@ struct StagesPreferencesView: View { } ) ) - .textFieldStyle(.roundedBorder) + .textFieldStyle(.plain) } TableColumn("Stage III") { customStage in @@ -87,7 +82,7 @@ struct StagesPreferencesView: View { } ) ) - .textFieldStyle(.roundedBorder) + .textFieldStyle(.plain) } TableColumn("Stage IV") { customStage in @@ -100,7 +95,7 @@ struct StagesPreferencesView: View { } ) ) - .textFieldStyle(.roundedBorder) + .textFieldStyle(.plain) } TableColumn("") { customStage in @@ -113,9 +108,16 @@ struct StagesPreferencesView: View { } .tableStyle(.bordered(alternatesRowBackgrounds: true)) - Spacer() + HStack { + Spacer() + Button(action: addStage) { + Image(systemName: "plus") + } + .buttonStyle(.borderless) + } } - .padding() + .padding(.vertical, Dimensions.Spacing.loose) + .padding(.horizontal, Dimensions.Spacing.indulgent) .onAppear { loadStages() } @@ -125,7 +127,9 @@ struct StagesPreferencesView: View { if let decoded = try? JSONDecoder().decode([CustomStage].self, from: customStagesData) { customStages = decoded } else { - customStages = [CustomStage()] + // First use - create default Cynefin stage + customStages = [CustomStage.cynefinDefault()] + saveStages() // Save the default to UserDefaults } } @@ -136,7 +140,7 @@ struct StagesPreferencesView: View { } private func addStage() { - customStages.append(CustomStage()) + customStages.append(CustomStage.placeholderDefault()) saveStages() } diff --git a/Map/Presentation/Preferences/TemplatesPreferencesView.swift b/Map/Presentation/Preferences/TemplatesPreferencesView.swift index 806c22b..42db1a3 100644 --- a/Map/Presentation/Preferences/TemplatesPreferencesView.swift +++ b/Map/Presentation/Preferences/TemplatesPreferencesView.swift @@ -86,95 +86,106 @@ struct TemplatesPreferencesView: View { @State private var newTemplateName = "" var body: some View { - HStack(spacing: 0) { - VStack(alignment: .leading, spacing: 8) { - HStack { - Text("Templates") - .font(.headline) - - Spacer() - - Button(action: { showingAddSheet = true }) { - Image(systemName: "plus") - } - .buttonStyle(.borderless) - - Button(action: removeTemplate) { - Image(systemName: "minus") - } - .buttonStyle(.borderless) - .disabled(selectedTemplate == nil) - } - - List(templates, selection: $selectedTemplate) { template in - Text(template.name) - .tag(template) - } - .listStyle(.sidebar) - } - .frame(width: 200) - - Divider() - - VStack(alignment: .leading, spacing: 8) { - if let selected = selectedTemplate { + 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: 0) { + VStack(alignment: .leading, spacing: 8) { HStack { - Text("Template: \(selected.name)") + Text("Templates") .font(.headline) + Spacer() + + Button(action: { showingAddSheet = true }) { + Image(systemName: "plus") + } + .buttonStyle(.borderless) + + Button(action: removeTemplate) { + Image(systemName: "minus") + } + .buttonStyle(.borderless) + .disabled(selectedTemplate == nil) } - - SimpleTextEditor(text: $templateText) - .onChange(of: templateText) { _, newValue in - updateSelectedTemplate(newValue) + + List(templates, selection: $selectedTemplate) { template in + Text(template.name) + .tag(template) + } + .listStyle(.sidebar) + } + .frame(width: 200) + + Divider() + + VStack(alignment: .leading, spacing: 8) { + if let selected = selectedTemplate { + HStack { + Text("Template: \(selected.name)") + .font(.headline) + Spacer() } - } else { - VStack { - Spacer() - Text("Select a template to edit") - .foregroundColor(.secondary) - Spacer() + + SimpleTextEditor(text: $templateText) + .onChange(of: templateText) { _, newValue in + updateSelectedTemplate(newValue) + } + } else { + VStack { + Spacer() + Text("Select a template to edit") + .foregroundColor(.secondary) + Spacer() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) } - .frame(maxWidth: .infinity, maxHeight: .infinity) } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .padding(.leading) } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .padding(.leading) - } - .padding() - .onAppear { - loadTemplates() - } - .onChange(of: selectedTemplate) { _, newSelection in - if let template = newSelection { - templateText = template.content + .padding() + .onAppear { + loadTemplates() } - } - .sheet(isPresented: $showingAddSheet) { - VStack(spacing: 16) { - Text("Add New Template") - .font(.headline) - - TextField("Template Name", text: $newTemplateName) - .textFieldStyle(.roundedBorder) - - HStack { - Button("Cancel") { - showingAddSheet = false - newTemplateName = "" - } - - Spacer() - - Button("Add") { - addTemplate() + .onChange(of: selectedTemplate) { _, newSelection in + if let template = newSelection { + templateText = template.content + } + } + .sheet(isPresented: $showingAddSheet) { + VStack(spacing: 16) { + Text("Add New Template") + .font(.headline) + + TextField("Template Name", text: $newTemplateName) + .textFieldStyle(.roundedBorder) + + HStack { + Button("Cancel") { + showingAddSheet = false + newTemplateName = "" + } + + Spacer() + + Button("Add") { + addTemplate() + } + .disabled(newTemplateName.isEmpty) } - .disabled(newTemplateName.isEmpty) } + .frame(width: 300, height: 120) } - .padding() - .frame(width: 300, height: 120) - } + }.padding(.vertical, Dimensions.Spacing.loose) + .padding(.horizontal, Dimensions.Spacing.indulgent) } private func loadTemplates() { diff --git a/Map/Presentation/PreferencesWindow.swift b/Map/Presentation/PreferencesWindow.swift index 22bd987..da2b75f 100644 --- a/Map/Presentation/PreferencesWindow.swift +++ b/Map/Presentation/PreferencesWindow.swift @@ -56,22 +56,22 @@ struct PreferencesWindow: View { TemplatesPreferencesView() } } - .frame(width: 600, height: 400) + .frame(width: Dimensions.Preferences.Window.width, height: Dimensions.Preferences.Window.height) .toolbar { ToolbarItem(placement: .principal) { - HStack(spacing: 12) { + HStack(spacing: Dimensions.Spacing.loose) { ForEach(PreferencesTab.allCases, id: \.self) { tab in Button(action: { selectedTab = tab }) { - VStack(spacing: 2) { + VStack(spacing: Dimensions.Spacing.coziest) { Image(systemName: tab.systemImage) - .font(.system(size: 16)) + .font(.system(size: Dimensions.Preferences.Toolbar.size)) Text(tab.rawValue) - .font(.caption2) + .font(.Theme.SmallControl.regular) } - .frame(width: 60, height: 40) - .background(selectedTab == tab ? Color.accentColor.opacity(0.2) : Color.clear) - .foregroundColor(selectedTab == tab ? .accentColor : .primary) - .cornerRadius(6) + .frame(width: Dimensions.Preferences.Toolbar.width, height: Dimensions.Preferences.Toolbar.height) + .background(selectedTab == tab ? Color.Theme.UI.accent : Color.clear) + .foregroundColor(selectedTab == tab ? Color.white : Color.Theme.UI.foreground) + .cornerRadius(Dimensions.Preferences.Toolbar.radius) } .buttonStyle(.plain) } diff --git a/Map/Presentation/Theme/Dimensions.swift b/Map/Presentation/Theme/Dimensions.swift index 7df3f1a..5597c4e 100644 --- a/Map/Presentation/Theme/Dimensions.swift +++ b/Map/Presentation/Theme/Dimensions.swift @@ -19,10 +19,10 @@ struct Dimensions { // MARK: - Fonts struct FontSize { - static let body: CGFloat = 15 + static let body: CGFloat = 13 static let title: CGFloat = 32 - static let caption: CGFloat = 13 - static let smallControl: CGFloat = 11 + static let caption: CGFloat = 11 + static let smallControl: CGFloat = 9 struct Map { static let note: CGFloat = 12 @@ -43,6 +43,10 @@ struct Dimensions { static let vertexLabel: CGFloat = 18 } } + + struct Kerning { + static let body: CGFloat = 0.5 + } // MARK: - Spacing @@ -62,9 +66,25 @@ struct Dimensions { } // MARK: - Assorted Controls + struct EvolutionPicker { static let radius = 8.0 static let controlSize = 8.0 static let controlSpacing = 3.0 } + + struct Preferences { + + struct Window { + static let width: CGFloat = 600.0 + static let height: CGFloat = 400.0 + } + + struct Toolbar { + static let radius: CGFloat = 8.0 + static let size: CGFloat = 16.0 + static let width: CGFloat = 60.0 + static let height: CGFloat = 40.0 + } + } } -- cgit