mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
macos: working on custom split view
This commit is contained in:
@ -12,10 +12,12 @@
|
||||
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB529B6F47F0055DE60 /* AppState.swift */; };
|
||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */; };
|
||||
A55B7BBE29B701360055DE60 /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BBD29B701360055DE60 /* SplitView.swift */; };
|
||||
A55B7BBE29B701360055DE60 /* Ghostty.SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BBD29B701360055DE60 /* Ghostty.SplitView.swift */; };
|
||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59444F629A2ED5200725BBA /* SettingsView.swift */; };
|
||||
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 */; };
|
||||
A5D495A2299BEC7E00DD1313 /* GhosttyKit.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@ -25,12 +27,14 @@
|
||||
A55B7BB529B6F47F0055DE60 /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
||||
A55B7BB729B6F53A0055DE60 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
|
||||
A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurfaceView.swift; sourceTree = "<group>"; };
|
||||
A55B7BBD29B701360055DE60 /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.swift; sourceTree = "<group>"; };
|
||||
A55B7BBD29B701360055DE60 /* Ghostty.SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.SplitView.swift; sourceTree = "<group>"; };
|
||||
A59444F629A2ED5200725BBA /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A5B30534299BEAAA0047F10C /* GhosttyApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhosttyApp.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
A5D495A1299BEC7E00DD1313 /* GhosttyKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = GhosttyKit.xcframework; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -50,6 +54,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5D495A0299BEC2200DD1313 /* Preview Content */,
|
||||
A5CEAFDA29B8005900646FDA /* SplitView */,
|
||||
A55B7BB429B6F4410055DE60 /* Ghostty */,
|
||||
A5B30534299BEAAA0047F10C /* GhosttyApp.swift */,
|
||||
A535B9D9299C569B0017E2E4 /* ErrorView.swift */,
|
||||
@ -65,7 +70,7 @@
|
||||
A55B7BB729B6F53A0055DE60 /* Package.swift */,
|
||||
A55B7BB529B6F47F0055DE60 /* AppState.swift */,
|
||||
A55B7BBB29B6FC330055DE60 /* SurfaceView.swift */,
|
||||
A55B7BBD29B701360055DE60 /* SplitView.swift */,
|
||||
A55B7BBD29B701360055DE60 /* Ghostty.SplitView.swift */,
|
||||
);
|
||||
path = Ghostty;
|
||||
sourceTree = "<group>";
|
||||
@ -89,6 +94,15 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A5CEAFDA29B8005900646FDA /* SplitView */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5CEAFDB29B8009000646FDA /* SplitView.swift */,
|
||||
A5CEAFDD29B8058B00646FDA /* SplitView.Splitter.swift */,
|
||||
);
|
||||
path = SplitView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A5D495A0299BEC2200DD1313 /* Preview Content */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -176,11 +190,13 @@
|
||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
||||
A55B7BBE29B701360055DE60 /* SplitView.swift in Sources */,
|
||||
A55B7BBE29B701360055DE60 /* Ghostty.SplitView.swift in Sources */,
|
||||
A55B7BB629B6F47F0055DE60 /* AppState.swift in Sources */,
|
||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */,
|
||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||
A535B9DA299C569B0017E2E4 /* ErrorView.swift in Sources */,
|
||||
A5B30535299BEAAA0047F10C /* GhosttyApp.swift in Sources */,
|
||||
A5CEAFDE29B8058B00646FDA /* SplitView.Splitter.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -21,8 +21,15 @@ struct GhosttyApp: App {
|
||||
case .error:
|
||||
ErrorView()
|
||||
case .ready:
|
||||
Ghostty.TerminalSplitView()
|
||||
.ghosttyApp(ghostty.app!)
|
||||
SplitView(.horizontal, left: {
|
||||
Color.green
|
||||
}, right: {
|
||||
Color.red
|
||||
})
|
||||
/*
|
||||
Ghostty.Terminal()
|
||||
.ghosttyApp(ghostty.app!)
|
||||
*/
|
||||
}
|
||||
}.commands {
|
||||
CommandGroup(after: .newItem) {
|
||||
|
67
macos/Sources/SplitView/SplitView.Splitter.swift
Normal file
67
macos/Sources/SplitView/SplitView.Splitter.swift
Normal file
@ -0,0 +1,67 @@
|
||||
import SwiftUI
|
||||
|
||||
extension SplitView {
|
||||
struct Splitter: View {
|
||||
let direction: Direction
|
||||
let visibleSize: CGFloat
|
||||
let invisibleSize: CGFloat
|
||||
|
||||
private var visibleWidth: CGFloat? {
|
||||
switch (direction) {
|
||||
case .horizontal:
|
||||
return visibleSize
|
||||
case .vertical:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private var visibleHeight: CGFloat? {
|
||||
switch (direction) {
|
||||
case .horizontal:
|
||||
return nil
|
||||
case .vertical:
|
||||
return visibleSize
|
||||
}
|
||||
}
|
||||
|
||||
private var invisibleWidth: CGFloat? {
|
||||
switch (direction) {
|
||||
case .horizontal:
|
||||
return visibleSize + invisibleSize
|
||||
case .vertical:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private var invisibleHeight: CGFloat? {
|
||||
switch (direction) {
|
||||
case .horizontal:
|
||||
return nil
|
||||
case .vertical:
|
||||
return visibleSize + invisibleSize
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Color.clear
|
||||
.frame(width: invisibleWidth, height: invisibleHeight)
|
||||
Rectangle()
|
||||
.fill(Color.gray)
|
||||
.frame(width: visibleWidth, height: visibleHeight)
|
||||
}
|
||||
.onHover { isHovered in
|
||||
if (isHovered) {
|
||||
switch (direction) {
|
||||
case .horizontal:
|
||||
NSCursor.resizeLeftRight.push()
|
||||
case .vertical:
|
||||
NSCursor.resizeUpDown.push()
|
||||
}
|
||||
} else {
|
||||
NSCursor.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
macos/Sources/SplitView/SplitView.swift
Normal file
87
macos/Sources/SplitView/SplitView.swift
Normal file
@ -0,0 +1,87 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SplitView<L: View, R: View>: View {
|
||||
let direction: Direction
|
||||
let left: L
|
||||
let right: R
|
||||
|
||||
private let splitterVisibleSize: CGFloat = 5
|
||||
private let splitterInvisibleSize: CGFloat = 5
|
||||
|
||||
@State var split: CGFloat = 0.5
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
let leftRect = self.leftRect(for: geo.size)
|
||||
let rightRect = self.rightRect(for: geo.size, leftRect: leftRect)
|
||||
let splitterPoint = self.splitterPoint(for: geo.size, leftRect: leftRect)
|
||||
|
||||
ZStack(alignment: .topLeading) {
|
||||
left
|
||||
.frame(width: leftRect.size.width, height: leftRect.size.height)
|
||||
.offset(x: leftRect.origin.x, y: leftRect.origin.y)
|
||||
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)
|
||||
.position(splitterPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
case .horizontal:
|
||||
result.size.width = result.size.width * split
|
||||
result.size.width -= splitterVisibleSize / 2
|
||||
|
||||
case .vertical:
|
||||
result.size.height = result.size.height * split
|
||||
result.size.height -= splitterVisibleSize / 2
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
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) {
|
||||
case .horizontal:
|
||||
// For horizontal layouts we offset the starting X by the left rect
|
||||
// and make the width fit the remaining space.
|
||||
result.origin.x += leftRect.size.width
|
||||
result.origin.x += splitterVisibleSize / 2
|
||||
result.size.width -= result.origin.x
|
||||
|
||||
case .vertical:
|
||||
assert(false)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func splitterPoint(for size: CGSize, leftRect: CGRect) -> CGPoint {
|
||||
switch (direction) {
|
||||
case .horizontal:
|
||||
return CGPoint(x: leftRect.size.width, y: size.height / 2)
|
||||
|
||||
case .vertical:
|
||||
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 {
|
||||
enum Direction {
|
||||
case horizontal, vertical
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user