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/PreviewViewController.swift | |
| parent | 8c74a32ef5aade1960b056950e52f2f3273db8c7 (diff) | |
Add QuickLook plugin
Diffstat (limited to 'QuickLook/PreviewViewController.swift')
| -rw-r--r-- | QuickLook/PreviewViewController.swift | 131 |
1 files changed, 131 insertions, 0 deletions
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) + } + } +} |