diff options
| author | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-07-07 16:52:28 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-07-07 16:52:28 +0200 |
| commit | e59dcf2bcf7841ebe6f3d85ec3ec03319b261da4 (patch) | |
| tree | 3311ea9e5fbc9f26fe1b170318c9b179faf42dff /Map | |
| parent | 3bacbf6ac85330d35493953d7296e5ba420b7750 (diff) | |
More localization keys, font editor preferences, better new file
Diffstat (limited to 'Map')
| -rw-r--r-- | Map/Data/MapDocument.swift | 18 | ||||
| -rw-r--r-- | Map/Localizable.xcstrings | 86 | ||||
| -rw-r--r-- | Map/Presentation/Base Components/MapTextEditor.swift | 2 | ||||
| -rw-r--r-- | Map/Presentation/Commands/MapCommands.swift | 23 | ||||
| -rw-r--r-- | Map/Presentation/MapEditor.swift | 4 | ||||
| -rw-r--r-- | Map/Presentation/Preferences/AppearancePreferencesView.swift | 36 | ||||
| -rw-r--r-- | Map/Presentation/Preferences/EditorPreferencesView.swift | 120 | ||||
| -rw-r--r-- | Map/Presentation/Preferences/MapPreferencesView.swift | 23 | ||||
| -rw-r--r-- | Map/Presentation/PreferencesWindow.swift | 14 | ||||
| -rw-r--r-- | Map/Presentation/Theme/Dimensions.swift | 11 | ||||
| -rw-r--r-- | Map/Presentation/Theme/NSFont+theme.swift | 36 |
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) + } + } + } +} |