aboutsummaryrefslogtreecommitdiff
path: root/Map
diff options
context:
space:
mode:
authorRuben Beltran del Rio <git@r.bdr.sh>2025-07-07 16:52:28 +0200
committerRuben Beltran del Rio <git@r.bdr.sh>2025-07-07 16:52:28 +0200
commite59dcf2bcf7841ebe6f3d85ec3ec03319b261da4 (patch)
tree3311ea9e5fbc9f26fe1b170318c9b179faf42dff /Map
parent3bacbf6ac85330d35493953d7296e5ba420b7750 (diff)
More localization keys, font editor preferences, better new file
Diffstat (limited to 'Map')
-rw-r--r--Map/Data/MapDocument.swift18
-rw-r--r--Map/Localizable.xcstrings86
-rw-r--r--Map/Presentation/Base Components/MapTextEditor.swift2
-rw-r--r--Map/Presentation/Commands/MapCommands.swift23
-rw-r--r--Map/Presentation/MapEditor.swift4
-rw-r--r--Map/Presentation/Preferences/AppearancePreferencesView.swift36
-rw-r--r--Map/Presentation/Preferences/EditorPreferencesView.swift120
-rw-r--r--Map/Presentation/Preferences/MapPreferencesView.swift23
-rw-r--r--Map/Presentation/PreferencesWindow.swift14
-rw-r--r--Map/Presentation/Theme/Dimensions.swift11
-rw-r--r--Map/Presentation/Theme/NSFont+theme.swift36
11 files changed, 273 insertions, 100 deletions
diff --git a/Map/Data/MapDocument.swift b/Map/Data/MapDocument.swift
index 2377d5a..4d1f78b 100644
--- a/Map/Data/MapDocument.swift
+++ b/Map/Data/MapDocument.swift
@@ -30,24 +30,16 @@ struct MapDocument: FileDocument {
if let text = text {
self.text = text
} else {
- // Try to get default template, fallback to built-in content
- if let templatesData = UserDefaults.standard.data(forKey: "mapTemplates"),
+ // Check for pending template content first
+ if let pendingContent = UserDefaults.standard.string(forKey: "pendingTemplateContent") {
+ self.text = pendingContent
+ } else if let templatesData = UserDefaults.standard.data(forKey: "mapTemplates"),
let templates = try? JSONDecoder().decode([Template].self, from: templatesData),
let defaultTemplate = templates.first(where: { $0.isDefault })
{
self.text = defaultTemplate.content
} else {
- self.text = """
- Anchor (60, 5) [x]
- Node A (78, 23)
- Node B (40, 30) [square]
- Node C (65, 70)
-
- Anchor -> Node A
- Anchor -> Node B
- Node A -> Node C
- Node B -> Node C
- """
+ self.text = String(localized: "map_template.default.content")
}
}
}
diff --git a/Map/Localizable.xcstrings b/Map/Localizable.xcstrings
index 4dc5e1b..73a58dd 100644
--- a/Map/Localizable.xcstrings
+++ b/Map/Localizable.xcstrings
@@ -4,12 +4,6 @@
"" : {
"shouldTranslate" : false
},
- "Appearance Preferences" : {
-
- },
- "Appearance settings will be available here in a future version." : {
-
- },
"commands.application.check_for_updates" : {
"extractionState" : "manual",
"localizations" : {
@@ -43,6 +37,28 @@
}
}
},
+ "commands.file.export.panel.content" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Choose a location to save the image"
+ }
+ }
+ }
+ },
+ "commands.file.export.panel.title %@" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Save “%@” as image."
+ }
+ }
+ }
+ },
"commands.file.new_from_template" : {
"extractionState" : "manual",
"localizations" : {
@@ -236,18 +252,51 @@
}
}
},
- "preferences.map.editor.title" : {
+ "preferences.editor.editor_style.title" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Editor Style:"
+ }
+ }
+ }
+ },
+ "preferences.editor.editor_style.use_custom_font" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Editor:"
+ "value" : "Use custom font"
}
}
}
},
- "preferences.map.editor.use_smart_editor" : {
+ "preferences.editor.font_size.decrease.help" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Decrease editor font size"
+ }
+ }
+ }
+ },
+ "preferences.editor.font_size.increase.help" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Increase editor font size"
+ }
+ }
+ }
+ },
+ "preferences.editor.smart_editor.enabled" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
@@ -258,13 +307,24 @@
}
}
},
+ "preferences.editor.smart_editor.title" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Smart Editor:"
+ }
+ }
+ }
+ },
"preferences.map.map_style.show_background" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Show Background"
+ "value" : "Draw background patterns"
}
}
}
@@ -286,18 +346,18 @@
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Use Custom Font"
+ "value" : "Use custom font"
}
}
}
},
- "preferences.menu.appearance" : {
+ "preferences.menu.editor" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Appearance"
+ "value" : "Editor"
}
}
}
diff --git a/Map/Presentation/Base Components/MapTextEditor.swift b/Map/Presentation/Base Components/MapTextEditor.swift
index b247047..5c7d74c 100644
--- a/Map/Presentation/Base Components/MapTextEditor.swift
+++ b/Map/Presentation/Base Components/MapTextEditor.swift
@@ -71,7 +71,7 @@ class MapTextEditorController: NSViewController {
textView.textStorage?.delegate = self
textView.string = self.text
textView.isEditable = true
- textView.font = .monospacedSystemFont(ofSize: 16.0, weight: .regular)
+ textView.font = NSFont.Theme.Editor.regular
textView.textContainerInset = NSSize(width: 0, height: Dimensions.Spacing.coziest)
self.view = scrollView
}
diff --git a/Map/Presentation/Commands/MapCommands.swift b/Map/Presentation/Commands/MapCommands.swift
index bd0227d..baf69ad 100644
--- a/Map/Presentation/Commands/MapCommands.swift
+++ b/Map/Presentation/Commands/MapCommands.swift
@@ -66,8 +66,8 @@ struct MapCommands: Commands {
savePanel.allowedContentTypes = [.png]
savePanel.canCreateDirectories = true
savePanel.isExtensionHidden = false
- savePanel.title = "Save \(filename) as image"
- savePanel.message = "Choose a location to save the image"
+ savePanel.title = String(localized: "commands.file.export.panel.title \(filename)")
+ savePanel.message = String(localized: "commands.file.export.panel.content")
savePanel.nameFieldStringValue = "\(filename).png"
savePanel.begin { result in
if result == .OK, let url = savePanel.url {
@@ -139,17 +139,16 @@ struct MapCommands: Commands {
@MainActor
private func createNewDocument(from template: Template) {
- // Create a temporary file with the template content
- let temporaryDirectory = FileManager.default.temporaryDirectory
- let temporaryFile = temporaryDirectory.appendingPathComponent("Template-\(template.name).wmap")
+ // Store the template content in UserDefaults temporarily
+ UserDefaults.standard.set(template.content, forKey: "pendingTemplateContent")
- do {
- try template.content.write(to: temporaryFile, atomically: true, encoding: .utf8)
- NSWorkspace.shared.open(temporaryFile)
- } catch {
- // Fallback: just trigger the new document command
- NSApplication.shared.sendAction(
- #selector(NSDocumentController.newDocument(_:)), to: nil, from: nil)
+ // Create a new document using the standard mechanism
+ NSApplication.shared.sendAction(
+ #selector(NSDocumentController.newDocument(_:)), to: nil, from: nil)
+
+ // Clear the pending template content after a short delay
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+ UserDefaults.standard.removeObject(forKey: "pendingTemplateContent")
}
}
}
diff --git a/Map/Presentation/MapEditor.swift b/Map/Presentation/MapEditor.swift
index 4116171..56ac898 100644
--- a/Map/Presentation/MapEditor.swift
+++ b/Map/Presentation/MapEditor.swift
@@ -23,6 +23,9 @@ struct MapEditor: View {
private let changeDebouncer: Debouncer = Debouncer(seconds: 0.05)
@AppStorage("viewStyle") var viewStyle: ViewStyle = .horizontal
+ @AppStorage("useCustomEditorFont") var useCustomEditorFont: Bool = false
+ @AppStorage("customEditorFontName") var customEditorFontName: String = "Menlo"
+ @AppStorage("editorFontSize") var editorFontSize: Double = 14
let zoomRange = Constants.kMinZoom...Constants.kMaxZoom
@AppStorage("zoom") var zoom = 1.0
@@ -99,6 +102,7 @@ struct MapEditor: View {
highlightRanges: results,
selectedRange: selectedTerm
)
+ .id("\(useCustomEditorFont)-\(customEditorFontName)-\(editorFontSize)")
.background(Color.Theme.UI.background)
.foregroundColor(Color.Theme.UI.foreground)
.frame(minHeight: 96.0)
diff --git a/Map/Presentation/Preferences/AppearancePreferencesView.swift b/Map/Presentation/Preferences/AppearancePreferencesView.swift
deleted file mode 100644
index a71276d..0000000
--- a/Map/Presentation/Preferences/AppearancePreferencesView.swift
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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 AppearancePreferencesView: View {
- var body: some View {
- VStack {
- Text("Appearance Preferences")
- .font(.largeTitle)
- .padding()
-
- Text("Appearance settings will be available here in a future version.")
- .foregroundColor(.secondary)
-
- Spacer()
- }
- .padding()
- }
-}
-
-#Preview {
- AppearancePreferencesView()
-}
diff --git a/Map/Presentation/Preferences/EditorPreferencesView.swift b/Map/Presentation/Preferences/EditorPreferencesView.swift
new file mode 100644
index 0000000..3d456c9
--- /dev/null
+++ b/Map/Presentation/Preferences/EditorPreferencesView.swift
@@ -0,0 +1,120 @@
+// 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 EditorPreferencesView: View {
+
+ @AppStorage("useSmartEditor") private var useSmartEditor = false
+ @AppStorage("useCustomEditorFont") private var useCustomEditorFont = false
+ @AppStorage("customEditorFontName") private var customEditorFontName = "Menlo"
+ @AppStorage("editorFontSize") private var editorFontSize: Double = 14
+
+ var monospacedFonts: [String] {
+ let fontManager = NSFontManager.shared
+ let monospaceTrait = NSFontTraitMask.fixedPitchFontMask
+
+ return fontManager.availableFontFamilies.filter { familyName in
+ let members = fontManager.availableMembers(ofFontFamily: familyName) ?? []
+ return members.contains { member in
+ if let traits = member[3] as? NSNumber {
+ return NSFontTraitMask(rawValue: traits.uintValue).contains(monospaceTrait)
+ }
+ return false
+ }
+ }
+ }
+
+ var body: some View {
+ VStack(alignment: .center, spacing: Dimensions.Spacing.loose) {
+ HStack(alignment: .center, spacing: Dimensions.Spacing.regular) {
+ Button(action: {
+ if editorFontSize > 7 {
+ editorFontSize -= 1
+ }
+ }) {
+ Image(systemName: "textformat.size.smaller")
+ }
+ .buttonStyle(BorderlessButtonStyle())
+ .disabled(editorFontSize <= 7)
+ .help("preferences.editor.font_size.decrease.help")
+
+ Slider(value: $editorFontSize, in: 7...21, step: 1)
+ .frame(width: 250)
+
+ Button(action: {
+ if editorFontSize < 21 {
+ editorFontSize += 1
+ }
+ }) {
+ Image(systemName: "textformat.size.larger")
+ }
+ .buttonStyle(BorderlessButtonStyle())
+ .disabled(editorFontSize >= 21)
+ .help("preferences.editor.font_size.increase.help")
+ }
+
+ Divider()
+
+ HStack(alignment: .firstTextBaseline, spacing: Dimensions.Spacing.regular) {
+ Text("preferences.editor.editor_style.title")
+ .font(.Theme.Body.emphasized)
+ .frame(maxWidth: 250, alignment: .trailing)
+
+ VStack(alignment: .leading, spacing: Dimensions.Spacing.cozy) {
+
+ Toggle(
+ isOn: $useCustomEditorFont,
+ label: {
+ Text("preferences.editor.editor_style.use_custom_font")
+ .font(.Theme.Body.regular)
+ })
+
+ Picker("", selection: $customEditorFontName) {
+ ForEach(monospacedFonts, id: \.self) { fontFamily in
+ Text(fontFamily).tag(fontFamily)
+ }
+ }
+ .pickerStyle(MenuPickerStyle())
+ .frame(maxWidth: 250)
+ .padding(.leading, -Dimensions.Spacing.regular)
+ .disabled(!useCustomEditorFont)
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
+ }
+
+ Divider()
+
+ HStack(alignment: .firstTextBaseline, spacing: Dimensions.Spacing.regular) {
+ Text("preferences.editor.smart_editor.title")
+ .font(.Theme.Body.emphasized)
+ .frame(maxWidth: 250, alignment: .trailing)
+
+ VStack(alignment: .leading, spacing: Dimensions.Spacing.cozy) {
+ Toggle(
+ String(localized: "preferences.editor.smart_editor.enabled"), isOn: $useSmartEditor)
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
+ }
+
+ Spacer()
+ }.padding(.vertical, Dimensions.Spacing.loose)
+ .padding(.horizontal, Dimensions.Spacing.indulgent)
+ }
+}
+
+#Preview {
+ EditorPreferencesView()
+}
diff --git a/Map/Presentation/Preferences/MapPreferencesView.swift b/Map/Presentation/Preferences/MapPreferencesView.swift
index 264be14..d81916f 100644
--- a/Map/Presentation/Preferences/MapPreferencesView.swift
+++ b/Map/Presentation/Preferences/MapPreferencesView.swift
@@ -17,7 +17,6 @@ import SwiftUI
struct MapPreferencesView: View {
@AppStorage("showMapBackground") private var showBackground = true
- @AppStorage("useSmartEditor") private var useSmartEditor = false
@AppStorage("useCustomFont") private var useCustomFont = false
@AppStorage("customFontName") private var customFontName = "Helvetica"
@@ -26,7 +25,7 @@ struct MapPreferencesView: View {
HStack(alignment: .firstTextBaseline, spacing: Dimensions.Spacing.regular) {
Text("preferences.map.map_style.title")
.font(.Theme.Body.emphasized)
- .frame(maxWidth: .infinity, alignment: .trailing)
+ .frame(maxWidth: 250, alignment: .trailing)
VStack(alignment: .leading, spacing: Dimensions.Spacing.cozy) {
Toggle(
@@ -49,28 +48,16 @@ struct MapPreferencesView: View {
}
}
.pickerStyle(MenuPickerStyle())
+ .frame(maxWidth: 250)
+ .padding(.leading, -Dimensions.Spacing.regular)
.disabled(!useCustomFont)
}
.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()
- }
- .padding()
+ }.padding(.vertical, Dimensions.Spacing.loose)
+ .padding(.horizontal, Dimensions.Spacing.indulgent)
}
}
diff --git a/Map/Presentation/PreferencesWindow.swift b/Map/Presentation/PreferencesWindow.swift
index 962d5ca..fa5bd4c 100644
--- a/Map/Presentation/PreferencesWindow.swift
+++ b/Map/Presentation/PreferencesWindow.swift
@@ -18,7 +18,7 @@ import SwiftUI
enum PreferencesTab: CaseIterable {
case general
- case appearance
+ case editor
case map
case stages
case templates
@@ -27,8 +27,8 @@ enum PreferencesTab: CaseIterable {
switch self {
case .general:
return "preferences.menu.general"
- case .appearance:
- return "preferences.menu.appearance"
+ case .editor:
+ return "preferences.menu.editor"
case .map:
return "preferences.menu.map"
case .stages:
@@ -42,8 +42,8 @@ enum PreferencesTab: CaseIterable {
switch self {
case .general:
return "gearshape"
- case .appearance:
- return "paintpalette"
+ case .editor:
+ return "text.word.spacing"
case .map:
return "map"
case .stages:
@@ -62,8 +62,8 @@ struct PreferencesWindow: View {
switch selectedTab {
case .general:
GeneralPreferencesView()
- case .appearance:
- AppearancePreferencesView()
+ case .editor:
+ EditorPreferencesView()
case .map:
MapPreferencesView()
case .stages:
diff --git a/Map/Presentation/Theme/Dimensions.swift b/Map/Presentation/Theme/Dimensions.swift
index f31af9a..26b4496 100644
--- a/Map/Presentation/Theme/Dimensions.swift
+++ b/Map/Presentation/Theme/Dimensions.swift
@@ -24,6 +24,11 @@ struct Dimensions {
static let caption: CGFloat = 11
static let smallControl: CGFloat = 11
+ static var editor: CGFloat {
+ let fontSize = UserDefaults.standard.double(forKey: "editorFontSize")
+ return fontSize > 0 ? CGFloat(fontSize) : 14
+ }
+
struct Map {
static let note: CGFloat = 12
static let axisLabel: CGFloat = 14
@@ -37,6 +42,12 @@ struct Dimensions {
static let caption: CGFloat = 18
static let smallControl: CGFloat = 18
+ static var editor: CGFloat {
+ let fontSize = UserDefaults.standard.double(forKey: "editorFontSize")
+ let actualSize = fontSize > 0 ? fontSize : 14
+ return actualSize >= 16 ? 38 : 18
+ }
+
struct Map {
static let note: CGFloat = 18
static let axisLabel: CGFloat = 18
diff --git a/Map/Presentation/Theme/NSFont+theme.swift b/Map/Presentation/Theme/NSFont+theme.swift
new file mode 100644
index 0000000..5d8d8dd
--- /dev/null
+++ b/Map/Presentation/Theme/NSFont+theme.swift
@@ -0,0 +1,36 @@
+// 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 AppKit
+
+extension NSFont {
+ public struct Theme {
+ struct Editor {
+ static var regular: NSFont {
+ if UserDefaults.standard.bool(forKey: "useCustomEditorFont"),
+ let customEditorFontName = UserDefaults.standard.string(forKey: "customEditorFontName"),
+ !customEditorFontName.isEmpty
+ {
+ return NSFontManager.shared.font(
+ withFamily: customEditorFontName,
+ traits: NSFontTraitMask(),
+ weight: 5,
+ size: Dimensions.FontSize.editor)
+ ?? .monospacedSystemFont(ofSize: Dimensions.FontSize.editor, weight: .regular)
+ }
+ return .monospacedSystemFont(ofSize: Dimensions.FontSize.editor, weight: .regular)
+ }
+ }
+ }
+}