diff --git a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFile.swift b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFile.swift index ac6acc55f..3e116900a 100644 --- a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFile.swift +++ b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFile.swift @@ -55,16 +55,16 @@ final class CEWorkspaceFile: Codable, Comparable, Hashable, Identifiable, Editor /// If the item already is the top-level ``CEWorkspaceFile`` this returns `nil`. var parent: CEWorkspaceFile? - private let fileDocumentSubject = PassthroughSubject() + private let fileDocumentSubject = PassthroughSubject() var fileDocument: CodeFileDocument? { didSet { - fileDocumentSubject.send() + fileDocumentSubject.send(fileDocument) } } /// Publisher for fileDocument property - var fileDocumentPublisher: AnyPublisher { + var fileDocumentPublisher: AnyPublisher { fileDocumentSubject.eraseToAnyPublisher() } diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift index 41aab954d..56c9f7dab 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowController.swift @@ -7,6 +7,7 @@ import Cocoa import SwiftUI +import Combine final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, ObservableObject { static let minSidebarWidth: CGFloat = 242 @@ -23,6 +24,8 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs var splitViewController: NSSplitViewController! + internal var cancellables = [AnyCancellable]() + init(window: NSWindow, workspace: WorkspaceDocument) { super.init(window: window) self.workspace = workspace @@ -47,6 +50,10 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs registerCommands() } + deinit { + cancellables.forEach({ $0.cancel() }) + } + @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") @@ -106,6 +113,7 @@ final class CodeEditWindowController: NSWindowController, NSToolbarDelegate, Obs splitVC.addSplitViewItem(inspector) self.splitViewController = splitVC + self.listenToDocumentEdited(workspace: workspace) } private func setupToolbar() { diff --git a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift index f5899054c..0bec375d5 100644 --- a/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift +++ b/CodeEdit/Features/Documents/Controllers/CodeEditWindowControllerExtensions.swift @@ -6,6 +6,7 @@ // import SwiftUI +import Combine extension CodeEditWindowController { @objc @@ -63,6 +64,53 @@ extension CodeEditWindowController { command: CommandClosureWrapper(closure: { self.toggleLastPanel() }) ) } + + // Listen to changes in all tabs/files + internal func listenToDocumentEdited(workspace: WorkspaceDocument) { + workspace.editorManager.$activeEditor + .flatMap({ editor in + editor.$tabs + }) + .compactMap({ tab in + Publishers.MergeMany(tab.elements.compactMap({ $0.fileDocumentPublisher })) + }) + .switchToLatest() + .compactMap({ fileDocument in + fileDocument?.isDocumentEditedPublisher + }) + .flatMap({ $0 }) + .sink { isDocumentEdited in + if isDocumentEdited { + self.setDocumentEdited(true) + return + } + + self.updateDocumentEdited(workspace: workspace) + } + .store(in: &cancellables) + + // Listen to change of tabs, if closed tab without saving content, + // we also need to recalculate isDocumentEdited + workspace.editorManager.$activeEditor + .flatMap({ editor in + editor.$tabs + }) + .sink { _ in + self.updateDocumentEdited(workspace: workspace) + } + .store(in: &cancellables) + } + + // Recalculate documentEdited by checking if any tab/file is edited + private func updateDocumentEdited(workspace: WorkspaceDocument) { + let hasEditedDocuments = !workspace + .editorManager + .editorLayout + .gatherOpenFiles() + .filter({ $0.fileDocument?.isDocumentEdited == true }) + .isEmpty + self.setDocumentEdited(hasEditedDocuments) + } } extension NSToolbarItem.Identifier {