From 2492aec9bdcf75fba32fd8f9a44d9d81247e06a0 Mon Sep 17 00:00:00 2001
From: Daniel Tull
Date: Mon, 6 Jan 2025 18:19:45 +0000
Subject: [PATCH] AlignmentID
---
.../Modifiers/Layout/FixedFrame.swift | 10 ++-
Sources/TerminalUI/Primitives/Alignment.swift | 81 +++++++++++++++++--
.../TerminalUI/Primitives/Dimensions.swift | 6 ++
Sources/TerminalUI/Primitives/Size.swift | 8 +-
.../Modifiers/Layout/FixedFrameTests.swift | 24 ++++++
5 files changed, 115 insertions(+), 14 deletions(-)
diff --git a/Sources/TerminalUI/Modifiers/Layout/FixedFrame.swift b/Sources/TerminalUI/Modifiers/Layout/FixedFrame.swift
index 5a4ffbd..946999f 100644
--- a/Sources/TerminalUI/Modifiers/Layout/FixedFrame.swift
+++ b/Sources/TerminalUI/Modifiers/Layout/FixedFrame.swift
@@ -3,9 +3,14 @@ extension View {
public func frame(
width: Horizontal? = nil,
- height: Vertical? = nil
+ height: Vertical? = nil,
+ alignment: Alignment = .center
) -> some View {
- FixedFrame(content: self, width: width, height: height)
+ FixedFrame(
+ content: self,
+ width: width,
+ height: height,
+ alignment: alignment)
}
}
@@ -14,6 +19,7 @@ private struct FixedFrame: Builtin, View {
let content: Content
let width: Horizontal?
let height: Vertical?
+ let alignment: Alignment
func size(
for proposal: ProposedSize,
diff --git a/Sources/TerminalUI/Primitives/Alignment.swift b/Sources/TerminalUI/Primitives/Alignment.swift
index 7113c6f..033db62 100644
--- a/Sources/TerminalUI/Primitives/Alignment.swift
+++ b/Sources/TerminalUI/Primitives/Alignment.swift
@@ -25,18 +25,83 @@ extension Alignment {
public static var bottomTrailing: Self { Self(horizontal: .trailing, vertical: .bottom) }
}
+// MARK: - AlignmentID
+
+public protocol AlignmentID: Equatable {
+ static func defaultValue(in size: Size) -> Int
+}
+
+// MARK: - AlignmentKey
+
+public struct AlignmentKey: Equatable, Hashable, Sendable {
+
+ fileprivate let id: any AlignmentID.Type
+
+ public static func == (lhs: AlignmentKey, rhs: AlignmentKey) -> Bool {
+ String(describing: lhs.id) == String(describing: rhs.id)
+ }
+
+ public func hash(into hasher: inout Hasher) {
+ hasher.combine(String(describing: id))
+ }
+}
+
// MARK: - Horizontal Alignment
-public enum HorizontalAlignment: Equatable, Hashable, Sendable {
- case leading
- case center
- case trailing
+public struct HorizontalAlignment: Equatable, Hashable, Sendable {
+ let id: AlignmentKey
+}
+
+extension HorizontalAlignment {
+ public init(_ id: any AlignmentID.Type) {
+ self.id = AlignmentKey(id: id)
+ }
+}
+
+extension HorizontalAlignment {
+ public static var leading: Self { Self(HorizontalLeading.self) }
+ public static var center: Self { Self(HorizontalCenter.self) }
+ public static var trailing: Self { Self(HorizontalTrailing.self) }
+}
+
+private enum HorizontalLeading: AlignmentID {
+ static func defaultValue(in size: Size) -> Int { 1 }
+}
+
+private enum HorizontalCenter: AlignmentID {
+ static func defaultValue(in size: Size) -> Int { Int(size.width) / 2 }
+}
+
+private enum HorizontalTrailing: AlignmentID {
+ static func defaultValue(in size: Size) -> Int { Int(size.width) }
}
// MARK: - Vertical Alignment
-public enum VerticalAlignment: Equatable, Hashable, Sendable {
- case top
- case center
- case bottom
+public struct VerticalAlignment: Equatable, Hashable, Sendable {
+ let id: AlignmentKey
+}
+
+extension VerticalAlignment {
+ public init(_ id: any AlignmentID.Type) {
+ self.id = AlignmentKey(id: id)
+ }
+}
+
+extension VerticalAlignment {
+ public static var top: Self { Self(VerticalTop.self) }
+ public static var center: Self { Self(VerticalCenter.self) }
+ public static var bottom: Self { Self(VerticalBottom.self) }
+}
+
+private enum VerticalTop: AlignmentID {
+ static func defaultValue(in size: Size) -> Int { 1 }
+}
+
+private enum VerticalCenter: AlignmentID {
+ static func defaultValue(in size: Size) -> Int { Int(size.height) / 2 }
+}
+
+private enum VerticalBottom: AlignmentID {
+ static func defaultValue(in size: Size) -> Int { Int(size.height) }
}
diff --git a/Sources/TerminalUI/Primitives/Dimensions.swift b/Sources/TerminalUI/Primitives/Dimensions.swift
index 8412c87..dfd3f52 100644
--- a/Sources/TerminalUI/Primitives/Dimensions.swift
+++ b/Sources/TerminalUI/Primitives/Dimensions.swift
@@ -118,3 +118,9 @@ extension Vertical {
Vertical(-vertical.value)
}
}
+
+extension Int {
+ init(_ vertical: Vertical) {
+ self = vertical.value
+ }
+}
diff --git a/Sources/TerminalUI/Primitives/Size.swift b/Sources/TerminalUI/Primitives/Size.swift
index b09d11b..57c3237 100644
--- a/Sources/TerminalUI/Primitives/Size.swift
+++ b/Sources/TerminalUI/Primitives/Size.swift
@@ -1,10 +1,10 @@
-package struct Size {
+public struct Size {
- package let width: Horizontal
- package let height: Vertical
+ public let width: Horizontal
+ public let height: Vertical
- package init(width: Horizontal, height: Vertical) {
+ public init(width: Horizontal, height: Vertical) {
self.width = width
self.height = height
}
diff --git a/Tests/TerminalUITests/Modifiers/Layout/FixedFrameTests.swift b/Tests/TerminalUITests/Modifiers/Layout/FixedFrameTests.swift
index 9fefca3..20b199f 100644
--- a/Tests/TerminalUITests/Modifiers/Layout/FixedFrameTests.swift
+++ b/Tests/TerminalUITests/Modifiers/Layout/FixedFrameTests.swift
@@ -131,4 +131,28 @@ struct FixedFrameTests {
Position(x: 2, y: 2): pixel,
])
}
+
+ @Test("alignment", arguments: Array<(
+ Alignment, Horizontal, Vertical
+ )>([
+ (.topLeading, 1, 1),
+ (.top, 2, 1),
+ (.topTrailing, 3, 1),
+ (.leading, 1, 2),
+ (.center, 2, 2),
+ (.trailing, 3, 2),
+ (.bottomLeading, 1, 3),
+ (.bottom, 2, 3),
+ (.bottomTrailing, 3, 3),
+ ]))
+ func alignment(alignment: Alignment, x: Horizontal, y: Vertical) {
+
+ canvas.render {
+ view
+ .frame(width: 1, height: 1)
+ .frame(width: 3, height: 3, alignment: alignment)
+ }
+
+ #expect(canvas.pixels == [Position(x: x, y: y): pixel])
+ }
}