diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index 376e05276..2789bb4c4 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -3,6 +3,7 @@ import Cocoa import SwiftUI import GhosttyKit +/// The terminal controller is an NSWindowController that maps 1:1 to a terminal window. class TerminalController: NSWindowController, NSWindowDelegate, TerminalViewDelegate, TerminalViewModel { override var windowNibName: NSNib.Name? { "Terminal" } diff --git a/macos/Sources/Features/Terminal/TerminalManager.swift b/macos/Sources/Features/Terminal/TerminalManager.swift index 0613abcb9..34acc0ec5 100644 --- a/macos/Sources/Features/Terminal/TerminalManager.swift +++ b/macos/Sources/Features/Terminal/TerminalManager.swift @@ -3,7 +3,8 @@ import SwiftUI import GhosttyKit import Combine -/// Manages a set of terminal windows. +/// Manages a set of terminal windows. This is effectively an array of TerminalControllers. +/// This abstraction helps manage tabs and multi-window scenarios. class TerminalManager { struct Window { let controller: TerminalController @@ -48,15 +49,8 @@ class TerminalManager { } deinit { - let center = NotificationCenter.default; - center.removeObserver( - self, - name: Ghostty.Notification.ghosttyNewTab, - object: nil) - center.removeObserver( - self, - name: Ghostty.Notification.ghosttyNewWindow, - object: nil) + let center = NotificationCenter.default + center.removeObserver(self) } // MARK: - Window Management diff --git a/macos/Sources/Features/Terminal/TerminalView.swift b/macos/Sources/Features/Terminal/TerminalView.swift index 163d4e575..d3b1a66dc 100644 --- a/macos/Sources/Features/Terminal/TerminalView.swift +++ b/macos/Sources/Features/Terminal/TerminalView.swift @@ -1,6 +1,9 @@ import SwiftUI import GhosttyKit +/// This delegate is notified of actions and property changes regarding the terminal view. This +/// delegate is optional and can be used by a TerminalView caller to react to changes such as +/// titles being set, cell sizes being changed, etc. protocol TerminalViewDelegate: AnyObject, ObservableObject { /// Called when the currently focused surface changed. This can be nil. func focusedSurfaceDidChange(to: Ghostty.SurfaceView?) @@ -12,16 +15,23 @@ protocol TerminalViewDelegate: AnyObject, ObservableObject { func cellSizeDidChange(to: NSSize) } -protocol TerminalViewModel: ObservableObject { - var surfaceTree: Ghostty.SplitNode? { get set } -} - +// Default all the functions so they're optional extension TerminalViewDelegate { func focusedSurfaceDidChange(to: Ghostty.SurfaceView?) {} func titleDidChange(to: String) {} func cellSizeDidChange(to: NSSize) {} } +/// The view model is a required implementation for TerminalView callers. This contains +/// the main state between the TerminalView caller and SwiftUI. This abstraction is what +/// allows AppKit to own most of the data in SwiftUI. +protocol TerminalViewModel: ObservableObject { + /// The tree of terminal surfaces (splits) within the view. This is mutated by TerminalView + /// and children. This should be @Published. + var surfaceTree: Ghostty.SplitNode? { get set } +} + +/// The main terminal view. This terminal view supports splits. struct TerminalView: View { @ObservedObject var ghostty: Ghostty.AppState