diff --git a/Sources/TerminalUI Demo/Demo.swift b/Sources/TerminalUI Demo/Demo.swift index 87b4d0c..398201b 100644 --- a/Sources/TerminalUI Demo/Demo.swift +++ b/Sources/TerminalUI Demo/Demo.swift @@ -4,11 +4,6 @@ import TerminalUI struct Demo: App { var body: some View { - Text("daniel") - .bold() - .italic() - .underline() - .blinking() - .inverse() + Color.red.padding(top: 5, leading: 2, bottom: 10, trailing: 8) } } diff --git a/Sources/TerminalUI/Rendering/Dimensions.swift b/Sources/TerminalUI/Rendering/Dimensions.swift index 817323e..aaba466 100644 --- a/Sources/TerminalUI/Rendering/Dimensions.swift +++ b/Sources/TerminalUI/Rendering/Dimensions.swift @@ -2,34 +2,44 @@ // MARK: Horizontal /// A measurement of the horizontal dimension. -struct Horizontal: Equatable, Hashable { +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 { - static func < (lhs: Horizontal, rhs: Horizontal) -> Bool { + public static func < (lhs: Horizontal, rhs: Horizontal) -> Bool { lhs.value < rhs.value } } extension Horizontal: CustomStringConvertible { - var description: String { + public var description: String { value.description } } extension Horizontal: ExpressibleByIntegerLiteral { - init(integerLiteral value: Int) { + public init(integerLiteral value: Int) { self.init(value: value) } } extension Horizontal: Strideable { - func advanced(by n: Int) -> Horizontal { + public func advanced(by n: Int) -> Horizontal { Horizontal(value: value + n) } - func distance(to other: Horizontal) -> Int { + public func distance(to other: Horizontal) -> Int { value - other.value } } @@ -43,34 +53,44 @@ extension Horizontal { // MARK: - Vertical /// A measurement of the vertical dimension. -struct Vertical: Equatable, Hashable { +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 { - static func < (lhs: Vertical, rhs: Vertical) -> Bool { + public static func < (lhs: Vertical, rhs: Vertical) -> Bool { lhs.value < rhs.value } } extension Vertical: CustomStringConvertible { - var description: String { + public var description: String { value.description } } extension Vertical: ExpressibleByIntegerLiteral { - init(integerLiteral value: Int) { + public init(integerLiteral value: Int) { self.init(value: value) } } extension Vertical: Strideable { - func advanced(by n: Int) -> Vertical { + public func advanced(by n: Int) -> Vertical { Vertical(value: value + n) } - func distance(to other: Vertical) -> Int { + public func distance(to other: Vertical) -> Int { value - other.value } } diff --git a/Sources/TerminalUI/ViewModifiers/Padding.swift b/Sources/TerminalUI/ViewModifiers/Padding.swift new file mode 100644 index 0000000..2605d52 --- /dev/null +++ b/Sources/TerminalUI/ViewModifiers/Padding.swift @@ -0,0 +1,99 @@ + +extension View { + + public func padding(_ length: Int) -> some View { + padding(.all(length)) + } + + public func padding( + top: Vertical = 0, + leading: Horizontal = 0, + bottom: Vertical = 0, + trailing: Horizontal = 0 + ) -> some View { + padding(EdgeInsets( + top: top, + leading: leading, + bottom: bottom, + trailing: trailing + )) + } + + public func padding(_ edgeInsets: EdgeInsets) -> some View { + Padding(content: self, edgeInsets: edgeInsets) + } +} + +private struct Padding: Builtin, View { + + let content: Content + let edgeInsets: EdgeInsets + + func render(in canvas: any Canvas, size: Size, environment: EnvironmentValues) { + content.render( + in: canvas.inset(edgeInsets), + size: size.inset(edgeInsets), + environment: environment + ) + } +} + +extension Canvas { + func inset(_ insets: EdgeInsets) -> Canvas { + InsetCanvas(base: self, insets: insets) + } +} + +private struct InsetCanvas: Canvas { + let base: Base + let insets: EdgeInsets + + func draw(_ pixel: Pixel, at position: Position) { + base.draw(pixel, at: position.offset(by: insets)) + } +} + +extension Position { + func offset(by edgeInsets: EdgeInsets) -> Position { + Position(x: x + edgeInsets.leading, y: y + edgeInsets.top) + } +} + +extension Size { + func inset(_ edgeInsets: EdgeInsets) -> Size { + Size( + width: width - edgeInsets.leading - edgeInsets.trailing, + height: height - edgeInsets.top - edgeInsets.bottom + ) + } +} + +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 + } +} + +extension EdgeInsets { + static func all(_ length: Int) -> EdgeInsets { + EdgeInsets( + top: Vertical(length), + leading: Horizontal(length), + bottom: Vertical(length), + trailing: Horizontal(length) + ) + } +}