Skip to content

Commit

Permalink
Add padding view modifier
Browse files Browse the repository at this point in the history
  • Loading branch information
danielctull committed Dec 16, 2024
2 parents 903640e + b48312c commit 7a38554
Show file tree
Hide file tree
Showing 9 changed files with 495 additions and 6 deletions.
102 changes: 102 additions & 0 deletions Sources/TerminalUI/Rendering/Dimensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@

// MARK: Horizontal

/// A measurement of the horizontal dimension.
public struct Horizontal: Equatable, Hashable {
fileprivate let value: Int
}

extension Horizontal: AdditiveArithmetic {
public static func + (lhs: Horizontal, rhs: Horizontal) -> Horizontal {
Horizontal(value: lhs.value + rhs.value)
}

public static func - (lhs: Horizontal, rhs: Horizontal) -> Horizontal {
Horizontal(value: lhs.value - rhs.value)
}
}

extension Horizontal: Comparable {
public static func < (lhs: Horizontal, rhs: Horizontal) -> Bool {
lhs.value < rhs.value
}
}

extension Horizontal: CustomStringConvertible {
public var description: String {
value.description
}
}

extension Horizontal: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self.init(value: value)
}
}

extension Horizontal: Strideable {
public func advanced(by n: Int) -> Horizontal {
Horizontal(value: value + n)
}

public func distance(to other: Horizontal) -> Int {
value - other.value
}
}

extension Horizontal {
package init(_ value: some BinaryInteger) {
self.init(value: Int(value))
}
}

// MARK: - Vertical

/// A measurement of the vertical dimension.
public struct Vertical: Equatable, Hashable {
fileprivate let value: Int
}

extension Vertical: AdditiveArithmetic {
public static func + (lhs: Vertical, rhs: Vertical) -> Vertical {
Vertical(value: lhs.value + rhs.value)
}

public static func - (lhs: Vertical, rhs: Vertical) -> Vertical {
Vertical(value: lhs.value - rhs.value)
}
}

extension Vertical: Comparable {
public static func < (lhs: Vertical, rhs: Vertical) -> Bool {
lhs.value < rhs.value
}
}

extension Vertical: CustomStringConvertible {
public var description: String {
value.description
}
}

extension Vertical: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self.init(value: value)
}
}

extension Vertical: Strideable {
public func advanced(by n: Int) -> Vertical {
Vertical(value: value + n)
}

public func distance(to other: Vertical) -> Int {
value - other.value
}
}

extension Vertical {
package init(_ value: some BinaryInteger) {
self.init(value: Int(value))
}
}
59 changes: 59 additions & 0 deletions Sources/TerminalUI/Rendering/Edge.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

public enum Edge {}

extension Edge {
public struct Set<Value> {
fileprivate let _insets: (Value) -> EdgeInsets
}
}

extension Edge.Set {
func insets(_ value: Value) -> EdgeInsets {
_insets(value)
}
}

extension Edge.Set<Int> {

public static var all: Self {
Self { value in
let vertical = Vertical(value)
let horizontal = Horizontal(value)
return EdgeInsets(
top: vertical,
leading: horizontal,
bottom: vertical,
trailing: horizontal)
}
}
}

extension Edge.Set<Vertical> {

public static var top: Self {
Self { EdgeInsets(top: $0, leading: 0, bottom: 0, trailing: 0) }
}

public static var bottom: Self {
Self { EdgeInsets(top: 0, leading: 0, bottom: $0, trailing: 0) }
}

public static var vertical: Self {
Self { EdgeInsets(top: $0, leading: 0, bottom: $0, trailing: 0) }
}
}

extension Edge.Set<Horizontal> {

public static var leading: Self {
Self { EdgeInsets(top: 0, leading: $0, bottom: 0, trailing: 0) }
}

public static var trailing: Self {
Self { EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: $0) }
}

public static var horizontal: Self {
Self { EdgeInsets(top: 0, leading: $0, bottom: 0, trailing: $0) }
}
}
19 changes: 19 additions & 0 deletions Sources/TerminalUI/Rendering/EdgeInsets.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

public struct EdgeInsets {
let top: Vertical
let leading: Horizontal
let bottom: Vertical
let trailing: Horizontal

public init(
top: Vertical,
leading: Horizontal,
bottom: Vertical,
trailing: Horizontal
) {
self.top = top
self.leading = leading
self.bottom = bottom
self.trailing = trailing
}
}
4 changes: 2 additions & 2 deletions Sources/TerminalUI/Rendering/Position.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

struct Position: Equatable, Hashable {
let x: Int
let y: Int
let x: Horizontal
let y: Vertical
}

extension Position {
Expand Down
6 changes: 3 additions & 3 deletions Sources/TerminalUI/Rendering/Size.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Foundation

struct Size {
let width: Int
let height: Int
let width: Horizontal
let height: Vertical
}

extension Size {

static var window: Size {
var size = winsize()
_ = ioctl(STDOUT_FILENO, UInt(TIOCGWINSZ), &size)
return Size(width: Int(size.ws_col), height: Int(size.ws_row))
return Size(width: Horizontal(size.ws_col), height: Vertical(size.ws_row))
}
}
66 changes: 66 additions & 0 deletions Sources/TerminalUI/ViewModifiers/Padding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@

extension View {

public func padding(_ insets: EdgeInsets) -> some View {
Padding(content: self, insets: insets)
}

public func padding<Value>(
_ set: Edge.Set<Value>,
_ value: Value
) -> some View {
padding(set.insets(value))
}

public func padding(_ length: Int) -> some View {
padding(.all, length)
}
}

private struct Padding<Content: View>: Builtin, View {

let content: Content
let insets: EdgeInsets

func render(
in canvas: any Canvas,
size: Size,
environment: EnvironmentValues
) {
content._render(
in: canvas.inset(insets),
size: size.inset(insets),
environment: environment
)
}
}

extension Canvas {
fileprivate func inset(_ insets: EdgeInsets) -> Canvas {
InsetCanvas(base: self, insets: insets)
}
}

private struct InsetCanvas<Base: Canvas>: Canvas {
let base: Base
let insets: EdgeInsets

func draw(_ pixel: Pixel, at position: Position) {
base.draw(pixel, at: position.offset(by: insets))
}
}

extension Position {
fileprivate func offset(by insets: EdgeInsets) -> Position {
Position(x: x + insets.leading, y: y + insets.top)
}
}

extension Size {
fileprivate func inset(_ insets: EdgeInsets) -> Size {
Size(
width: width - insets.leading - insets.trailing,
height: height - insets.top - insets.bottom
)
}
}
2 changes: 1 addition & 1 deletion Sources/TerminalUI/Views/Text.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public struct Text: Builtin, View {
hidden: environment.hidden,
strikethrough: environment.strikethrough
)
canvas.draw(pixel, at: Position(x: index, y: 0))
canvas.draw(pixel, at: Position(x: Horizontal(index), y: 0))
}
}
}
94 changes: 94 additions & 0 deletions Tests/TerminalUITests/Internal/DimensionsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import TerminalUI
import TerminalUITesting
import Testing

@Suite("Dimensions") struct DimensionTests {

@Suite("Horizontal") struct HorizontalTests {

@Test("ExpressibleByIntegerLiteral")
func expressibleByIntegerLiteral() {
let horizontal: Horizontal = 3
#expect(horizontal.description == "3")
}

@Test("AdditiveArithmetic")
func additiveArithmetic() {
#expect(Horizontal(4) + Horizontal(2) == Horizontal(6))
#expect(Horizontal(4) - Horizontal(2) == Horizontal(2))
}

@Test("Comparable")
func comparable() {
#expect(Horizontal(1) < Horizontal(2))
#expect(Horizontal(2) > Horizontal(1))
#expect(Horizontal(1) <= Horizontal(1))
#expect(Horizontal(1) >= Horizontal(1))
}

@Test("Equatable")
func equatable() {
#expect(Horizontal(1) == Horizontal(1))
#expect(Horizontal(2) != Horizontal(1))
}

@Test("Strideable")
func strideable() {
var horizontals = (Horizontal(1)...Horizontal(3)).makeIterator()
#expect(horizontals.next() == 1)
#expect(horizontals.next() == 2)
#expect(horizontals.next() == 3)
#expect(horizontals.next() == nil)
}

@Test("init(some BinaryInteger)")
func initBinaryInteger() {
let value = Int.random(in: 0..<1_000_000)
#expect(Horizontal(value).description == value.description)
}
}

@Suite("Vertical") struct VerticalTests {

@Test("AdditiveArithmetic")
func additiveArithmetic() {
#expect(Vertical(4) + Vertical(2) == Vertical(6))
#expect(Vertical(4) - Vertical(2) == Vertical(2))
}

@Test("Comparable")
func comparable() {
#expect(Vertical(1) < Vertical(2))
#expect(Vertical(2) > Vertical(1))
#expect(Vertical(1) <= Vertical(1))
#expect(Vertical(1) >= Vertical(1))
}

@Test("Equatable")
func equatable() {
#expect(Vertical(1) == Vertical(1))
#expect(Vertical(2) != Vertical(1))
}

@Test("ExpressibleByIntegerLiteral")
func expressibleByIntegerLiteral() {
let vertical: Vertical = 3
#expect(vertical.description == "3")
}

@Test("Strideable")
func strideable() {
var verticals = (Vertical(1)...Vertical(3)).makeIterator()
#expect(verticals.next() == 1)
#expect(verticals.next() == 2)
#expect(verticals.next() == 3)
#expect(verticals.next() == nil)
}

@Test("init(some BinaryInteger)")
func initBinaryInteger() {
let value: Int = Int.random(in: 0..<1_000_000)
#expect(Vertical(value).description == value.description)
}
}
}
Loading

0 comments on commit 7a38554

Please sign in to comment.