aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Map.xcodeproj/project.pbxproj235
-rw-r--r--Map/Business/Template.swift36
-rw-r--r--Map/Data/MapDocument.swift6
-rw-r--r--Map/Data/UTType+wmap.swift21
-rw-r--r--Map/Data/UserPreferencesJSON.swift0
-rw-r--r--Map/Localizable.xcstrings22
-rw-r--r--Map/Presentation/Preferences/TemplatesPreferencesView.swift21
-rw-r--r--QuickLook/Base.lproj/PreviewViewController.xib21
-rw-r--r--QuickLook/Info.plist26
-rw-r--r--QuickLook/PreviewViewController.swift131
-rw-r--r--QuickLook/QuickLook.entitlements10
11 files changed, 500 insertions, 29 deletions
diff --git a/Map.xcodeproj/project.pbxproj b/Map.xcodeproj/project.pbxproj
index 77943e2..1a3a167 100644
--- a/Map.xcodeproj/project.pbxproj
+++ b/Map.xcodeproj/project.pbxproj
@@ -10,9 +10,20 @@
B5012E742C9625E200AC4D68 /* Patterns in Frameworks */ = {isa = PBXBuildFile; productRef = B5012E732C9625E200AC4D68 /* Patterns */; };
B5012E7F2C97315800AC4D68 /* ConcaveHull in Frameworks */ = {isa = PBXBuildFile; productRef = B5012E7E2C97315800AC4D68 /* ConcaveHull */; };
B51A45F52C982FAE00870ED5 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = B51A45F42C982FAE00870ED5 /* Sparkle */; };
+ B5282B052E210D4500C62006 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5282B042E210D4400C62006 /* Quartz.framework */; };
+ B5282B122E210D4500C62006 /* QuickLook.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = B5282B022E210D4400C62006 /* QuickLook.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ B5282B3D2E21109C00C62006 /* Patterns in Frameworks */ = {isa = PBXBuildFile; productRef = B5282B3C2E21109C00C62006 /* Patterns */; };
+ B5282B3F2E21109C00C62006 /* ConcaveHull in Frameworks */ = {isa = PBXBuildFile; productRef = B5282B3E2E21109C00C62006 /* ConcaveHull */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ B5282B102E210D4500C62006 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B54587042C961E9C0067B788 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = B5282B012E210D4400C62006;
+ remoteInfo = QuickLook;
+ };
B54587212C961E9E0067B788 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B54587042C961E9C0067B788 /* Project object */;
@@ -30,6 +41,27 @@
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
+ B5282B172E210D4500C62006 /* Embed Foundation Extensions */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ B5282B122E210D4500C62006 /* QuickLook.appex in Embed Foundation Extensions */,
+ );
+ name = "Embed Foundation Extensions";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ B5282B452E21123600C62006 /* Copy Fonts */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = Fonts;
+ dstSubfolderSpec = 7;
+ files = (
+ );
+ name = "Copy Fonts";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
B5DF1E3B2E1868D4003477B5 /* Copy Fonts */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -43,12 +75,59 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ B5282B022E210D4400C62006 /* QuickLook.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = QuickLook.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ B5282B042E210D4400C62006 /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; };
B545870C2C961E9C0067B788 /* Map.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Map.app; sourceTree = BUILT_PRODUCTS_DIR; };
B54587202C961E9E0067B788 /* MapTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MapTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B545872A2C961E9E0067B788 /* MapUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MapUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
+ B5282B162E210D4500C62006 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
+ isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
+ membershipExceptions = (
+ Info.plist,
+ );
+ target = B5282B012E210D4400C62006 /* QuickLook */;
+ };
+ B5282B1B2E210D9F00C62006 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
+ isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
+ membershipExceptions = (
+ Assets.xcassets,
+ Business/Constants.swift,
+ Business/CustomStage.swift,
+ Business/RenderableMap.swift,
+ Business/SmartLabelPositioning/SmartLabelPositioning.swift,
+ Business/Stage.swift,
+ Business/Template.swift,
+ Business/WmapParser/Entity.swift,
+ Business/WmapParser/Lexer.swift,
+ Business/WmapParser/Parser.swift,
+ Business/WmapParser/SourcePosition.swift,
+ Business/WmapParser/Syntax.swift,
+ Business/WmapParser/Token.swift,
+ Business/WmapParser/TokenType.swift,
+ Data/UserPreferences.swift,
+ "Data/UTType+wmap.swift",
+ Localizable.xcstrings,
+ "Presentation/Base Components/MapRender/MapAxes.swift",
+ "Presentation/Base Components/MapRender/MapBackground.swift",
+ "Presentation/Base Components/MapRender/MapEdges.swift",
+ "Presentation/Base Components/MapRender/MapGroup.swift",
+ "Presentation/Base Components/MapRender/MapGroups.swift",
+ "Presentation/Base Components/MapRender/MapIntertias.swift",
+ "Presentation/Base Components/MapRender/MapMask.swift",
+ "Presentation/Base Components/MapRender/MapNotes.swift",
+ "Presentation/Base Components/MapRender/MapOpportunities.swift",
+ "Presentation/Base Components/MapRender/MapStages.swift",
+ "Presentation/Base Components/MapRender/MapVertices.swift",
+ "Presentation/Complex Components/MapRender/MapRenderView.swift",
+ "Presentation/Theme/Color+theme.swift",
+ Presentation/Theme/Dimensions.swift,
+ "Presentation/Theme/Font+theme.swift",
+ );
+ target = B5282B012E210D4400C62006 /* QuickLook */;
+ };
B59830752E17E7C700F5FBF6 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
@@ -62,6 +141,15 @@
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */
+ B5282B492E21124E00C62006 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = {
+ isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet;
+ buildPhase = B5282B452E21123600C62006 /* Copy Fonts */;
+ membershipExceptions = (
+ "Presentation/Theme/Fonts/LibertinusSerif-Regular.otf",
+ "Presentation/Theme/Fonts/Ronzino-Medium.otf",
+ "Presentation/Theme/Fonts/Ronzino-Regular.otf",
+ );
+ };
B5DF1E3F2E1868E8003477B5 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = {
isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet;
buildPhase = B5DF1E3B2E1868D4003477B5 /* Copy Fonts */;
@@ -74,12 +162,23 @@
/* End PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
- B598304E2E17E7C700F5FBF6 /* Map */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (B59830752E17E7C700F5FBF6 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, B5DF1E3F2E1868E8003477B5 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Map; sourceTree = "<group>"; };
+ B5282B062E210D4500C62006 /* QuickLook */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (B5282B162E210D4500C62006 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = QuickLook; sourceTree = "<group>"; };
+ B598304E2E17E7C700F5FBF6 /* Map */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (B59830752E17E7C700F5FBF6 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, B5DF1E3F2E1868E8003477B5 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */, B5282B1B2E210D9F00C62006 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, B5282B492E21124E00C62006 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Map; sourceTree = "<group>"; };
B59830772E17E7CC00F5FBF6 /* MapTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = MapTests; sourceTree = "<group>"; };
B598307C2E17E7D600F5FBF6 /* MapUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = MapUITests; sourceTree = "<group>"; };
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
+ B5282AFF2E210D4400C62006 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B5282B052E210D4500C62006 /* Quartz.framework in Frameworks */,
+ B5282B3D2E21109C00C62006 /* Patterns in Frameworks */,
+ B5282B3F2E21109C00C62006 /* ConcaveHull in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
B54587092C961E9C0067B788 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -107,12 +206,22 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ B5282B032E210D4400C62006 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ B5282B042E210D4400C62006 /* Quartz.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
B54587032C961E9C0067B788 = {
isa = PBXGroup;
children = (
B598304E2E17E7C700F5FBF6 /* Map */,
B59830772E17E7CC00F5FBF6 /* MapTests */,
B598307C2E17E7D600F5FBF6 /* MapUITests */,
+ B5282B062E210D4500C62006 /* QuickLook */,
+ B5282B032E210D4400C62006 /* Frameworks */,
B545870D2C961E9C0067B788 /* Products */,
);
sourceTree = "<group>";
@@ -123,6 +232,7 @@
B545870C2C961E9C0067B788 /* Map.app */,
B54587202C961E9E0067B788 /* MapTests.xctest */,
B545872A2C961E9E0067B788 /* MapUITests.xctest */,
+ B5282B022E210D4400C62006 /* QuickLook.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -130,6 +240,31 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ B5282B012E210D4400C62006 /* QuickLook */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = B5282B132E210D4500C62006 /* Build configuration list for PBXNativeTarget "QuickLook" */;
+ buildPhases = (
+ B5282AFE2E210D4400C62006 /* Sources */,
+ B5282AFF2E210D4400C62006 /* Frameworks */,
+ B5282B002E210D4400C62006 /* Resources */,
+ B5282B452E21123600C62006 /* Copy Fonts */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ fileSystemSynchronizedGroups = (
+ B5282B062E210D4500C62006 /* QuickLook */,
+ );
+ name = QuickLook;
+ packageProductDependencies = (
+ B5282B3C2E21109C00C62006 /* Patterns */,
+ B5282B3E2E21109C00C62006 /* ConcaveHull */,
+ );
+ productName = QuickLook;
+ productReference = B5282B022E210D4400C62006 /* QuickLook.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
B545870B2C961E9C0067B788 /* Map */ = {
isa = PBXNativeTarget;
buildConfigurationList = B54587342C961E9E0067B788 /* Build configuration list for PBXNativeTarget "Map" */;
@@ -138,10 +273,12 @@
B54587092C961E9C0067B788 /* Frameworks */,
B545870A2C961E9C0067B788 /* Resources */,
B5DF1E3B2E1868D4003477B5 /* Copy Fonts */,
+ B5282B172E210D4500C62006 /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
+ B5282B112E210D4500C62006 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
B598304E2E17E7C700F5FBF6 /* Map */,
@@ -205,9 +342,12 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
- LastSwiftUpdateCheck = 1600;
+ LastSwiftUpdateCheck = 1640;
LastUpgradeCheck = 1640;
TargetAttributes = {
+ B5282B012E210D4400C62006 = {
+ CreatedOnToolsVersion = 16.4;
+ };
B545870B2C961E9C0067B788 = {
CreatedOnToolsVersion = 16.0;
};
@@ -242,11 +382,19 @@
B545870B2C961E9C0067B788 /* Map */,
B545871F2C961E9E0067B788 /* MapTests */,
B54587292C961E9E0067B788 /* MapUITests */,
+ B5282B012E210D4400C62006 /* QuickLook */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ B5282B002E210D4400C62006 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
B545870A2C961E9C0067B788 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -271,6 +419,13 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ B5282AFE2E210D4400C62006 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
B54587082C961E9C0067B788 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -295,6 +450,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ B5282B112E210D4500C62006 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = B5282B012E210D4400C62006 /* QuickLook */;
+ targetProxy = B5282B102E210D4500C62006 /* PBXContainerItemProxy */;
+ };
B54587222C961E9E0067B788 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B545870B2C961E9C0067B788 /* Map */;
@@ -308,6 +468,58 @@
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
+ B5282B142E210D4500C62006 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = QuickLook/QuickLook.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ ENABLE_HARDENED_RUNTIME = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = QuickLook/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = QuickLook;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ "@executable_path/../../../../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 15.5;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = systems.tranquil.Map.QuickLook;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ B5282B152E210D4500C62006 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_ENTITLEMENTS = QuickLook/QuickLook.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ ENABLE_HARDENED_RUNTIME = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = QuickLook/Info.plist;
+ INFOPLIST_KEY_CFBundleDisplayName = QuickLook;
+ INFOPLIST_KEY_NSHumanReadableCopyright = "";
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ "@executable_path/../../../../Frameworks",
+ );
+ MACOSX_DEPLOYMENT_TARGET = 15.5;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = systems.tranquil.Map.QuickLook;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
B54587322C961E9E0067B788 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -565,6 +777,15 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ B5282B132E210D4500C62006 /* Build configuration list for PBXNativeTarget "QuickLook" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B5282B142E210D4500C62006 /* Debug */,
+ B5282B152E210D4500C62006 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
B54587072C961E9C0067B788 /* Build configuration list for PBXProject "Map" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -646,6 +867,16 @@
package = B51A45F32C982FAE00870ED5 /* XCRemoteSwiftPackageReference "Sparkle" */;
productName = Sparkle;
};
+ B5282B3C2E21109C00C62006 /* Patterns */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = B5012E722C9625E200AC4D68 /* XCRemoteSwiftPackageReference "patterns" */;
+ productName = Patterns;
+ };
+ B5282B3E2E21109C00C62006 /* ConcaveHull */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = B5012E7D2C97315800AC4D68 /* XCRemoteSwiftPackageReference "ConcaveHull" */;
+ productName = ConcaveHull;
+ };
/* End XCSwiftPackageProductDependency section */
};
rootObject = B54587042C961E9C0067B788 /* Project object */;
diff --git a/Map/Business/Template.swift b/Map/Business/Template.swift
new file mode 100644
index 0000000..0648d07
--- /dev/null
+++ b/Map/Business/Template.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 Foundation
+
+struct Template: Identifiable, Codable, Hashable {
+ var id = UUID()
+ var name: String
+ var content: String
+ var isDefault: Bool = false
+
+ init(name: String, content: String, isDefault: Bool = false) {
+ self.name = name
+ self.content = content
+ self.isDefault = isDefault
+ }
+
+ func hash(into hasher: inout Hasher) {
+ hasher.combine(id)
+ }
+
+ static func == (lhs: Template, rhs: Template) -> Bool {
+ lhs.id == rhs.id
+ }
+}
diff --git a/Map/Data/MapDocument.swift b/Map/Data/MapDocument.swift
index b9ed33f..3c19ca1 100644
--- a/Map/Data/MapDocument.swift
+++ b/Map/Data/MapDocument.swift
@@ -15,12 +15,6 @@
import SwiftUI
import UniformTypeIdentifiers
-extension UTType {
- static var wmap: UTType {
- UTType(importedAs: "systems.tranquil.map.wmap")
- }
-}
-
struct MapDocument: FileDocument {
var text: String
diff --git a/Map/Data/UTType+wmap.swift b/Map/Data/UTType+wmap.swift
new file mode 100644
index 0000000..aa8b8fd
--- /dev/null
+++ b/Map/Data/UTType+wmap.swift
@@ -0,0 +1,21 @@
+// 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 UniformTypeIdentifiers
+
+extension UTType {
+ static var wmap: UTType {
+ UTType(importedAs: "systems.tranquil.map.wmap")
+ }
+}
diff --git a/Map/Data/UserPreferencesJSON.swift b/Map/Data/UserPreferencesJSON.swift
deleted file mode 100644
index e69de29..0000000
--- a/Map/Data/UserPreferencesJSON.swift
+++ /dev/null
diff --git a/Map/Localizable.xcstrings b/Map/Localizable.xcstrings
index ec8ed46..13932a4 100644
--- a/Map/Localizable.xcstrings
+++ b/Map/Localizable.xcstrings
@@ -988,6 +988,28 @@
}
}
},
+ "quick_look.empty_map.description" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "This .wmap file contains no map elements"
+ }
+ }
+ }
+ },
+ "quick_look.empty_map.title" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Empty Map"
+ }
+ }
+ }
+ },
"search_bar.done" : {
"extractionState" : "manual",
"localizations" : {
diff --git a/Map/Presentation/Preferences/TemplatesPreferencesView.swift b/Map/Presentation/Preferences/TemplatesPreferencesView.swift
index 375ed68..51d1c5e 100644
--- a/Map/Presentation/Preferences/TemplatesPreferencesView.swift
+++ b/Map/Presentation/Preferences/TemplatesPreferencesView.swift
@@ -15,27 +15,6 @@
import SwiftUI
-struct Template: Identifiable, Codable, Hashable {
- var id = UUID()
- var name: String
- var content: String
- var isDefault: Bool = false
-
- init(name: String, content: String, isDefault: Bool = false) {
- self.name = name
- self.content = content
- self.isDefault = isDefault
- }
-
- func hash(into hasher: inout Hasher) {
- hasher.combine(id)
- }
-
- static func == (lhs: Template, rhs: Template) -> Bool {
- lhs.id == rhs.id
- }
-}
-
struct TemplatesPreferencesView: View {
@AppStorage("mapTemplates") private var templatesData: Data = UserPreferences.Defaults
.mapTemplates
diff --git a/QuickLook/Base.lproj/PreviewViewController.xib b/QuickLook/Base.lproj/PreviewViewController.xib
new file mode 100644
index 0000000..d8dd0f3
--- /dev/null
+++ b/QuickLook/Base.lproj/PreviewViewController.xib
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+ <dependencies>
+ <deployment identifier="macosx"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="PreviewViewController" customModuleProvider="target">
+ <connections>
+ <outlet property="view" destination="c22-O7-iKe" id="NRM-P4-wb6"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <customView id="c22-O7-iKe" userLabel="Preview View">
+ <rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ </customView>
+ </objects>
+</document>
diff --git a/QuickLook/Info.plist b/QuickLook/Info.plist
new file mode 100644
index 0000000..38643ce
--- /dev/null
+++ b/QuickLook/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ATSApplicationFontsPath</key>
+ <string>Fonts</string>
+ <key>NSExtension</key>
+ <dict>
+ <key>NSExtensionAttributes</key>
+ <dict>
+ <key>QLIsDataBasedPreview</key>
+ <false/>
+ <key>QLSupportedContentTypes</key>
+ <array>
+ <string>systems.tranquil.map.wmap</string>
+ </array>
+ <key>QLSupportsSearchableItems</key>
+ <false/>
+ </dict>
+ <key>NSExtensionPointIdentifier</key>
+ <string>com.apple.quicklook.preview</string>
+ <key>NSExtensionPrincipalClass</key>
+ <string>$(PRODUCT_MODULE_NAME).PreviewViewController</string>
+ </dict>
+</dict>
+</plist>
diff --git a/QuickLook/PreviewViewController.swift b/QuickLook/PreviewViewController.swift
new file mode 100644
index 0000000..03efe72
--- /dev/null
+++ b/QuickLook/PreviewViewController.swift
@@ -0,0 +1,131 @@
+//
+// PreviewViewController.swift
+// QuickLook
+//
+// Created by Ruben Beltran del Rio on 2025-07-11.
+//
+
+import Cocoa
+import Quartz
+import SwiftUI
+
+class PreviewViewController: NSViewController, QLPreviewingController {
+
+ override var nibName: NSNib.Name? {
+ return NSNib.Name("PreviewViewController")
+ }
+
+ override func loadView() {
+ super.loadView()
+ }
+
+ func preparePreviewOfFile(at url: URL) async throws {
+ // Read the .wmap file content
+ let content = try String(contentsOf: url, encoding: .utf8)
+
+ // Parse the content to create a ParsedMap
+ let lexer = Wmap.Lexer(content)
+ let parser = Wmap.Parser(lexer: lexer)
+ let parsedMap = parser.parse()
+
+ await MainActor.run {
+ // Clear any existing subviews and force cleanup
+ view.subviews.forEach { subview in
+ if let hostingView = subview as? NSHostingView<AnyView> {
+ hostingView.rootView = AnyView(EmptyView())
+ }
+ subview.removeFromSuperview()
+ }
+
+ // Force memory cleanup
+ autoreleasepool {
+ // Create the map preview view with error handling
+ let mapPreview: AnyView
+ if parsedMap.entities.isEmpty {
+ // Fallback view for empty or invalid maps
+ mapPreview = AnyView(
+ VStack {
+ Image(systemName: "map")
+ .font(.system(size: 48))
+ .foregroundColor(.gray)
+ Text("quick_look.empty_map.title")
+ .font(.Theme.Title.emphasized)
+ .foregroundColor(.Theme.UI.foreground)
+ Text("quick_look.empty_map.description")
+ .font(.Theme.Body.regular)
+ .foregroundColor(.Theme.UI.foreground)
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .foregroundColor(.Theme.UI.background)
+ )
+ } else {
+ // Limit entities for memory efficiency in QuickLook
+ let limitedEntities = Array(parsedMap.entities.prefix(100)) // Limit to first 100 entities
+ let limitedMap = Wmap.ParsedMap(
+ entities: limitedEntities, vertexLabels: parsedMap.vertexLabels)
+ mapPreview = AnyView(MapPreviewView(parsedMap: limitedMap))
+ }
+
+ let hostingView = NSHostingView(rootView: mapPreview)
+
+ hostingView.translatesAutoresizingMaskIntoConstraints = false
+ view.addSubview(hostingView)
+
+ NSLayoutConstraint.activate([
+ hostingView.topAnchor.constraint(equalTo: view.topAnchor),
+ hostingView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+ hostingView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+ hostingView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
+ ])
+
+ view.needsLayout = true
+ view.layoutSubtreeIfNeeded()
+ } // autoreleasepool
+ }
+ }
+
+ deinit {
+ // Cleanup when view controller is deallocated
+ view.subviews.forEach { subview in
+ if let hostingView = subview as? NSHostingView<AnyView> {
+ hostingView.rootView = AnyView(EmptyView())
+ }
+ subview.removeFromSuperview()
+ }
+ }
+}
+
+// A SwiftUI view specifically for QuickLook preview
+struct MapPreviewView: View {
+ let parsedMap: Wmap.ParsedMap
+ @State private var selectedEvolution: StageType = .general
+
+ var body: some View {
+ GeometryReader { geometry in
+ let mapSize = Dimensions.Map.size
+ let padding = Dimensions.Map.padding
+ let totalWidth = mapSize.width + 2 * padding
+ let totalHeight = mapSize.height + 2 * padding
+
+ // Calculate scale to fit the available space
+ let scaleX = geometry.size.width / totalWidth
+ let scaleY = geometry.size.height / totalHeight
+ let scale = min(scaleX, scaleY, 1.0) // Don't scale up, only down
+
+ ScrollView([.horizontal, .vertical]) {
+ MapRenderView(
+ entities: parsedMap.entities,
+ evolution: $selectedEvolution
+ )
+ .drawingGroup(opaque: false)
+ .scaleEffect(scale)
+ .frame(
+ width: totalWidth * scale,
+ height: totalHeight * scale
+ )
+ }
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .background(Color.white)
+ }
+ }
+}
diff --git a/QuickLook/QuickLook.entitlements b/QuickLook/QuickLook.entitlements
new file mode 100644
index 0000000..f2ef3ae
--- /dev/null
+++ b/QuickLook/QuickLook.entitlements
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.app-sandbox</key>
+ <true/>
+ <key>com.apple.security.files.user-selected.read-only</key>
+ <true/>
+</dict>
+</plist>