macos: rename TerminalSurfaceView to TerminalSurface

This commit is contained in:
Mitchell Hashimoto
2023-03-05 13:18:19 -08:00
parent b1a7baf394
commit b1d57cd500
4 changed files with 75 additions and 16 deletions

View File

@ -7,7 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
A507573E299FF33C009D7DC7 /* TerminalSurfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A507573D299FF33C009D7DC7 /* TerminalSurfaceView.swift */; }; A507573E299FF33C009D7DC7 /* TerminalSurface.swift in Sources */ = {isa = PBXBuildFile; fileRef = A507573D299FF33C009D7DC7 /* TerminalSurface.swift */; };
A518502429A197C700E4CC4F /* TerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518502329A197C700E4CC4F /* TerminalView.swift */; }; A518502429A197C700E4CC4F /* TerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518502329A197C700E4CC4F /* TerminalView.swift */; };
A518502629A1A45100E4CC4F /* WindowTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518502529A1A45100E4CC4F /* WindowTracker.swift */; }; A518502629A1A45100E4CC4F /* WindowTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A518502529A1A45100E4CC4F /* WindowTracker.swift */; };
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; }; A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A535B9D9299C569B0017E2E4 /* ErrorView.swift */; };
@ -19,7 +19,7 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
A507573D299FF33C009D7DC7 /* TerminalSurfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSurfaceView.swift; sourceTree = "<group>"; }; A507573D299FF33C009D7DC7 /* TerminalSurface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalSurface.swift; sourceTree = "<group>"; };
A518502329A197C700E4CC4F /* TerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalView.swift; sourceTree = "<group>"; }; A518502329A197C700E4CC4F /* TerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalView.swift; sourceTree = "<group>"; };
A518502529A1A45100E4CC4F /* WindowTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTracker.swift; sourceTree = "<group>"; }; A518502529A1A45100E4CC4F /* WindowTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowTracker.swift; sourceTree = "<group>"; };
A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; }; A535B9D9299C569B0017E2E4 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = "<group>"; };
@ -53,7 +53,7 @@
A55685DF29A03A9F004303CE /* AppError.swift */, A55685DF29A03A9F004303CE /* AppError.swift */,
A59444F629A2ED5200725BBA /* SettingsView.swift */, A59444F629A2ED5200725BBA /* SettingsView.swift */,
A518502329A197C700E4CC4F /* TerminalView.swift */, A518502329A197C700E4CC4F /* TerminalView.swift */,
A507573D299FF33C009D7DC7 /* TerminalSurfaceView.swift */, A507573D299FF33C009D7DC7 /* TerminalSurface.swift */,
A518502529A1A45100E4CC4F /* WindowTracker.swift */, A518502529A1A45100E4CC4F /* WindowTracker.swift */,
); );
path = Sources; path = Sources;
@ -166,7 +166,7 @@
A518502629A1A45100E4CC4F /* WindowTracker.swift in Sources */, A518502629A1A45100E4CC4F /* WindowTracker.swift in Sources */,
A518502429A197C700E4CC4F /* TerminalView.swift in Sources */, A518502429A197C700E4CC4F /* TerminalView.swift in Sources */,
A55685E029A03A9F004303CE /* AppError.swift in Sources */, A55685E029A03A9F004303CE /* AppError.swift in Sources */,
A507573E299FF33C009D7DC7 /* TerminalSurfaceView.swift in Sources */, A507573E299FF33C009D7DC7 /* TerminalSurface.swift in Sources */,
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */, A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
A5B30535299BEAAA0047F10C /* GhosttyApp.swift in Sources */, A5B30535299BEAAA0047F10C /* GhosttyApp.swift in Sources */,
); );

View File

@ -21,7 +21,7 @@ struct GhosttyApp: App {
case .error: case .error:
ErrorView() ErrorView()
case .ready: case .ready:
TerminalView(app: ghostty.app!) TerminalSplittableView(app: ghostty.app!)
.modifier(WindowObservationModifier()) .modifier(WindowObservationModifier())
} }
}.commands { }.commands {
@ -160,7 +160,7 @@ class GhosttyState: ObservableObject {
} }
static func setTitle(_ userdata: UnsafeMutableRawPointer?, title: UnsafePointer<CChar>?) { static func setTitle(_ userdata: UnsafeMutableRawPointer?, title: UnsafePointer<CChar>?) {
let surfaceView = Unmanaged<TerminalSurfaceView_Real>.fromOpaque(userdata!).takeUnretainedValue() let surfaceView = Unmanaged<TerminalSurfaceView>.fromOpaque(userdata!).takeUnretainedValue()
guard let titleStr = String(cString: title!, encoding: .utf8) else { return } guard let titleStr = String(cString: title!, encoding: .utf8) else { return }
DispatchQueue.main.async { DispatchQueue.main.async {
surfaceView.title = titleStr surfaceView.title = titleStr
@ -169,7 +169,7 @@ class GhosttyState: ObservableObject {
/// Returns the GhosttyState from the given userdata value. /// Returns the GhosttyState from the given userdata value.
static func appState(fromSurface userdata: UnsafeMutableRawPointer?) -> GhosttyState? { static func appState(fromSurface userdata: UnsafeMutableRawPointer?) -> GhosttyState? {
let surfaceView = Unmanaged<TerminalSurfaceView_Real>.fromOpaque(userdata!).takeUnretainedValue() let surfaceView = Unmanaged<TerminalSurfaceView>.fromOpaque(userdata!).takeUnretainedValue()
guard let surface = surfaceView.surface else { return nil } guard let surface = surfaceView.surface else { return nil }
guard let app = ghostty_surface_app(surface) else { return nil } guard let app = ghostty_surface_app(surface) else { return nil }
guard let app_ud = ghostty_app_userdata(app) else { return nil } guard let app_ud = ghostty_app_userdata(app) else { return nil }

View File

@ -9,7 +9,7 @@ import GhosttyKit
/// We just wrap an AppKit NSView here at the moment so that we can behave as low level as possible /// We just wrap an AppKit NSView here at the moment so that we can behave as low level as possible
/// since that is what the Metal renderer in Ghostty expects. In the future, it may make more sense to /// since that is what the Metal renderer in Ghostty expects. In the future, it may make more sense to
/// wrap an MTKView and use that, but for legacy reasons we didn't do that to begin with. /// wrap an MTKView and use that, but for legacy reasons we didn't do that to begin with.
struct TerminalSurfaceView: NSViewRepresentable { struct TerminalSurface: NSViewRepresentable {
static let logger = Logger( static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier!, subsystem: Bundle.main.bundleIdentifier!,
category: String(describing: TerminalSurfaceView.self) category: String(describing: TerminalSurfaceView.self)
@ -34,16 +34,16 @@ struct TerminalSurfaceView: NSViewRepresentable {
/// set the appropriate title of the window/tab/split/etc. if they care. /// set the appropriate title of the window/tab/split/etc. if they care.
@Binding var title: String @Binding var title: String
@StateObject private var state: TerminalSurfaceView_Real @StateObject private var state: TerminalSurfaceView
init(_ app: ghostty_app_t, hasFocus: Bool, size: CGSize, title: Binding<String>) { init(_ app: ghostty_app_t, hasFocus: Bool, size: CGSize, title: Binding<String>) {
self._state = StateObject(wrappedValue: TerminalSurfaceView_Real(app)) self._state = StateObject(wrappedValue: TerminalSurfaceView(app))
self._title = title self._title = title
self.hasFocus = hasFocus self.hasFocus = hasFocus
self.size = size self.size = size
} }
func makeNSView(context: Context) -> TerminalSurfaceView_Real { func makeNSView(context: Context) -> TerminalSurfaceView {
// We need the view as part of the state to be created previously because // We need the view as part of the state to be created previously because
// the view is sent to the Ghostty API so that it can manipulate it // the view is sent to the Ghostty API so that it can manipulate it
// directly since we draw on a render thread. // directly since we draw on a render thread.
@ -51,7 +51,7 @@ struct TerminalSurfaceView: NSViewRepresentable {
return state; return state;
} }
func updateNSView(_ view: TerminalSurfaceView_Real, context: Context) { func updateNSView(_ view: TerminalSurfaceView, context: Context) {
state.delegate = context.coordinator state.delegate = context.coordinator
state.focusDidChange(hasFocus) state.focusDidChange(hasFocus)
state.sizeDidChange(size) state.sizeDidChange(size)
@ -62,9 +62,9 @@ struct TerminalSurfaceView: NSViewRepresentable {
} }
class Coordinator : TerminalSurfaceDelegate { class Coordinator : TerminalSurfaceDelegate {
let view: TerminalSurfaceView let view: TerminalSurface
init(_ view: TerminalSurfaceView) { init(_ view: TerminalSurface) {
self.view = view self.view = view
} }
@ -80,7 +80,7 @@ protocol TerminalSurfaceDelegate: AnyObject {
} }
/// The actual NSView implementation for the terminal surface. /// The actual NSView implementation for the terminal surface.
class TerminalSurfaceView_Real: NSView, NSTextInputClient, ObservableObject { class TerminalSurfaceView: NSView, NSTextInputClient, ObservableObject {
weak var delegate: TerminalSurfaceDelegate? weak var delegate: TerminalSurfaceDelegate?
// The current title of the surface as defined by the pty. This can be // The current title of the surface as defined by the pty. This can be

View File

@ -16,9 +16,68 @@ struct TerminalView: View {
// is up to date. See TerminalSurfaceView for why we don't use the NSView // is up to date. See TerminalSurfaceView for why we don't use the NSView
// resize callback. // resize callback.
GeometryReader { geo in GeometryReader { geo in
TerminalSurfaceView(app, hasFocus: hasFocus, size: geo.size, title: $title) TerminalSurface(app, hasFocus: hasFocus, size: geo.size, title: $title)
.focused($surfaceFocus) .focused($surfaceFocus)
.navigationTitle(title) .navigationTitle(title)
} }
} }
} }
/// A spittable terminal view is one where the terminal allows for "splits" (vertical and horizontal) within the
/// view. The terminal starts in the unsplit state (a plain ol' TerminalView) but responds to changes to the
/// split direction by splitting the terminal.
struct TerminalSplittableView: View {
enum Direction {
case none
case vertical
case horizontal
}
enum Side: Hashable {
case TopLeft
case BottomRight
}
@FocusState private var focusedSide: Side?
let app: ghostty_app_t
/// Direction of the current split. If this is "nil" then the terminal is not currently split at all.
@State var splitDirection: Direction = .none
var body: some View {
switch (splitDirection) {
case .none:
VStack {
HStack {
Button("Split Horizontal") { splitDirection = .horizontal }
Button("Split Vertical") { splitDirection = .vertical }
}
TerminalView(app: app)
.focused($focusedSide, equals: .TopLeft)
}
case .horizontal:
VStack {
HStack {
Button("Close Left") { splitDirection = .none }
Button("Close Right") { splitDirection = .none }
}
HSplitView {
TerminalSplittableView(app: app)
.focused($focusedSide, equals: .TopLeft)
TerminalSplittableView(app: app)
.focused($focusedSide, equals: .BottomRight)
}
}
case .vertical:
VSplitView {
TerminalSplittableView(app: app)
.focused($focusedSide, equals: .TopLeft)
TerminalSplittableView(app: app)
.focused($focusedSide, equals: .BottomRight)
}
}
}
}