mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: comment my split view
This commit is contained in:
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ struct GhosttyApp: App {
|
||||
case .error:
|
||||
ErrorView()
|
||||
case .ready:
|
||||
Ghostty.TerminalSplitView()
|
||||
Ghostty.TerminalSplit()
|
||||
.ghosttyApp(ghostty.app!)
|
||||
}
|
||||
}.commands {
|
||||
|
@ -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
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user