diff options
| author | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-07-11 12:06:04 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-07-11 12:06:04 +0200 |
| commit | 0cc8ac9cc39ce3a1bcc9ea88345d18f17a46cb2a (patch) | |
| tree | 97b0f4e6a38040e73412a0981c43c29b473abcd9 | |
| parent | 8c74a32ef5aade1960b056950e52f2f3273db8c7 (diff) | |
Add QuickLook plugin
| -rw-r--r-- | Map.xcodeproj/project.pbxproj | 235 | ||||
| -rw-r--r-- | Map/Business/Template.swift | 36 | ||||
| -rw-r--r-- | Map/Data/MapDocument.swift | 6 | ||||
| -rw-r--r-- | Map/Data/UTType+wmap.swift | 21 | ||||
| -rw-r--r-- | Map/Data/UserPreferencesJSON.swift | 0 | ||||
| -rw-r--r-- | Map/Localizable.xcstrings | 22 | ||||
| -rw-r--r-- | Map/Presentation/Preferences/TemplatesPreferencesView.swift | 21 | ||||
| -rw-r--r-- | QuickLook/Base.lproj/PreviewViewController.xib | 21 | ||||
| -rw-r--r-- | QuickLook/Info.plist | 26 | ||||
| -rw-r--r-- | QuickLook/PreviewViewController.swift | 131 | ||||
| -rw-r--r-- | QuickLook/QuickLook.entitlements | 10 |
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> |