From f715337425971b51188e16e90dcb7696ef63a5c7 Mon Sep 17 00:00:00 2001 From: Daniel Tull Date: Tue, 24 Dec 2024 13:54:01 +0000 Subject: [PATCH] HStack 2 --- Sources/TerminalUI/Layouts/HStack.swift | 68 +++++++++++++++++++ .../TerminalUI/Primitives/Dimensions.swift | 6 ++ Sources/TerminalUI/Views/HStack.swift | 47 ------------- 3 files changed, 74 insertions(+), 47 deletions(-) create mode 100644 Sources/TerminalUI/Layouts/HStack.swift delete mode 100644 Sources/TerminalUI/Views/HStack.swift diff --git a/Sources/TerminalUI/Layouts/HStack.swift b/Sources/TerminalUI/Layouts/HStack.swift new file mode 100644 index 0000000..c0e5152 --- /dev/null +++ b/Sources/TerminalUI/Layouts/HStack.swift @@ -0,0 +1,68 @@ + +struct HStack { + private let children: [any View] + @Mutable private var sizes: [Size] = [] +} + +extension HStack: Builtin, View { + + func size( + for proposal: ProposedSize, + environment: EnvironmentValues + ) -> Size { + + let flexibility = children.map { child in + let min = ProposedSize(width: 0, height: proposal.height) + let max = ProposedSize(width: .max, height: proposal.height) + let smallest = child._size(for: min) + let largest = child._size(for: max) + return largest.width - smallest.width + } + + var indices = children.indices + .sorted { flexibility[$0] < flexibility[$1] } + + sizes = Array(repeating: .zero, count: children.count) + var remaining = proposal.width + + while !indices.isEmpty { + + let proposal = ProposedSize( + width: remaining / indices.count, + height: proposal.height) + + let index = indices.removeFirst() + + let child = children[index] + let size = child._size(for: proposal, environment: environment) + sizes[index] = size + remaining -= size.width + if remaining < 0 { remaining = 0 } + } + + let width = sizes.map(\.width).reduce(0, +) + let height = sizes.map(\.height).reduce(0, +) + return Size(width: width, height: height) + } + + func render( + in canvas: any Canvas, + size: Size, + environment: EnvironmentValues + ) { + var x: Horizontal = 0 + for (child, childSize) in zip(children, sizes) { + let canvas = canvas.translateBy(x: x, y: 0) + child._render(in: canvas, size: childSize, environment: environment) + x += childSize.width + } + } +} + +extension Horizontal { + fileprivate static var max: Self { Self(Int.max) } +} + +extension Size { + fileprivate static var zero: Size { Size(width: 0, height: 0) } +} diff --git a/Sources/TerminalUI/Primitives/Dimensions.swift b/Sources/TerminalUI/Primitives/Dimensions.swift index 0b26fa9..0679777 100644 --- a/Sources/TerminalUI/Primitives/Dimensions.swift +++ b/Sources/TerminalUI/Primitives/Dimensions.swift @@ -56,6 +56,12 @@ extension Horizontal { } } +extension Horizontal { + static func / (lhs: Horizontal, rhs: Int) -> Horizontal { + Horizontal(lhs.value / rhs) + } +} + // MARK: - Vertical /// A measurement of the vertical dimension. diff --git a/Sources/TerminalUI/Views/HStack.swift b/Sources/TerminalUI/Views/HStack.swift deleted file mode 100644 index 4733ddf..0000000 --- a/Sources/TerminalUI/Views/HStack.swift +++ /dev/null @@ -1,47 +0,0 @@ - -struct HStack { - private let children: [any View] - @Mutable private var sizes: [Size] = [] -} - -extension HStack: Builtin, View { - - func size( - for proposal: ProposedSize, - environment: EnvironmentValues - ) -> Size { - - let flexibility: [Horizontal] = children.map { child in - let min = ProposedSize(width: 0, height: proposal.height) - let max = ProposedSize(width: .max, height: proposal.height) - let lower = child._size(for: min) - let upper = child._size(for: max) - return upper.width - lower.width - } - - var remainingIndices = children.indices - .sorted { lhs, rhs in flexibility[lhs] < flexibility[rhs] } - - - - - Size(width: proposal.width, height: proposal.height) - } - - func render( - in canvas: any Canvas, - size: Size, - environment: EnvironmentValues - ) { - var x: Horizontal = 0 - for (child, childSize) in zip(children, sizes) { - let canvas = canvas.translateBy(x: x, y: 0) - child._render(in: canvas, size: childSize, environment: environment) - x += childSize.width - } - } -} - -extension Horizontal { - fileprivate static var max: Self { Self(Int.max) } -}