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 /QuickLook | |
| parent | 8c74a32ef5aade1960b056950e52f2f3273db8c7 (diff) | |
Add QuickLook plugin
Diffstat (limited to 'QuickLook')
| -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 |
4 files changed, 188 insertions, 0 deletions
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> |