macOS: equalize splits when double tapping on SplitView divider (#9524)

Resolves Issue #8357 

### Implementation

Following the existing `onResize` callback pattern in
`TerminalSplitTreeView`, I added an `onEqualize` callback to
`SplitView`. When a divider is double-tapped, the callback retrieves a
surface from that `TerminalView`'s `SplitTree` and calls `splitEqualize`
to equalize the entire tree.

### Context

There is an existing PR #8364 that implements this feature but uses
`focusedSurface`, which doesn't work for unfocused windows. Since that
PR has been inactive for a few months after requested changes, I've
implemented this alternative approach.

Credit to @liby for that initial implementation!

### AI Usage

I chatted with Claude Code in Plan mode to understand the relationship
between surfaces and the split tree/split views, but I wrote all the
code myself.

### Screenshot


https://github.com/user-attachments/assets/0efd70ef-c90e-4b50-b853-b05e2ca2be67
pull/9528/head
Lukas 2025-11-09 09:07:09 +01:00 committed by GitHub
commit 84082c2b96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 16 additions and 1 deletions

View File

@ -21,6 +21,9 @@ struct SplitView<L: View, R: View>: View {
let left: L let left: L
let right: R let right: R
/// Called when the divider is double-tapped to equalize splits.
let onEqualize: () -> Void
/// The minimum size (in points) of a split /// The minimum size (in points) of a split
let minSize: CGFloat = 10 let minSize: CGFloat = 10
@ -56,6 +59,9 @@ struct SplitView<L: View, R: View>: View {
split: $split) split: $split)
.position(splitterPoint) .position(splitterPoint)
.gesture(dragGesture(geo.size, splitterPoint: splitterPoint)) .gesture(dragGesture(geo.size, splitterPoint: splitterPoint))
.onTapGesture(count: 2) {
onEqualize()
}
} }
.accessibilityElement(children: .contain) .accessibilityElement(children: .contain)
.accessibilityLabel(splitViewLabel) .accessibilityLabel(splitViewLabel)
@ -69,7 +75,8 @@ struct SplitView<L: View, R: View>: View {
dividerColor: Color, dividerColor: Color,
resizeIncrements: NSSize = .init(width: 1, height: 1), resizeIncrements: NSSize = .init(width: 1, height: 1),
@ViewBuilder left: (() -> L), @ViewBuilder left: (() -> L),
@ViewBuilder right: (() -> R) @ViewBuilder right: (() -> R),
onEqualize: @escaping () -> Void
) { ) {
self.direction = direction self.direction = direction
self._split = split self._split = split
@ -77,6 +84,7 @@ struct SplitView<L: View, R: View>: View {
self.resizeIncrements = resizeIncrements self.resizeIncrements = resizeIncrements
self.left = left() self.left = left()
self.right = right() self.right = right()
self.onEqualize = onEqualize
} }
private func dragGesture(_ size: CGSize, splitterPoint: CGPoint) -> some Gesture { private func dragGesture(_ size: CGSize, splitterPoint: CGPoint) -> some Gesture {

View File

@ -55,6 +55,10 @@ struct TerminalSplitSubtreeView: View {
}, },
right: { right: {
TerminalSplitSubtreeView(node: split.right, onResize: onResize) TerminalSplitSubtreeView(node: split.right, onResize: onResize)
},
onEqualize: {
guard let surface = node.leftmostLeaf().surface else { return }
ghostty.splitEqualize(surface: surface)
} }
) )
} }

View File

@ -32,6 +32,9 @@ extension Ghostty {
InspectorViewRepresentable(surfaceView: surfaceView) InspectorViewRepresentable(surfaceView: surfaceView)
.focused($inspectorFocus) .focused($inspectorFocus)
.focusedValue(\.ghosttySurfaceView, surfaceView) .focusedValue(\.ghosttySurfaceView, surfaceView)
}, onEqualize: {
guard let surface = surfaceView.surface else { return }
ghostty.splitEqualize(surface: surface)
}) })
} }
} }