Files
kotej/App/SplitPane.swift
alexandrev-tibco e0bff88225 Make the panes fill the window: replace VSplitView with an explicit split
The tree kept rendering as a ~628pt column centred in a wide window. That number
was exactly the sum of the row's column minimums, which gave it away: VSplitView
proposes its children their *ideal* width, so the List settled at the narrowest
size its content allowed instead of filling.

SplitPane hands each pane geo.size.width explicitly, removing the ambiguity, and
its divider is draggable with the position remembered across launches. With the
width no longer ambiguous the two name columns share it evenly, so each side gets
half the window; their floor dropped to 120pt since a large minimum is what
becomes the ideal width in the first place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UfhDYRLTpGJSKGP1m3LVJN
2026-07-27 17:44:40 +02:00

63 lines
2.1 KiB
Swift

import SwiftUI
/// A vertical split with an explicit width for both panes.
///
/// `VSplitView` proposes its children their *ideal* width, so a `List` inside it
/// settles at the sum of its columns' minimums and sits centred with empty space
/// on both sides. Handing each pane `geo.size.width` removes the ambiguity, and
/// the divider stays draggable.
struct SplitPane<Top: View, Bottom: View>: View {
@Binding var fraction: CGFloat
let minimumFraction: CGFloat
@ViewBuilder var top: () -> Top
@ViewBuilder var bottom: () -> Bottom
private let dividerHeight: CGFloat = 6
init(fraction: Binding<CGFloat>,
minimumFraction: CGFloat = 0.15,
@ViewBuilder top: @escaping () -> Top,
@ViewBuilder bottom: @escaping () -> Bottom) {
self._fraction = fraction
self.minimumFraction = minimumFraction
self.top = top
self.bottom = bottom
}
var body: some View {
GeometryReader { geo in
let available = max(geo.size.height - dividerHeight, 1)
let topHeight = (available * fraction).rounded()
VStack(spacing: 0) {
top()
.frame(width: geo.size.width, height: topHeight)
divider(totalHeight: available)
bottom()
.frame(width: geo.size.width, height: available - topHeight)
}
}
}
private func divider(totalHeight: CGFloat) -> some View {
ZStack {
Rectangle().fill(Color(nsColor: .separatorColor))
.frame(height: 1)
Rectangle().fill(Color.clear)
.frame(height: dividerHeight)
.contentShape(Rectangle())
}
.frame(height: dividerHeight)
.onHover { inside in
if inside { NSCursor.resizeUpDown.push() } else { NSCursor.pop() }
}
.gesture(
DragGesture()
.onChanged { value in
let delta = value.translation.height / totalHeight
fraction = min(max(fraction + delta * 0.35, minimumFraction), 1 - minimumFraction)
}
)
}
}