macos: comment my split view

This commit is contained in:
Mitchell Hashimoto
2023-03-07 17:04:12 -08:00
parent 4bbb419cb0
commit e07a4e6892
5 changed files with 43 additions and 26 deletions

View File

@ -17,7 +17,7 @@
A5B30535299BEAAA0047F10C /* GhosttyApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B30534299BEAAA0047F10C /* GhosttyApp.swift */; };
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDB29B8009000646FDA /* SplitView.swift */; };
A5CEAFDE29B8058B00646FDA /* SplitView.Splitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDD29B8058B00646FDA /* SplitView.Splitter.swift */; };
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */; };
A5D495A2299BEC7E00DD1313 /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */; };
/* End PBXBuildFile section */
@ -34,7 +34,7 @@
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
A5CEAFDB29B8009000646FDA /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.swift; sourceTree = "<group>"; };
A5CEAFDD29B8058B00646FDA /* SplitView.Splitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.Splitter.swift; sourceTree = "<group>"; };
A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.Divider.swift; sourceTree = "<group>"; };
A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = GhosttyKit.xcframework; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -98,7 +98,7 @@
isa = PBXGroup;
children = (
A5CEAFDB29B8009000646FDA /* SplitView.swift */,
A5CEAFDD29B8058B00646FDA /* SplitView.Splitter.swift */,
A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */,
);
path = SplitView;
sourceTree = "<group>";
@ -196,7 +196,7 @@
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
A5B30535299BEAAA0047F10C /* GhosttyApp.swift in Sources */,
A5CEAFDE29B8058B00646FDA /* SplitView.Splitter.swift in Sources */,
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -5,17 +5,17 @@ extension Ghostty {
/// 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 TerminalSplitView: View {
struct TerminalSplit: View {
@Environment(\.ghosttyApp) private var app
var body: some View {
if let app = app {
SplitViewChild(app)
TerminalSplitChild(app)
}
}
}
private struct SplitViewChild: View {
private struct TerminalSplitChild: View {
enum Direction {
case none
case vertical
@ -80,6 +80,7 @@ extension Ghostty {
assert(state.bottomRight != nil)
state.topLeft = state.bottomRight!
state.direction = .none
focusedSide = .TopLeft
}
func closeBottomRight() {
@ -87,6 +88,7 @@ extension Ghostty {
assert(state.bottomRight != nil)
state.bottomRight = nil
state.direction = .none
focusedSide = .TopLeft
}
var body: some View {
@ -109,10 +111,10 @@ extension Ghostty {
}
SplitView(.horizontal, left: {
SplitViewChild(app, topLeft: state.topLeft)
TerminalSplitChild(app, topLeft: state.topLeft)
.focused($focusedSide, equals: .TopLeft)
}, right: {
SplitViewChild(app, topLeft: state.bottomRight!)
TerminalSplitChild(app, topLeft: state.bottomRight!)
.focused($focusedSide, equals: .BottomRight)
})
}
@ -124,10 +126,10 @@ extension Ghostty {
}
SplitView(.vertical, left: {
SplitViewChild(app, topLeft: state.topLeft)
TerminalSplitChild(app, topLeft: state.topLeft)
.focused($focusedSide, equals: .TopLeft)
}, right: {
SplitViewChild(app, topLeft: state.bottomRight!)
TerminalSplitChild(app, topLeft: state.bottomRight!)
.focused($focusedSide, equals: .BottomRight)
})
}

View File

@ -21,7 +21,7 @@ struct GhosttyApp: App {
case .error:
ErrorView()
case .ready:
Ghostty.TerminalSplitView()
Ghostty.TerminalSplit()
.ghosttyApp(ghostty.app!)
}
}.commands {

View File

@ -1,7 +1,8 @@
import SwiftUI
extension SplitView {
struct Splitter: View {
/// The split divider that is rendered and can be used to resize a split view.
struct Divider: View {
let direction: Direction
let visibleSize: CGFloat
let invisibleSize: CGFloat

View File

@ -1,15 +1,26 @@
import SwiftUI
/// A split view shows a left and right (or top and bottom) view with a divider in the middle to do resizing.
/// The terminlogy "left" and "right" is always used but for vertical splits "left" is "top" and "right" is "bottom".
///
/// This view is purpose built for our use case and I imagine we'll continue to make it more configurable
/// as time goes on. For example, the splitter divider size and styling is all hardcoded.
struct SplitView<L: View, R: View>: View {
/// Direction of the split
let direction: Direction
/// The left and right views to render.
let left: L
let right: R
/// The current fractional width of the split view. 0.5 means L/R are equally sized, for example.
@State var split: CGFloat = 0.5
/// The visible size of the splitter, in points. The invisible size is a transparent hitbox that can still
/// be used for getting a resize handle. The total width/height of the splitter is the sum of both.
private let splitterVisibleSize: CGFloat = 1
private let splitterInvisibleSize: CGFloat = 6
@State var split: CGFloat = 0.5
var body: some View {
GeometryReader { geo in
let leftRect = self.leftRect(for: geo.size)
@ -23,14 +34,20 @@ struct SplitView<L: View, R: View>: View {
right
.frame(width: rightRect.size.width, height: rightRect.size.height)
.offset(x: rightRect.origin.x, y: rightRect.origin.y)
Splitter(direction: direction, visibleSize: splitterVisibleSize, invisibleSize: splitterInvisibleSize)
Divider(direction: direction, visibleSize: splitterVisibleSize, invisibleSize: splitterInvisibleSize)
.position(splitterPoint)
.gesture(dragGesture(geo.size, splitterPoint: splitterPoint))
}
}
}
func dragGesture(_ size: CGSize, splitterPoint: CGPoint) -> some Gesture {
init(_ direction: Direction, @ViewBuilder left: (() -> L), @ViewBuilder right: (() -> R)) {
self.direction = direction
self.left = left()
self.right = right()
}
private func dragGesture(_ size: CGSize, splitterPoint: CGPoint) -> some Gesture {
return DragGesture()
.onChanged { gesture in
let minSize: CGFloat = 10
@ -47,7 +64,8 @@ struct SplitView<L: View, R: View>: View {
}
}
func leftRect(for size: CGSize) -> CGRect {
/// Calculates the bounding rect for the left view.
private func leftRect(for size: CGSize) -> CGRect {
// Initially the rect is the full size
var result = CGRect(x: 0, y: 0, width: size.width, height: size.height)
switch (direction) {
@ -63,7 +81,8 @@ struct SplitView<L: View, R: View>: View {
return result
}
func rightRect(for size: CGSize, leftRect: CGRect) -> CGRect {
/// Calculates the bounding rect for the right view.
private func rightRect(for size: CGSize, leftRect: CGRect) -> CGRect {
// Initially the rect is the full size
var result = CGRect(x: 0, y: 0, width: size.width, height: size.height)
switch (direction) {
@ -83,7 +102,8 @@ struct SplitView<L: View, R: View>: View {
return result
}
func splitterPoint(for size: CGSize, leftRect: CGRect) -> CGPoint {
/// Calculates the point at which the splitter should be rendered.
private func splitterPoint(for size: CGSize, leftRect: CGRect) -> CGPoint {
switch (direction) {
case .horizontal:
return CGPoint(x: leftRect.size.width, y: size.height / 2)
@ -92,12 +112,6 @@ struct SplitView<L: View, R: View>: View {
return CGPoint(x: size.width / 2, y: leftRect.size.height)
}
}
init(_ direction: Direction, @ViewBuilder left: (() -> L), @ViewBuilder right: (() -> R)) {
self.direction = direction
self.left = left()
self.right = right()
}
}
extension SplitView {