From 2e4bec8d3a66af2cf927a3ce0c2b60d93a9cf509 Mon Sep 17 00:00:00 2001 From: Paul Ebose <49006567+plbstl@users.noreply.github.com> Date: Wed, 21 Aug 2024 05:49:38 +0100 Subject: [PATCH] Correctly view non-text files in single file window and Open Quickly (#1820) * add `updateStatusBarInfo` view modifier * fix QuickLook previews in single file window * change `fileURL` type to `NSURL` in `AnyFileView` * fix QuickLook previews in Open Quickly Use `dismantleNSView` in `AnyFileView` to prevent crashes when using Open Quickly. What crashes? 1. When using Open Quickly without the workaround in `AnyFileView`, search for a media file (.png, .gif, .mp4, etc) 2. Press escape to close Open Quickly 3. re-open Open Quickly. The media preview is gone 4. Attempt to navigate upwards or downwards using the arrow buttons 5. A crash occurs (QuickLook is attempting to reuse an already closed preview) * add assertionFailure in `CodeEditWindowController.openQuickly` for when an editorManager or statusBarViewModel is unavailable * make `UpdateStatusBarInfo.fileURL` an optional * remove the need to pass environment objects to `NonTextFileView` This also prevents Open Quickly previews from updating the status bar * rename function parameters in `UpdateStatusBarInfo` view modifier * remove `updateStatusBarInfo` view extension * remove guard in `CodeEditWindowController.openQuickly` editorManager and statusBarViewModel environment objects are not used in Open Quickly anymore * merge #1822 fix into this branch allow files with a utType that is neither image nor PDF to be previewed (#1822) --- .../Features/Editor/Views/AnyFileView.swift | 15 +++++-- .../Features/Editor/Views/CodeFileView.swift | 4 -- .../Editor/Views/EditorAreaFileView.swift | 6 +++ .../Editor/Views/NonTextFileView.swift | 13 +----- .../Editor/Views/WindowCodeFileView.swift | 17 ++------ .../Views/OpenQuicklyPreviewView.swift | 2 +- .../ViewModifiers/UpdateStatusBarInfo.swift | 42 +++++++++++-------- 7 files changed, 48 insertions(+), 51 deletions(-) diff --git a/CodeEdit/Features/Editor/Views/AnyFileView.swift b/CodeEdit/Features/Editor/Views/AnyFileView.swift index 9851fa6af..db58b7274 100644 --- a/CodeEdit/Features/Editor/Views/AnyFileView.swift +++ b/CodeEdit/Features/Editor/Views/AnyFileView.swift @@ -17,20 +17,27 @@ import QuickLookUI struct AnyFileView: NSViewRepresentable { /// URL of the file to preview. You can pass in any file type. - private let fileURL: URL + private let fileURL: NSURL init(_ fileURL: URL) { - self.fileURL = fileURL + self.fileURL = fileURL as NSURL } func makeNSView(context: Context) -> QLPreviewView { let qlPreviewView = QLPreviewView() - qlPreviewView.previewItem = fileURL as any QLPreviewItem + qlPreviewView.previewItem = fileURL + qlPreviewView.shouldCloseWithWindow = false // Temp work around for something more reasonable. return qlPreviewView } func updateNSView(_ qlPreviewView: QLPreviewView, context: Context) { - qlPreviewView.previewItem = fileURL as any QLPreviewItem + qlPreviewView.previewItem = fileURL } + // Temp work around for something more reasonable. + // Open quickly should empty the results (but cache the query) when closed, + // and then re-search or recompute the results when re-opened. + static func dismantleNSView(_ qlPreviewView: QLPreviewView, coordinator: ()) { + qlPreviewView.close() + } } diff --git a/CodeEdit/Features/Editor/Views/CodeFileView.swift b/CodeEdit/Features/Editor/Views/CodeFileView.swift index f485f277e..7e9054f14 100644 --- a/CodeEdit/Features/Editor/Views/CodeFileView.swift +++ b/CodeEdit/Features/Editor/Views/CodeFileView.swift @@ -46,8 +46,6 @@ struct CodeFileView: View { @Environment(\.colorScheme) private var colorScheme - @EnvironmentObject private var editorManager: EditorManager - @ObservedObject private var themeModel: ThemeModel = .shared private var cancellables = Set() @@ -105,8 +103,6 @@ struct CodeFileView: View { @Environment(\.edgeInsets) private var edgeInsets - @EnvironmentObject private var editor: Editor - var body: some View { CodeEditSourceEditor( codeFile.content ?? NSTextStorage(), diff --git a/CodeEdit/Features/Editor/Views/EditorAreaFileView.swift b/CodeEdit/Features/Editor/Views/EditorAreaFileView.swift index 18dad183f..7189a7968 100644 --- a/CodeEdit/Features/Editor/Views/EditorAreaFileView.swift +++ b/CodeEdit/Features/Editor/Views/EditorAreaFileView.swift @@ -14,6 +14,7 @@ struct EditorAreaFileView: View { @EnvironmentObject private var editorManager: EditorManager @EnvironmentObject private var editor: Editor + @EnvironmentObject private var statusBarViewModel: StatusBarViewModel @Environment(\.edgeInsets) private var edgeInsets @@ -32,6 +33,11 @@ struct EditorAreaFileView: View { NonTextFileView(fileDocument: document) .padding(.top, edgeInsets.top - 1.74) // Use the magic number to fine-tune its appearance. .padding(.bottom, StatusBarView.height + 1.26) // Use the magic number to fine-tune its appearance. + .modifier(UpdateStatusBarInfo(with: document.fileURL)) + .onDisappear { + statusBarViewModel.dimensions = nil + statusBarViewModel.fileSize = nil + } } } else { diff --git a/CodeEdit/Features/Editor/Views/NonTextFileView.swift b/CodeEdit/Features/Editor/Views/NonTextFileView.swift index b64302897..36ca0cf5e 100644 --- a/CodeEdit/Features/Editor/Views/NonTextFileView.swift +++ b/CodeEdit/Features/Editor/Views/NonTextFileView.swift @@ -17,27 +17,20 @@ struct NonTextFileView: View { /// The file document you wish to open. let fileDocument: CodeFileDocument - @EnvironmentObject private var editorManager: EditorManager - @EnvironmentObject private var statusBarViewModel: StatusBarViewModel - var body: some View { Group { if let fileURL = fileDocument.fileURL { - if let utType = fileDocument.utType { + if let utType = fileDocument.utType { if utType.conforms(to: .image) { ImageFileView(fileURL) - .modifier(UpdateStatusBarInfo(withURL: fileURL)) } else if utType.conforms(to: .pdf) { PDFFileView(fileURL) - .modifier(UpdateStatusBarInfo(withURL: fileURL)) } else { AnyFileView(fileURL) - .modifier(UpdateStatusBarInfo(withURL: fileURL)) } } else { AnyFileView(fileURL) - .modifier(UpdateStatusBarInfo(withURL: fileURL)) } } else { ZStack { @@ -45,9 +38,5 @@ struct NonTextFileView: View { } } } - .onDisappear { - statusBarViewModel.dimensions = nil - statusBarViewModel.fileSize = nil - } } } diff --git a/CodeEdit/Features/Editor/Views/WindowCodeFileView.swift b/CodeEdit/Features/Editor/Views/WindowCodeFileView.swift index a3edb056f..3bc2a16f8 100644 --- a/CodeEdit/Features/Editor/Views/WindowCodeFileView.swift +++ b/CodeEdit/Features/Editor/Views/WindowCodeFileView.swift @@ -13,20 +13,11 @@ import SwiftUI struct WindowCodeFileView: View { var codeFile: CodeFileDocument - @State var hasAppeared = false - @FocusState var focused: Bool - var body: some View { - Group { - if !hasAppeared { - Color.clear.onAppear { - hasAppeared = true - focused = true - } - } else { - CodeFileView(codeFile: codeFile) - .focused($focused) - } + if let utType = codeFile.utType, utType.conforms(to: .text) { + CodeFileView(codeFile: codeFile) + } else { + NonTextFileView(fileDocument: codeFile) } } } diff --git a/CodeEdit/Features/OpenQuickly/Views/OpenQuicklyPreviewView.swift b/CodeEdit/Features/OpenQuickly/Views/OpenQuicklyPreviewView.swift index 78edae4d3..920bdf2c4 100644 --- a/CodeEdit/Features/OpenQuickly/Views/OpenQuicklyPreviewView.swift +++ b/CodeEdit/Features/OpenQuickly/Views/OpenQuicklyPreviewView.swift @@ -19,7 +19,7 @@ struct OpenQuicklyPreviewView: View { let doc = try? CodeFileDocument( for: item.url, withContentsOf: item.url, - ofType: "public.source-code" + ofType: item.contentType?.identifier ?? "public.source-code" ) self._document = .init(wrappedValue: doc ?? .init()) } diff --git a/CodeEdit/Features/StatusBar/ViewModifiers/UpdateStatusBarInfo.swift b/CodeEdit/Features/StatusBar/ViewModifiers/UpdateStatusBarInfo.swift index e5385074d..97f299bcc 100644 --- a/CodeEdit/Features/StatusBar/ViewModifiers/UpdateStatusBarInfo.swift +++ b/CodeEdit/Features/StatusBar/ViewModifiers/UpdateStatusBarInfo.swift @@ -15,7 +15,11 @@ import SwiftUI struct UpdateStatusBarInfo: ViewModifier { /// The URL of the file to compute information from. - let withURL: URL + let fileURL: URL? + + init(with fileURL: URL?) { + self.fileURL = fileURL + } @EnvironmentObject private var editorManager: EditorManager @EnvironmentObject private var statusBarViewModel: StatusBarViewModel @@ -27,17 +31,17 @@ struct UpdateStatusBarInfo: ViewModifier { } /// Compute information that can be used to update properties in ``StatusBarFileInfoView``. - /// - Parameter url: URL of the file to compute information from. + /// - Parameter with fileURL: URL of the file to compute information from. /// - Returns: The file size and its image dimensions (if any). - private func computeStatusBarInfo(url: URL) -> ComputedStatusBarInfo? { - guard let resourceValues = try? url.resourceValues(forKeys: [.contentTypeKey, .fileSizeKey]), + private func computeStatusBarInfo(with fileURL: URL) -> ComputedStatusBarInfo? { + guard let resourceValues = try? fileURL.resourceValues(forKeys: [.contentTypeKey, .fileSizeKey]), let contentType = resourceValues.contentType, let fileSize = resourceValues.fileSize else { return nil } - if contentType.conforms(to: .image), let imageReps = NSImage(contentsOf: url)?.representations.first { + if contentType.conforms(to: .image), let imageReps = NSImage(contentsOf: fileURL)?.representations.first { let dimensions = ImageDimensions(width: imageReps.pixelsWide, height: imageReps.pixelsHigh) return ComputedStatusBarInfo(fileSize: fileSize, dimensions: dimensions) } else { // non-image file @@ -46,18 +50,22 @@ struct UpdateStatusBarInfo: ViewModifier { } func body(content: Content) -> some View { - content - .onAppear { - let statusBarInfo = computeStatusBarInfo(url: withURL) - statusBarViewModel.fileSize = statusBarInfo?.fileSize - statusBarViewModel.dimensions = statusBarInfo?.dimensions - } - .onChange(of: editorManager.activeEditor.selectedTab) { newTab in - guard let newTab else { return } - let statusBarInfo = computeStatusBarInfo(url: newTab.file.url) - statusBarViewModel.fileSize = statusBarInfo?.fileSize - statusBarViewModel.dimensions = statusBarInfo?.dimensions - } + if let fileURL { + content + .onAppear { + let statusBarInfo = computeStatusBarInfo(with: fileURL) + statusBarViewModel.fileSize = statusBarInfo?.fileSize + statusBarViewModel.dimensions = statusBarInfo?.dimensions + } + .onChange(of: editorManager.activeEditor.selectedTab) { newTab in + guard let newTab else { return } + let statusBarInfo = computeStatusBarInfo(with: newTab.file.url) + statusBarViewModel.fileSize = statusBarInfo?.fileSize + statusBarViewModel.dimensions = statusBarInfo?.dimensions + } + } else { + content + } } }