diff --git a/Sources/TUIkit/Extensions/View+Layout.swift b/Sources/TUIkit/Extensions/View+Layout.swift index 7eaef7a9..0fb3fa03 100644 --- a/Sources/TUIkit/Extensions/View+Layout.swift +++ b/Sources/TUIkit/Extensions/View+Layout.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + // MARK: - Border extension View { @@ -114,23 +116,26 @@ extension View { /// ``` /// /// - Parameters: - /// - width: The desired width in characters (nil preserves intrinsic width). - /// - height: The desired height in lines (nil preserves intrinsic height). - /// - alignment: The alignment within the frame (default: .topLeading). + /// - width: The desired width (nil preserves intrinsic width). + /// - height: The desired height (nil preserves intrinsic height). + /// - alignment: The alignment within the frame (default: .center, + /// matching SwiftUI). /// - Returns: A view constrained to the specified frame. public func frame( - width: Int? = nil, - height: Int? = nil, - alignment: Alignment = .topLeading + width: CGFloat? = nil, + height: CGFloat? = nil, + alignment: Alignment = .center ) -> some View { - FlexibleFrameView( + let cellWidth = width.map { max(0, TerminalGeometry.cells($0)) } + let cellHeight = height.map { max(0, TerminalGeometry.cells($0)) } + return FlexibleFrameView( content: self, - minWidth: width, - idealWidth: width, - maxWidth: width.map { .fixed($0) }, - minHeight: height, - idealHeight: height, - maxHeight: height.map { .fixed($0) }, + minWidth: cellWidth, + idealWidth: cellWidth, + maxWidth: cellWidth, + minHeight: cellHeight, + idealHeight: cellHeight, + maxHeight: cellHeight, alignment: alignment ) } @@ -156,31 +161,31 @@ extension View { /// ``` /// /// - Parameters: - /// - minWidth: Minimum width in characters. + /// - minWidth: Minimum width. /// - idealWidth: Preferred width (used when no max is set). /// - maxWidth: Maximum width, or `.infinity` to fill available space. - /// - minHeight: Minimum height in lines. + /// - minHeight: Minimum height. /// - idealHeight: Preferred height (used when no max is set). /// - maxHeight: Maximum height, or `.infinity` to fill available space. /// - alignment: The alignment within the frame (default: .center). /// - Returns: A view with flexible frame constraints. public func frame( - minWidth: Int? = nil, - idealWidth: Int? = nil, - maxWidth: FrameDimension? = nil, - minHeight: Int? = nil, - idealHeight: Int? = nil, - maxHeight: FrameDimension? = nil, + minWidth: CGFloat? = nil, + idealWidth: CGFloat? = nil, + maxWidth: CGFloat? = nil, + minHeight: CGFloat? = nil, + idealHeight: CGFloat? = nil, + maxHeight: CGFloat? = nil, alignment: Alignment = .center ) -> some View { FlexibleFrameView( content: self, - minWidth: minWidth, - idealWidth: idealWidth, - maxWidth: maxWidth, - minHeight: minHeight, - idealHeight: idealHeight, - maxHeight: maxHeight, + minWidth: minWidth.map { max(0, TerminalGeometry.cells($0)) }, + idealWidth: idealWidth.map { max(0, TerminalGeometry.cells($0)) }, + maxWidth: maxWidth.map { max(0, TerminalGeometry.cells($0)) }, + minHeight: minHeight.map { max(0, TerminalGeometry.cells($0)) }, + idealHeight: idealHeight.map { max(0, TerminalGeometry.cells($0)) }, + maxHeight: maxHeight.map { max(0, TerminalGeometry.cells($0)) }, alignment: alignment ) } @@ -232,29 +237,32 @@ extension View { /// .padding(2) // 2 lines top/bottom, 2 chars left/right /// ``` /// - /// - Parameter amount: The padding amount on all sides (default: 1). + /// - Parameter length: The padding amount on all sides. /// - Returns: A padded view. - public func padding(_ amount: Int = 1) -> some View { - BufferModifiedView(content: self, modifier: PaddingModifier(insets: EdgeInsets(all: amount))) + public func padding(_ length: CGFloat) -> some View { + padding(.all, length) } /// Adds padding on specific edges. /// - /// In a terminal context, 1 unit of padding means: - /// - **Vertical (top/bottom):** 1 line - /// - **Horizontal (leading/trailing):** 1 character + /// Matches SwiftUI's signature. In a terminal context, 1 unit of + /// padding means one line vertically and one character horizontally; + /// `nil` uses the terminal default of one cell. Fractional lengths + /// quantize through `TerminalGeometry`. /// /// ```swift /// Text("Hello") + /// .padding() // 1 cell on all edges /// .padding(.horizontal, 4) // 4 chars left and right /// .padding(.vertical, 2) // 2 lines top and bottom /// ``` /// /// - Parameters: - /// - edges: The edges to pad. - /// - amount: The padding amount (default: 1). + /// - edges: The set of edges to pad (default: all). + /// - length: The padding amount, or `nil` for the default cell. /// - Returns: A padded view. - public func padding(_ edges: Edge, _ amount: Int = 1) -> some View { + public func padding(_ edges: Edge.Set = .all, _ length: CGFloat? = nil) -> some View { + let amount = CGFloat(TerminalGeometry.spacing(length, default: 1)) let insets = EdgeInsets( top: edges.contains(.top) ? amount : 0, leading: edges.contains(.leading) ? amount : 0, diff --git a/Sources/TUIkit/Extensions/View+ListRowSeparator.swift b/Sources/TUIkit/Extensions/View+ListRowSeparator.swift index a150ae41..82817037 100644 --- a/Sources/TUIkit/Extensions/View+ListRowSeparator.swift +++ b/Sources/TUIkit/Extensions/View+ListRowSeparator.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + // MARK: - List Row Separator Modifier extension View { diff --git a/Sources/TUIkit/Modifiers/FrameModifier.swift b/Sources/TUIkit/Modifiers/FrameModifier.swift index fe9a463b..d32664eb 100644 --- a/Sources/TUIkit/Modifiers/FrameModifier.swift +++ b/Sources/TUIkit/Modifiers/FrameModifier.swift @@ -4,20 +4,6 @@ // Created by LAYERED.work // License: MIT -// MARK: - Frame Dimension - -/// Represents a frame dimension that can be a fixed value or infinity. -public enum FrameDimension: Equatable, Sendable { - /// A fixed size in characters/lines. - case fixed(Int) - - /// Expand to fill all available space. - case infinity - - /// The special infinity value for frame constraints. - public static let max: FrameDimension = .infinity -} - // MARK: - Flexible Frame View /// A view that applies flexible frame constraints to its content. @@ -34,8 +20,8 @@ public struct FlexibleFrameView: View { /// The ideal width in characters, or nil to use intrinsic size. let idealWidth: Int? - /// The maximum width constraint, or nil for no maximum. - let maxWidth: FrameDimension? + /// The maximum width in cells (`Int.max` fills), or nil for no maximum. + let maxWidth: Int? /// The minimum height in lines, or nil for no minimum. let minHeight: Int? @@ -43,8 +29,8 @@ public struct FlexibleFrameView: View { /// The ideal height in lines, or nil to use intrinsic size. let idealHeight: Int? - /// The maximum height constraint, or nil for no maximum. - let maxHeight: FrameDimension? + /// The maximum height in cells (`Int.max` fills), or nil for no maximum. + let maxHeight: Int? /// The alignment of the content within the frame. let alignment: Alignment @@ -76,12 +62,9 @@ extension FlexibleFrameView: Renderable { // Calculate the target width based on constraints let targetWidth: Int if let maximumWidth = maxWidth { - switch maximumWidth { - case .infinity: - targetWidth = context.availableWidth - case .fixed(let value): - targetWidth = min(value, context.availableWidth) - } + targetWidth = maximumWidth == .max + ? context.availableWidth + : min(maximumWidth, context.availableWidth) } else if let ideal = idealWidth { targetWidth = min(ideal, context.availableWidth) } else { @@ -92,12 +75,9 @@ extension FlexibleFrameView: Renderable { // Calculate the target height based on constraints let targetHeight: Int? if let maximumHeight = maxHeight { - switch maximumHeight { - case .infinity: - targetHeight = context.availableHeight - case .fixed(let value): - targetHeight = min(value, context.availableHeight) - } + targetHeight = maximumHeight == .max + ? context.availableHeight + : min(maximumHeight, context.availableHeight) } else if let ideal = idealHeight { targetHeight = min(ideal, context.availableHeight) } else { @@ -131,10 +111,10 @@ extension FlexibleFrameView: Renderable { } // Apply maximum constraints (expand to fill if infinity) - if let maximumWidth = maxWidth, case .infinity = maximumWidth { + if maxWidth == .max { finalWidth = context.availableWidth } - if let maximumHeight = maxHeight, case .infinity = maximumHeight { + if maxHeight == .max { finalHeight = context.availableHeight } @@ -152,15 +132,10 @@ extension FlexibleFrameView: Renderable { var result: [String] = [] // Calculate vertical offset for alignment - let verticalOffset: Int - switch alignment.vertical { - case .top: - verticalOffset = 0 - case .center: - verticalOffset = max(0, (targetHeight - buffer.height) / 2) - case .bottom: - verticalOffset = max(0, targetHeight - buffer.height) - } + let verticalOffset = alignment.vertical.cellOffset( + childHeight: buffer.height, + containerHeight: targetHeight + ) for row in 0.. Bool { + contains(Self(edge)) + } + } } /// A modifier that adds padding around a view. @@ -92,24 +139,26 @@ struct PaddingModifier: BufferViewModifier { let insets: EdgeInsets func adjustContext(_ context: RenderContext) -> RenderContext { + let cells = insets.cellInsets var adjusted = context - adjusted.availableWidth = max(0, context.availableWidth - insets.leading - insets.trailing) - adjusted.availableHeight = max(0, context.availableHeight - insets.top - insets.bottom) + adjusted.availableWidth = max(0, context.availableWidth - cells.leading - cells.trailing) + adjusted.availableHeight = max(0, context.availableHeight - cells.top - cells.bottom) return adjusted } func modify(buffer: FrameBuffer, context: RenderContext) -> FrameBuffer { + let cells = insets.cellInsets var result: [String] = [] - let leadingPad = String(repeating: " ", count: insets.leading) - let trailingPad = String(repeating: " ", count: insets.trailing) + let leadingPad = String(repeating: " ", count: cells.leading) + let trailingPad = String(repeating: " ", count: cells.trailing) // Calculate line width - let lineWidth = buffer.width + insets.leading + insets.trailing + let lineWidth = buffer.width + cells.leading + cells.trailing let emptyLine = String(repeating: " ", count: lineWidth) // Top padding (full lines) - for _ in 0.. CGFloat +} + +// MARK: - View Dimensions + +/// A view's size in the container's coordinate space. +/// +/// Terminal adaptation of SwiftUI's `ViewDimensions`: widths and heights +/// are whole cells surfaced as `CGFloat` so alignment math matches +/// SwiftUI's continuous geometry. +public struct ViewDimensions: Equatable, Sendable { + /// The view's width. + public let width: CGFloat + + /// The view's height. + public let height: CGFloat + + /// Creates dimensions from whole terminal cells. + init(cellWidth: Int, cellHeight: Int) { + self.width = CGFloat(cellWidth) + self.height = CGFloat(cellHeight) + } + + /// Returns the value of the given horizontal guide. + public subscript(guide: HorizontalAlignment) -> CGFloat { + guide.id.defaultValue(in: self) + } + + /// Returns the value of the given vertical guide. + public subscript(guide: VerticalAlignment) -> CGFloat { + guide.id.defaultValue(in: self) + } +} + // MARK: - Horizontal Alignment -/// Horizontal alignment for VStack and similar containers. -public enum HorizontalAlignment: Sendable { - /// Align to the leading (left) edge. - case leading +/// An alignment position along the horizontal axis. +/// +/// Extensible like SwiftUI's `HorizontalAlignment`: construct custom +/// guides from an ``AlignmentID`` conforming type. +public struct HorizontalAlignment: Equatable, Sendable { + /// The alignment guide's defining type. + let id: any AlignmentID.Type - /// Align to the center. - case center + /// Creates a custom horizontal alignment of the given identity. + /// + /// - Parameter id: The type defining the guide's default value. + public init(_ id: any AlignmentID.Type) { + self.id = id + } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.id == rhs.id + } + + /// Aligns to the leading (left) edge. + public static let leading = Self(LeadingID.self) + + /// Aligns to the horizontal center. + public static let center = Self(HorizontalCenterID.self) - /// Align to the trailing (right) edge. - case trailing + /// Aligns to the trailing (right) edge. + public static let trailing = Self(TrailingID.self) + + private enum LeadingID: AlignmentID { + static func defaultValue(in context: ViewDimensions) -> CGFloat { 0 } + } + + private enum HorizontalCenterID: AlignmentID { + static func defaultValue(in context: ViewDimensions) -> CGFloat { context.width / 2 } + } + + private enum TrailingID: AlignmentID { + static func defaultValue(in context: ViewDimensions) -> CGFloat { context.width } + } } // MARK: - Vertical Alignment -/// Vertical alignment for HStack and similar containers. -public enum VerticalAlignment: Sendable { - /// Align to the top edge. - case top +/// An alignment position along the vertical axis. +/// +/// Extensible like SwiftUI's `VerticalAlignment`: construct custom +/// guides from an ``AlignmentID`` conforming type. +public struct VerticalAlignment: Equatable, Sendable { + /// The alignment guide's defining type. + let id: any AlignmentID.Type - /// Align to the vertical center. - case center + /// Creates a custom vertical alignment of the given identity. + /// + /// - Parameter id: The type defining the guide's default value. + public init(_ id: any AlignmentID.Type) { + self.id = id + } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.id == rhs.id + } + + /// Aligns to the top edge. + public static let top = Self(TopID.self) + + /// Aligns to the vertical center. + public static let center = Self(VerticalCenterID.self) + + /// Aligns to the bottom edge. + public static let bottom = Self(BottomID.self) - /// Align to the bottom edge. - case bottom + private enum TopID: AlignmentID { + static func defaultValue(in context: ViewDimensions) -> CGFloat { 0 } + } + + private enum VerticalCenterID: AlignmentID { + static func defaultValue(in context: ViewDimensions) -> CGFloat { context.height / 2 } + } + + private enum BottomID: AlignmentID { + static func defaultValue(in context: ViewDimensions) -> CGFloat { context.height } + } +} + +// MARK: - Cell Resolution + +extension HorizontalAlignment { + /// Returns the child's leading cell offset inside a container. + /// + /// Aligns the child's guide with the container's guide and quantizes + /// with `TerminalGeometry` so macOS and Linux produce identical + /// layouts. The result is clamped to keep the child inside the + /// container. + /// + /// - Parameters: + /// - childWidth: The child's width in cells. + /// - containerWidth: The container's width in cells. + /// - Returns: The leading offset in whole cells. + package func cellOffset(childWidth: Int, containerWidth: Int) -> Int { + let containerGuide = id.defaultValue( + in: ViewDimensions(cellWidth: containerWidth, cellHeight: 0) + ) + let childGuide = id.defaultValue( + in: ViewDimensions(cellWidth: childWidth, cellHeight: 0) + ) + let offset = TerminalGeometry.alignmentOffset(containerGuide - childGuide) + return max(0, min(offset, containerWidth - childWidth)) + } +} + +extension VerticalAlignment { + /// Returns the child's top cell offset inside a container. + /// + /// See ``HorizontalAlignment/cellOffset(childWidth:containerWidth:)`` + /// for the resolution and quantization rules. + /// + /// - Parameters: + /// - childHeight: The child's height in cells. + /// - containerHeight: The container's height in cells. + /// - Returns: The top offset in whole cells. + package func cellOffset(childHeight: Int, containerHeight: Int) -> Int { + let containerGuide = id.defaultValue( + in: ViewDimensions(cellWidth: 0, cellHeight: containerHeight) + ) + let childGuide = id.defaultValue( + in: ViewDimensions(cellWidth: 0, cellHeight: childHeight) + ) + let offset = TerminalGeometry.alignmentOffset(containerGuide - childGuide) + return max(0, min(offset, containerHeight - childHeight)) + } } // MARK: - Combined Alignment -/// Combined alignment for both axes. +/// An alignment in both dimensions. public struct Alignment: Sendable, Equatable { /// The horizontal component. - public let horizontal: HorizontalAlignment + public var horizontal: HorizontalAlignment /// The vertical component. - public let vertical: VerticalAlignment + public var vertical: VerticalAlignment /// Creates a combined alignment. /// diff --git a/Sources/TUIkit/Views/Card.swift b/Sources/TUIkit/Views/Card.swift index 220111d7..8aca91da 100644 --- a/Sources/TUIkit/Views/Card.swift +++ b/Sources/TUIkit/Views/Card.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + /// A padded, optionally titled container with background support. /// /// `Card` is the most feature-rich of the simple containers. It combines diff --git a/Sources/TUIkit/Views/ContainerView.swift b/Sources/TUIkit/Views/ContainerView.swift index 70f7787d..027f985f 100644 --- a/Sources/TUIkit/Views/ContainerView.swift +++ b/Sources/TUIkit/Views/ContainerView.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + // MARK: - Container Config /// Shared visual configuration for container-type views. @@ -366,7 +368,8 @@ private struct _ContainerViewCore: View, Renderable let footerBuffer: FrameBuffer? if let footerView = footer { var footerContext = innerContext - footerContext.availableWidth = innerWidth - footerPadding.leading - footerPadding.trailing + let footerCells = footerPadding.cellInsets + footerContext.availableWidth = innerWidth - footerCells.leading - footerCells.trailing let paddedFooter = footerView.padding(footerPadding) footerBuffer = TUIkit.renderToBuffer(paddedFooter, context: footerContext) } else { diff --git a/Sources/TUIkit/Views/Dialog.swift b/Sources/TUIkit/Views/Dialog.swift index 71c53057..042af9da 100644 --- a/Sources/TUIkit/Views/Dialog.swift +++ b/Sources/TUIkit/Views/Dialog.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + /// A modal dialog view with a title, customizable content, and optional footer. /// /// `Dialog` is more flexible than `Alert` — it accepts any content, diff --git a/Sources/TUIkit/Views/GeometryReader.swift b/Sources/TUIkit/Views/GeometryReader.swift new file mode 100644 index 00000000..e9424a30 --- /dev/null +++ b/Sources/TUIkit/Views/GeometryReader.swift @@ -0,0 +1,110 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// GeometryReader.swift +// +// Created by LAYERED.work +// License: MIT + +import Foundation + +// MARK: - Axis + +/// The horizontal or vertical dimension in a 2D coordinate system. +public enum Axis: Int8, CaseIterable, Sendable { + /// The horizontal dimension. + case horizontal + + /// The vertical dimension. + case vertical + + /// An efficient set of axes. + public struct Set: OptionSet, Sendable { + /// The raw bitmask value for this axis set. + public let rawValue: Int8 + + /// Creates an axis set from a raw bitmask value. + /// + /// - Parameter rawValue: The bitmask value. + public init(rawValue: Int8) { + self.rawValue = rawValue + } + + /// The horizontal axis. + public static let horizontal = Self(rawValue: 1 << 0) + + /// The vertical axis. + public static let vertical = Self(rawValue: 1 << 1) + } +} + +// MARK: - Geometry Proxy + +/// A proxy for access to the size of the container view. +/// +/// Terminal adaptation of SwiftUI's `GeometryProxy`: the size reports +/// whole cells as `CGFloat`. Coordinate-space anchors and safe-area +/// insets have no terminal meaning and are omitted per the +/// compatibility manifest. +public struct GeometryProxy: Sendable { + /// The size of the container view in cells. + public let size: CGSize + + /// Creates a proxy for the given cell dimensions. + init(cellWidth: Int, cellHeight: Int) { + self.size = CGSize(width: CGFloat(cellWidth), height: CGFloat(cellHeight)) + } +} + +// MARK: - Geometry Reader + +/// A container view that defines its content as a function of its own +/// size. +/// +/// Like SwiftUI, the reader expands to fill all of its proposed space +/// and aligns content to the top-leading corner: +/// +/// ```swift +/// GeometryReader { proxy in +/// Text("width: \(Int(proxy.size.width))") +/// } +/// ``` +public struct GeometryReader: View { + /// Produces the content for the resolved size. + public var content: (GeometryProxy) -> Content + + /// Creates a geometry reader with the given view builder. + /// + /// - Parameter content: A builder receiving the container's geometry. + public init(@ViewBuilder content: @escaping (GeometryProxy) -> Content) { + self.content = content + } + + /// Never called — rendering is handled by `Renderable` conformance. + public var body: Never { + fatalError("GeometryReader renders via Renderable") + } +} + +// MARK: - Rendering + +extension GeometryReader: Renderable { + public func renderToBuffer(context: RenderContext) -> FrameBuffer { + let proxy = GeometryProxy( + cellWidth: context.availableWidth, + cellHeight: context.availableHeight + ) + let resolved = content(proxy) + let buffer = TUIkit.renderToBuffer( + resolved, + context: context.withChildIdentity(type: Content.self) + ) + + // Expand to the full proposed space with top-leading content, + // matching SwiftUI's geometry reader behavior. + var lines = buffer.lines + let width = max(buffer.width, context.availableWidth) + while lines.count < context.availableHeight { + lines.append("") + } + return FrameBuffer(lines: lines, width: width) + } +} diff --git a/Sources/TUIkit/Views/HStack.swift b/Sources/TUIkit/Views/HStack.swift index bed324ed..9d8ce9b8 100644 --- a/Sources/TUIkit/Views/HStack.swift +++ b/Sources/TUIkit/Views/HStack.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + // MARK: - HStack /// A view that arranges its children horizontally. @@ -31,8 +33,8 @@ public struct HStack: View { /// The vertical alignment of the children. public let alignment: VerticalAlignment - /// The horizontal spacing between children. - public let spacing: Int + /// The horizontal spacing between children, in cells. + let spacing: Int /// The content of the stack. public let content: Content @@ -41,15 +43,16 @@ public struct HStack: View { /// /// - Parameters: /// - alignment: The vertical alignment of children (default: .center). - /// - spacing: The spacing between children in characters (default: 1). + /// - spacing: The spacing between children, or `nil` for the + /// one-character terminal default. Quantized via `TerminalGeometry`. /// - content: A ViewBuilder that defines the children. public init( alignment: VerticalAlignment = .center, - spacing: Int = 1, + spacing: CGFloat? = nil, @ViewBuilder content: () -> Content ) { self.alignment = alignment - self.spacing = spacing + self.spacing = TerminalGeometry.spacing(spacing, default: 1) self.content = content() } diff --git a/Sources/TUIkit/Views/Layout.swift b/Sources/TUIkit/Views/Layout.swift new file mode 100644 index 00000000..c9783fd3 --- /dev/null +++ b/Sources/TUIkit/Views/Layout.swift @@ -0,0 +1,331 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// Layout.swift +// +// Created by LAYERED.work +// License: MIT + +import Foundation + +#if canImport(CoreGraphics) + import CoreGraphics +#endif + +// MARK: - Proposed View Size + +/// A proposal for the size of a view. +/// +/// Matches SwiftUI's shape: `nil` components ask for the ideal size. +/// The renderer quantizes accepted proposals to whole cells through +/// `TerminalGeometry`. +public struct ProposedViewSize: Equatable, Sendable { + /// The proposed width, or `nil` for the ideal width. + public var width: CGFloat? + + /// The proposed height, or `nil` for the ideal height. + public var height: CGFloat? + + /// Creates a proposal from optional dimensions. + public init(width: CGFloat?, height: CGFloat?) { + self.width = width + self.height = height + } + + /// Creates a proposal from a concrete size. + public init(_ size: CGSize) { + self.init(width: size.width, height: size.height) + } + + /// The proposal that asks for the ideal size in both dimensions. + public static let unspecified = Self(width: nil, height: nil) + + /// The proposal for zero size. + public static let zero = Self(width: 0, height: 0) + + /// The proposal for the maximum size. + public static let infinity = Self(width: .infinity, height: .infinity) +} + +// MARK: - Layout Subview + +/// A proxy for one child view of a custom layout. +/// +/// Use the proxy to measure the child and to place it inside the +/// layout's bounds. Placement coordinates quantize to whole cells. +public struct LayoutSubview { + /// Measures the child under the given proposal. + let measureChild: (ProposedViewSize) -> CGSize + + /// Records the child's placement. + let placeChild: (CGPoint, ProposedViewSize) -> Void + + /// Returns the size the child needs under the given proposal. + /// + /// - Parameter proposal: The size proposal for the child. + /// - Returns: The child's size in cells. + public func sizeThatFits(_ proposal: ProposedViewSize) -> CGSize { + measureChild(proposal) + } + + /// Places the child at a position with a sizing proposal. + /// + /// The terminal adaptation anchors placements at the top-leading + /// corner; positions quantize to whole cells. + /// + /// - Parameters: + /// - position: The top-leading position inside the layout bounds. + /// - proposal: The size proposal the child renders with. + public func place(at position: CGPoint, proposal: ProposedViewSize) { + placeChild(position, proposal) + } +} + +/// The collection of subview proxies handed to a custom layout. +public struct LayoutSubviews: RandomAccessCollection { + /// The wrapped proxies. + let subviews: [LayoutSubview] + + public var startIndex: Int { subviews.startIndex } + public var endIndex: Int { subviews.endIndex } + + public subscript(position: Int) -> LayoutSubview { + subviews[position] + } +} + +// MARK: - Layout Protocol + +/// A type that defines the geometry of a collection of views. +/// +/// Terminal adaptation of SwiftUI's `Layout`: implement +/// ``sizeThatFits(proposal:subviews:cache:)`` and +/// ``placeSubviews(in:proposal:subviews:cache:)``, then apply the layout +/// like a container view: +/// +/// ```swift +/// struct Columns: Layout { … } +/// +/// Columns { +/// Text("left") +/// Text("right") +/// } +/// ``` +@MainActor +public protocol Layout { + /// Cached values shared between sizing and placement. + associatedtype Cache = Void + + /// A collection of subview proxies. + typealias Subviews = LayoutSubviews + + /// Creates the layout's cache. + /// + /// - Parameter subviews: The layout's subview proxies. + func makeCache(subviews: Subviews) -> Cache + + /// Returns the size the layout container needs. + /// + /// - Parameters: + /// - proposal: The size proposed to the container. + /// - subviews: The subview proxies to measure. + /// - cache: The layout's cache. + /// - Returns: The container size in cells. + func sizeThatFits( + proposal: ProposedViewSize, + subviews: Subviews, + cache: inout Cache + ) -> CGSize + + /// Places the subviews inside the container bounds. + /// + /// - Parameters: + /// - bounds: The container's bounds in cells (origin at zero). + /// - proposal: The size proposed to the container. + /// - subviews: The subview proxies to place. + /// - cache: The layout's cache. + func placeSubviews( + in bounds: CGRect, + proposal: ProposedViewSize, + subviews: Subviews, + cache: inout Cache + ) +} + +extension Layout where Cache == Void { + /// Default: layouts without shared state need no cache. + public func makeCache(subviews: Subviews) { + () + } +} + +extension Layout { + /// Applies the layout to the given content, like a container view. + /// + /// - Parameter content: The children to lay out. + /// - Returns: A view arranging the children with this layout. + public func callAsFunction( + @ViewBuilder _ content: () -> Content + ) -> some View { + LayoutHostView(layout: self, content: content()) + } +} + +// MARK: - AnyLayout + +/// A type-erased instance of the layout protocol. +/// +/// Use `AnyLayout` to switch between layouts while preserving the +/// container's position in the view hierarchy: +/// +/// ```swift +/// let layout: AnyLayout = compact ? AnyLayout(Rows()) : AnyLayout(Columns()) +/// layout { content } +/// ``` +public struct AnyLayout: Layout { + public typealias Cache = Any + + /// Erased cache factory. + private let makeCacheErased: (Subviews) -> Any + + /// Erased sizing step. + private let sizeThatFitsErased: (ProposedViewSize, Subviews, inout Any) -> CGSize + + /// Erased placement step. + private let placeSubviewsErased: (CGRect, ProposedViewSize, Subviews, inout Any) -> Void + + /// Creates a type-erased layout wrapping the given instance. + /// + /// - Parameter layout: The layout to erase. + public init(_ layout: L) { + self.makeCacheErased = { subviews in + layout.makeCache(subviews: subviews) + } + self.sizeThatFitsErased = { proposal, subviews, cache in + guard var typed = cache as? L.Cache else { + var fresh = layout.makeCache(subviews: subviews) + let size = layout.sizeThatFits(proposal: proposal, subviews: subviews, cache: &fresh) + cache = fresh + return size + } + let size = layout.sizeThatFits(proposal: proposal, subviews: subviews, cache: &typed) + cache = typed + return size + } + self.placeSubviewsErased = { bounds, proposal, subviews, cache in + guard var typed = cache as? L.Cache else { return } + layout.placeSubviews(in: bounds, proposal: proposal, subviews: subviews, cache: &typed) + cache = typed + } + } + + public func makeCache(subviews: Subviews) -> Any { + makeCacheErased(subviews) + } + + public func sizeThatFits( + proposal: ProposedViewSize, + subviews: Subviews, + cache: inout Any + ) -> CGSize { + sizeThatFitsErased(proposal, subviews, &cache) + } + + public func placeSubviews( + in bounds: CGRect, + proposal: ProposedViewSize, + subviews: Subviews, + cache: inout Any + ) { + placeSubviewsErased(bounds, proposal, subviews, &cache) + } +} + +// MARK: - Placement Store + +/// One recorded placement per placed child of a custom layout. +private final class LayoutPlacementStore { + /// Recorded placements keyed by child index. + var placements: [Int: (position: CGPoint, proposal: ProposedViewSize)] = [:] +} + +// MARK: - Layout Host + +/// Renders a custom layout's children at their placed positions. +struct LayoutHostView: View { + /// The layout arranging the children. + let layout: L + + /// The children to arrange. + let content: Content + + /// Never called — rendering is handled by `Renderable` conformance. + var body: Never { + fatalError("LayoutHostView renders via Renderable") + } +} + +extension LayoutHostView: Renderable { + func renderToBuffer(context: RenderContext) -> FrameBuffer { + let children = resolveChildViews(from: content, context: context) + guard !children.isEmpty else { return FrameBuffer() } + + let store = LayoutPlacementStore() + + func quantized(_ proposal: ProposedViewSize) -> ProposedSize { + ProposedSize( + width: proposal.width.map { max(0, TerminalGeometry.cells($0)) }, + height: proposal.height.map { max(0, TerminalGeometry.cells($0)) } + ) + } + + let proxies = children.enumerated().map { index, child in + LayoutSubview( + measureChild: { proposal in + let size = child.measure(proposal: quantized(proposal), context: context) + return CGSize(width: CGFloat(size.width), height: CGFloat(size.height)) + }, + placeChild: { position, proposal in + store.placements[index] = (position, proposal) + } + ) + } + let subviews = LayoutSubviews(subviews: proxies) + + var cache = layout.makeCache(subviews: subviews) + let proposal = ProposedViewSize( + width: CGFloat(context.availableWidth), + height: CGFloat(context.availableHeight) + ) + let size = layout.sizeThatFits(proposal: proposal, subviews: subviews, cache: &cache) + let containerWidth = min(max(0, TerminalGeometry.cells(size.width)), context.availableWidth) + let containerHeight = min(max(0, TerminalGeometry.cells(size.height)), context.availableHeight) + + let bounds = CGRect( + origin: .zero, + size: CGSize(width: CGFloat(containerWidth), height: CGFloat(containerHeight)) + ) + layout.placeSubviews(in: bounds, proposal: proposal, subviews: subviews, cache: &cache) + + var result = FrameBuffer( + lines: Array(repeating: "", count: containerHeight), + width: containerWidth + ) + for (index, child) in children.enumerated() { + guard let placement = store.placements[index] else { continue } + let childProposal = quantized(placement.proposal) + let measured = child.measure(proposal: childProposal, context: context) + let buffer = child.render( + width: childProposal.width ?? measured.width, + height: childProposal.height ?? measured.height, + context: context + ) + result = result.composited( + with: buffer, + at: ( + x: TerminalGeometry.alignmentOffset(placement.position.x), + y: TerminalGeometry.alignmentOffset(placement.position.y) + ) + ) + } + return result + } +} diff --git a/Sources/TUIkit/Views/LazyStacks.swift b/Sources/TUIkit/Views/LazyStacks.swift index 85efc3a1..6f67fb7e 100644 --- a/Sources/TUIkit/Views/LazyStacks.swift +++ b/Sources/TUIkit/Views/LazyStacks.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + // MARK: - LazyVStack /// A view that arranges its children in a line that grows vertically, @@ -35,8 +37,11 @@ public struct LazyVStack: View { /// The horizontal alignment of the children. public let alignment: HorizontalAlignment - /// The vertical spacing between children. - public let spacing: Int + /// The vertical spacing between children, in cells. + let spacing: Int + + /// The kinds of child views that pin to the visible bounds. + let pinnedViews: PinnedScrollableViews /// The content of the stack. public let content: Content @@ -45,15 +50,21 @@ public struct LazyVStack: View { /// /// - Parameters: /// - alignment: The horizontal alignment of children (default: .center). - /// - spacing: The spacing between children in lines (default: 0). + /// - spacing: The spacing between children, or `nil` for the + /// zero-line terminal default. Quantized via `TerminalGeometry`. + /// - pinnedViews: The kinds of child views that pin to the visible + /// bounds. Accepted for SwiftUI parity; pinning takes effect with + /// true viewport-driven laziness (issue #25). /// - content: A ViewBuilder that defines the children. public init( alignment: HorizontalAlignment = .center, - spacing: Int = 0, + spacing: CGFloat? = nil, + pinnedViews: PinnedScrollableViews = .init(), @ViewBuilder content: () -> Content ) { self.alignment = alignment - self.spacing = spacing + self.spacing = TerminalGeometry.spacing(spacing, default: 0) + self.pinnedViews = pinnedViews self.content = content() } @@ -132,15 +143,10 @@ private struct _LazyVStackCore: View, Renderable { var alignedLines: [String] = [] - let bufferOffset: Int - switch alignment { - case .leading: - bufferOffset = 0 - case .center: - bufferOffset = (width - buffer.width) / 2 - case .trailing: - bufferOffset = width - buffer.width - } + let bufferOffset = alignment.cellOffset( + childWidth: buffer.width, + containerWidth: width + ) let leftPadding = String(repeating: " ", count: bufferOffset) let rightPaddingCount = width - bufferOffset - buffer.width @@ -186,8 +192,11 @@ public struct LazyHStack: View { /// The vertical alignment of the children. public let alignment: VerticalAlignment - /// The horizontal spacing between children. - public let spacing: Int + /// The horizontal spacing between children, in cells. + let spacing: Int + + /// The kinds of child views that pin to the visible bounds. + let pinnedViews: PinnedScrollableViews /// The content of the stack. public let content: Content @@ -196,15 +205,21 @@ public struct LazyHStack: View { /// /// - Parameters: /// - alignment: The vertical alignment of children (default: .center). - /// - spacing: The spacing between children in characters (default: 1). + /// - spacing: The spacing between children, or `nil` for the + /// one-character terminal default. Quantized via `TerminalGeometry`. + /// - pinnedViews: The kinds of child views that pin to the visible + /// bounds. Accepted for SwiftUI parity; pinning takes effect with + /// true viewport-driven laziness (issue #25). /// - content: A ViewBuilder that defines the children. public init( alignment: VerticalAlignment = .center, - spacing: Int = 1, + spacing: CGFloat? = nil, + pinnedViews: PinnedScrollableViews = .init(), @ViewBuilder content: () -> Content ) { self.alignment = alignment - self.spacing = spacing + self.spacing = TerminalGeometry.spacing(spacing, default: 1) + self.pinnedViews = pinnedViews self.content = content() } diff --git a/Sources/TUIkit/Views/Panel.swift b/Sources/TUIkit/Views/Panel.swift index 2b15a17c..f141bfef 100644 --- a/Sources/TUIkit/Views/Panel.swift +++ b/Sources/TUIkit/Views/Panel.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + /// A labeled container — always has a title, optionally a footer. /// /// `Panel` groups content under a visible heading. The title is mandatory, diff --git a/Sources/TUIkit/Views/PinnedScrollableViews.swift b/Sources/TUIkit/Views/PinnedScrollableViews.swift new file mode 100644 index 00000000..bcad0c37 --- /dev/null +++ b/Sources/TUIkit/Views/PinnedScrollableViews.swift @@ -0,0 +1,31 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// PinnedScrollableViews.swift +// +// Created by LAYERED.work +// License: MIT + +// MARK: - Pinned Scrollable Views + +/// The kinds of scrollable-view child content that pin to the visible +/// bounds of a lazy stack. +/// +/// Matches SwiftUI's option set. Lazy stacks accept the option for API +/// parity today; pinning takes visual effect once true viewport-driven +/// laziness lands (issue #25). +public struct PinnedScrollableViews: OptionSet, Sendable { + /// The raw bitmask value for this option set. + public let rawValue: UInt32 + + /// Creates an option set from a raw bitmask value. + /// + /// - Parameter rawValue: The bitmask value. + public init(rawValue: UInt32) { + self.rawValue = rawValue + } + + /// The header views of each section stay pinned. + public static let sectionHeaders = Self(rawValue: 1 << 0) + + /// The footer views of each section stay pinned. + public static let sectionFooters = Self(rawValue: 1 << 1) +} diff --git a/Sources/TUIkit/Views/Spacer.swift b/Sources/TUIkit/Views/Spacer.swift index 23b44f2d..06716eef 100644 --- a/Sources/TUIkit/Views/Spacer.swift +++ b/Sources/TUIkit/Views/Spacer.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + /// A flexible spacer that fills available space. /// /// `Spacer` expands along the main axis of its container @@ -36,9 +38,10 @@ public struct Spacer: View, Equatable { /// Creates a spacer with optional minimum length. /// /// - Parameter minLength: The minimum length. If nil, the - /// spacer expands as much as possible. - public init(minLength: Int? = nil) { - self.minLength = minLength + /// spacer expands as much as possible. Quantized via + /// `TerminalGeometry`. + public init(minLength: CGFloat? = nil) { + self.minLength = minLength.map { max(0, TerminalGeometry.cells($0)) } } public var body: Never { diff --git a/Sources/TUIkit/Views/Table.swift b/Sources/TUIkit/Views/Table.swift index e95123f0..de8afb52 100644 --- a/Sources/TUIkit/Views/Table.swift +++ b/Sources/TUIkit/Views/Table.swift @@ -429,16 +429,9 @@ private struct _TableCore: View, Renderable wher let visibleLength = text.strippedLength let padding = max(0, width - visibleLength) - switch alignment { - case .leading: - return text + String(repeating: " ", count: padding) - case .center: - let leftPad = padding / 2 - let rightPad = padding - leftPad - return String(repeating: " ", count: leftPad) + text + String(repeating: " ", count: rightPad) - case .trailing: - return String(repeating: " ", count: padding) + text - } + let leftPad = alignment.cellOffset(childWidth: visibleLength, containerWidth: width) + let rightPad = padding - leftPad + return String(repeating: " ", count: leftPad) + text + String(repeating: " ", count: rightPad) } } diff --git a/Sources/TUIkit/Views/TerminalGeometry.swift b/Sources/TUIkit/Views/TerminalGeometry.swift new file mode 100644 index 00000000..594d3d1d --- /dev/null +++ b/Sources/TUIkit/Views/TerminalGeometry.swift @@ -0,0 +1,68 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// TerminalGeometry.swift +// +// Created by LAYERED.work +// License: MIT + +import Foundation + +// MARK: - Terminal Geometry + +/// The single quantization policy from SwiftUI's continuous geometry to +/// whole terminal cells. +/// +/// All public layout APIs accept `CGFloat` like SwiftUI; the renderer +/// operates on cells. Every conversion runs through this policy so macOS +/// and Linux produce identical layouts: +/// +/// | Input | Result | +/// |----------------------|--------------------------------| +/// | Fractional values | Rounded half away from zero | +/// | Negative values | Clamped to 0 where sizes/spacing are consumed | +/// | `.infinity` | `Int.max` (fills available space) | +/// | NaN | 0 | +/// | Beyond `Int` range | Clamped to `Int.min`/`Int.max` | +package enum TerminalGeometry { + /// Converts a continuous value to whole cells. + /// + /// - Parameter value: The continuous value. + /// - Returns: The deterministic cell count. + package static func cells(_ value: CGFloat) -> Int { + if value.isNaN { return 0 } + if value == .infinity { return .max } + if value == -.infinity { return .min } + let rounded = value.rounded(.toNearestOrAwayFromZero) + if rounded >= CGFloat(Int.max) { return .max } + if rounded <= CGFloat(Int.min) { return .min } + return Int(rounded) + } + + /// Converts an alignment offset to whole cells. + /// + /// Alignment offsets round toward negative infinity (floor): centering + /// a 1-cell child in a 4-cell container yields offset 1, matching the + /// established terminal rendering. Sizes and spacing use ``cells(_:)``. + /// + /// - Parameter value: The continuous offset. + /// - Returns: The deterministic cell offset. + package static func alignmentOffset(_ value: CGFloat) -> Int { + if value.isNaN { return 0 } + if value == .infinity { return .max } + if value == -.infinity { return .min } + let rounded = value.rounded(.down) + if rounded >= CGFloat(Int.max) { return .max } + if rounded <= CGFloat(Int.min) { return .min } + return Int(rounded) + } + + /// Converts an optional spacing value to non-negative cells. + /// + /// - Parameters: + /// - value: The continuous spacing, or `nil` for the default. + /// - default: The default cell spacing when `value` is `nil`. + /// - Returns: The deterministic, non-negative cell spacing. + package static func spacing(_ value: CGFloat?, default defaultCells: Int) -> Int { + guard let value else { return defaultCells } + return max(0, cells(value)) + } +} diff --git a/Sources/TUIkit/Views/VStack.swift b/Sources/TUIkit/Views/VStack.swift index d134c9fb..1299c493 100644 --- a/Sources/TUIkit/Views/VStack.swift +++ b/Sources/TUIkit/Views/VStack.swift @@ -4,6 +4,8 @@ // Created by LAYERED.work // License: MIT +import Foundation + // MARK: - VStack /// A view that arranges its children vertically. @@ -33,8 +35,8 @@ public struct VStack: View { /// The horizontal alignment of the children. public let alignment: HorizontalAlignment - /// The vertical spacing between children. - public let spacing: Int + /// The vertical spacing between children, in cells. + let spacing: Int /// The content of the stack. public let content: Content @@ -43,15 +45,16 @@ public struct VStack: View { /// /// - Parameters: /// - alignment: The horizontal alignment of children (default: .center, like SwiftUI). - /// - spacing: The spacing between children in lines (default: 0). + /// - spacing: The spacing between children, or `nil` for the + /// zero-line terminal default. Quantized via `TerminalGeometry`. /// - content: A ViewBuilder that defines the children. public init( alignment: HorizontalAlignment = .center, - spacing: Int = 0, + spacing: CGFloat? = nil, @ViewBuilder content: () -> Content ) { self.alignment = alignment - self.spacing = spacing + self.spacing = TerminalGeometry.spacing(spacing, default: 0) self.content = content() } @@ -172,15 +175,10 @@ private struct _VStackCore: View, Renderable, Layoutable { var alignedLines: [String] = [] - let bufferOffset: Int - switch alignment { - case .leading: - bufferOffset = 0 - case .center: - bufferOffset = (width - buffer.width) / 2 - case .trailing: - bufferOffset = width - buffer.width - } + let bufferOffset = alignment.cellOffset( + childWidth: buffer.width, + containerWidth: width + ) let leftPadding = String(repeating: " ", count: bufferOffset) let rightPaddingCount = width - bufferOffset - buffer.width diff --git a/Sources/TUIkit/Views/ViewThatFits.swift b/Sources/TUIkit/Views/ViewThatFits.swift new file mode 100644 index 00000000..8b343650 --- /dev/null +++ b/Sources/TUIkit/Views/ViewThatFits.swift @@ -0,0 +1,102 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// ViewThatFits.swift +// +// Created by LAYERED.work +// License: MIT + +import Foundation + +// MARK: - View That Fits + +/// A view that adapts to the available space by providing the first child +/// view that fits. +/// +/// Declare candidates from largest to smallest; the first child whose +/// measured size fits the proposed space in the constrained axes renders. +/// When none fits, the last child renders: +/// +/// ```swift +/// ViewThatFits { +/// Text("A verbose description of the state") +/// Text("Short state") +/// Text("S") +/// } +/// ``` +public struct ViewThatFits: View { + /// The axes the candidates must fit in. + let axes: Axis.Set + + /// The candidate views, largest first. + let content: Content + + /// Creates a view that adapts to the available space. + /// + /// - Parameters: + /// - axes: The axes the child views must fit in (default: both). + /// - content: The candidate views, ordered largest to smallest. + public init( + in axes: Axis.Set = [.horizontal, .vertical], + @ViewBuilder content: () -> Content + ) { + self.axes = axes + self.content = content() + } + + /// Never called — rendering is handled by `Renderable` conformance. + public var body: Never { + fatalError("ViewThatFits renders via Renderable") + } + + /// The probe extent used to discover a candidate's ideal size. + /// + /// Wide enough for any realistic terminal content while keeping + /// measurement buffers bounded and deterministic on both platforms. + static var idealProbeCells: Int { 1024 } +} + +// MARK: - Rendering + +extension ViewThatFits: Renderable { + public func renderToBuffer(context: RenderContext) -> FrameBuffer { + let children = resolveChildViews(from: content, context: context) + guard !children.isEmpty else { return FrameBuffer() } + + // Measure against a generous probe in the constrained axes: + // adaptive children (like truncating text) would otherwise shrink + // to the proposal and always "fit". The ideal size decides whether + // a candidate fits the actually available space. + let probeWidth = axes.contains(.horizontal) + ? max(context.availableWidth, Self.idealProbeCells) + : context.availableWidth + let probeHeight = axes.contains(.vertical) + ? max(context.availableHeight, Self.idealProbeCells) + : context.availableHeight + var measureContext = context + measureContext.availableWidth = probeWidth + measureContext.availableHeight = probeHeight + let proposal = ProposedSize(width: probeWidth, height: probeHeight) + + for child in children { + let size = child.measure(proposal: proposal, context: measureContext) + + let fitsHorizontally = !axes.contains(.horizontal) + || size.width <= context.availableWidth + let fitsVertically = !axes.contains(.vertical) + || size.height <= context.availableHeight + + if fitsHorizontally && fitsVertically { + return child.render(width: size.width, height: size.height, context: context) + } + } + + // Nothing fits: fall back to the smallest (last) candidate, + // clipped to the available space. + let fallback = children[children.count - 1] + let size = fallback.measure(proposal: proposal, context: measureContext) + return fallback.render( + width: min(size.width, context.availableWidth), + height: min(size.height, context.availableHeight), + context: context + ) + } +} diff --git a/Sources/TUIkit/Views/ZStack.swift b/Sources/TUIkit/Views/ZStack.swift index 7e3f8a47..af70c3d1 100644 --- a/Sources/TUIkit/Views/ZStack.swift +++ b/Sources/TUIkit/Views/ZStack.swift @@ -87,11 +87,7 @@ private struct _ZStackCore: View, Renderable { content: Int, alignment: HorizontalAlignment ) -> Int { - switch alignment { - case .leading: return 0 - case .center: return max(0, (available - content) / 2) - case .trailing: return max(0, available - content) - } + alignment.cellOffset(childWidth: content, containerWidth: available) } private func offset( @@ -99,11 +95,7 @@ private struct _ZStackCore: View, Renderable { content: Int, alignment: VerticalAlignment ) -> Int { - switch alignment { - case .top: return 0 - case .center: return max(0, (available - content) / 2) - case .bottom: return max(0, available - content) - } + alignment.cellOffset(childHeight: content, containerHeight: available) } } diff --git a/Tests/TUIkitTests/AdaptiveLayoutViewTests.swift b/Tests/TUIkitTests/AdaptiveLayoutViewTests.swift new file mode 100644 index 00000000..a0b1c75d --- /dev/null +++ b/Tests/TUIkitTests/AdaptiveLayoutViewTests.swift @@ -0,0 +1,93 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// AdaptiveLayoutViewTests.swift +// +// Created by LAYERED.work +// License: MIT + +import Testing + +@testable import TUIkit + +@MainActor +@Suite("GeometryReader and ViewThatFits") +struct AdaptiveLayoutViewTests { + + /// Creates an isolated render context. + private func testContext(width: Int, height: Int) -> RenderContext { + RenderContext( + availableWidth: width, + availableHeight: height, + tuiContext: TUIContext() + ) + } + + // MARK: - GeometryReader + + @Test("GeometryReader reports the proposed size in cells") + func geometryReaderReportsSize() { + let reader = GeometryReader { proxy in + Text("w:\(Int(proxy.size.width)) h:\(Int(proxy.size.height))") + } + + let buffer = renderToBuffer(reader, context: testContext(width: 24, height: 6)) + + #expect(buffer.lines.first?.stripped == "w:24 h:6") + } + + @Test("GeometryReader expands to fill its proposed space") + func geometryReaderExpands() { + let reader = GeometryReader { _ in Text("x") } + + let buffer = renderToBuffer(reader, context: testContext(width: 10, height: 4)) + + #expect(buffer.height == 4) + #expect(buffer.width == 10) + } + + // MARK: - ViewThatFits + + @Test("ViewThatFits picks the first candidate that fits") + func viewThatFitsPicksFirstFitting() { + let adaptive = ViewThatFits { + Text("this is the very long variant") + Text("medium length") + Text("short") + } + + let wide = renderToBuffer(adaptive, context: testContext(width: 40, height: 3)) + #expect(wide.lines.first?.stripped == "this is the very long variant") + + let medium = renderToBuffer(adaptive, context: testContext(width: 16, height: 3)) + #expect(medium.lines.first?.stripped == "medium length") + + let narrow = renderToBuffer(adaptive, context: testContext(width: 7, height: 3)) + #expect(narrow.lines.first?.stripped == "short") + } + + @Test("ViewThatFits falls back to the last candidate when nothing fits") + func viewThatFitsFallsBack() { + let adaptive = ViewThatFits { + Text("long candidate") + Text("still long") + } + + let buffer = renderToBuffer(adaptive, context: testContext(width: 4, height: 3)) + + #expect(buffer.lines.first?.stripped.hasPrefix("stil") == true) + } + + @Test("ViewThatFits constrained to one axis ignores the other") + func viewThatFitsAxisConstrained() { + let tall = Text("a\nb\nc\nd") + let adaptive = ViewThatFits(in: .horizontal) { + tall + Text("x") + } + + // Height 2 is too small for the 4-line candidate, but only the + // horizontal axis is constrained, so the first candidate wins. + let buffer = renderToBuffer(adaptive, context: testContext(width: 10, height: 2)) + + #expect(buffer.lines.first?.stripped == "a") + } +} diff --git a/Tests/TUIkitTests/AlignmentGuideTests.swift b/Tests/TUIkitTests/AlignmentGuideTests.swift new file mode 100644 index 00000000..1cb02cae --- /dev/null +++ b/Tests/TUIkitTests/AlignmentGuideTests.swift @@ -0,0 +1,78 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// AlignmentGuideTests.swift +// +// Created by LAYERED.work +// License: MIT + +import Foundation +import Testing + +@testable import TUIkit + +@MainActor +@Suite("Alignment Guides") +struct AlignmentGuideTests { + + /// Aligns one third into the available width. + private enum OneThird: AlignmentID { + static func defaultValue(in context: ViewDimensions) -> CGFloat { + context.width / 3 + } + } + + // MARK: - Identity + + @Test("Built-in alignments equate by guide identity") + func builtInAlignmentsEquate() { + #expect(HorizontalAlignment.leading == .leading) + #expect(HorizontalAlignment.leading != .center) + #expect(VerticalAlignment.top != .bottom) + // swiftlint:disable:next identical_operands + #expect(HorizontalAlignment(OneThird.self) == HorizontalAlignment(OneThird.self)) + #expect(HorizontalAlignment(OneThird.self) != .center) + } + + // MARK: - Built-In Offsets + + @Test("Built-in guides keep the established floor-based offsets") + func builtInOffsets() { + #expect(HorizontalAlignment.leading.cellOffset(childWidth: 3, containerWidth: 10) == 0) + #expect(HorizontalAlignment.center.cellOffset(childWidth: 3, containerWidth: 10) == 3) + #expect(HorizontalAlignment.trailing.cellOffset(childWidth: 3, containerWidth: 10) == 7) + #expect(VerticalAlignment.center.cellOffset(childHeight: 1, containerHeight: 4) == 1) + #expect(VerticalAlignment.bottom.cellOffset(childHeight: 2, containerHeight: 5) == 3) + } + + @Test("Oversized children clamp to offset zero") + func oversizedChildrenClamp() { + #expect(HorizontalAlignment.center.cellOffset(childWidth: 12, containerWidth: 10) == 0) + #expect(VerticalAlignment.bottom.cellOffset(childHeight: 9, containerHeight: 4) == 0) + } + + // MARK: - Custom Guides + + @Test("Custom alignment guides resolve deterministically") + func customGuideResolves() { + let guide = HorizontalAlignment(OneThird.self) + + // container/3 - child/3 = (12 - 3) / 3 = 3 + #expect(guide.cellOffset(childWidth: 3, containerWidth: 12) == 3) + // floor((10 - 4) / 3) = 2 + #expect(guide.cellOffset(childWidth: 4, containerWidth: 10) == 2) + } + + // MARK: - Quantization Policy + + @Test("The quantization policy handles degenerate inputs deterministically") + func quantizationPolicy() { + #expect(TerminalGeometry.cells(2.5) == 3) + #expect(TerminalGeometry.cells(-2.5) == -3) + #expect(TerminalGeometry.cells(.infinity) == .max) + #expect(TerminalGeometry.cells(CGFloat.nan) == 0) + #expect(TerminalGeometry.alignmentOffset(2.5) == 2) + #expect(TerminalGeometry.alignmentOffset(-0.5) == -1) + #expect(TerminalGeometry.spacing(nil, default: 1) == 1) + #expect(TerminalGeometry.spacing(-3, default: 1) == 0) + #expect(TerminalGeometry.spacing(2.4, default: 1) == 2) + } +} diff --git a/Tests/TUIkitTests/CustomLayoutTests.swift b/Tests/TUIkitTests/CustomLayoutTests.swift new file mode 100644 index 00000000..991adb09 --- /dev/null +++ b/Tests/TUIkitTests/CustomLayoutTests.swift @@ -0,0 +1,90 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// CustomLayoutTests.swift +// +// Created by LAYERED.work +// License: MIT + +import Foundation +import Testing + +@testable import TUIkit + +@MainActor +@Suite("Custom Layouts") +struct CustomLayoutTests { + + /// Creates an isolated render context. + private func testContext(width: Int, height: Int) -> RenderContext { + RenderContext( + availableWidth: width, + availableHeight: height, + tuiContext: TUIContext() + ) + } + + /// Stacks children diagonally: each child moves one cell right and down. + struct DiagonalLayout: Layout { + func sizeThatFits( + proposal: ProposedViewSize, + subviews: Subviews, + cache: inout Void + ) -> CGSize { + var width: CGFloat = 0 + var height: CGFloat = 0 + for (index, subview) in subviews.enumerated() { + let size = subview.sizeThatFits(.unspecified) + width = max(width, CGFloat(index) + size.width) + height = max(height, CGFloat(index) + size.height) + } + return CGSize(width: width, height: height) + } + + func placeSubviews( + in bounds: CGRect, + proposal: ProposedViewSize, + subviews: Subviews, + cache: inout Void + ) { + for (index, subview) in subviews.enumerated() { + subview.place( + at: CGPoint(x: CGFloat(index), y: CGFloat(index)), + proposal: .unspecified + ) + } + } + } + + @Test("A custom layout places children at its computed positions") + func customLayoutPlacesChildren() { + let layout = DiagonalLayout() + let view = layout { + Text("a") + Text("b") + Text("c") + } + + let rows = renderToBuffer(view, context: testContext(width: 10, height: 5)) + .lines.map(\.stripped) + + #expect(rows.count == 3) + #expect(rows[0].hasPrefix("a")) + #expect(rows[1].hasPrefix(" b")) + #expect(rows[2].hasPrefix(" c")) + } + + @Test("AnyLayout delegates to the wrapped layout") + func anyLayoutDelegates() { + let layout = AnyLayout(DiagonalLayout()) + let view = layout { + Text("x") + Text("y") + } + + let rows = renderToBuffer(view, context: testContext(width: 10, height: 4)) + .lines.map(\.stripped) + + #expect(rows.count == 2) + #expect(rows[0].hasPrefix("x")) + #expect(rows[1].hasPrefix(" y")) + } +} diff --git a/Tests/TUIkitTests/EdgeInsetsTests.swift b/Tests/TUIkitTests/EdgeInsetsTests.swift index 730084f0..daf2257e 100644 --- a/Tests/TUIkitTests/EdgeInsetsTests.swift +++ b/Tests/TUIkitTests/EdgeInsetsTests.swift @@ -46,27 +46,27 @@ struct EdgeInsetsTests { @Suite("Edge Tests") struct EdgeTests { - @Test("Edge.all contains all edges") + @Test("Edge.Set.all contains all edges") func edgeAll() { - #expect(Edge.all.contains(.top)) - #expect(Edge.all.contains(.leading)) - #expect(Edge.all.contains(.bottom)) - #expect(Edge.all.contains(.trailing)) + #expect(Edge.Set.all.contains(.top)) + #expect(Edge.Set.all.contains(.leading)) + #expect(Edge.Set.all.contains(.bottom)) + #expect(Edge.Set.all.contains(.trailing)) } - @Test("Edge.horizontal contains leading and trailing") + @Test("Edge.Set.horizontal contains leading and trailing") func edgeHorizontal() { - #expect(Edge.horizontal.contains(.leading)) - #expect(Edge.horizontal.contains(.trailing)) - #expect(!Edge.horizontal.contains(.top)) - #expect(!Edge.horizontal.contains(.bottom)) + #expect(Edge.Set.horizontal.contains(.leading)) + #expect(Edge.Set.horizontal.contains(.trailing)) + #expect(!Edge.Set.horizontal.contains(.top)) + #expect(!Edge.Set.horizontal.contains(.bottom)) } - @Test("Edge.vertical contains top and bottom") + @Test("Edge.Set.vertical contains top and bottom") func edgeVertical() { - #expect(Edge.vertical.contains(.top)) - #expect(Edge.vertical.contains(.bottom)) - #expect(!Edge.vertical.contains(.leading)) - #expect(!Edge.vertical.contains(.trailing)) + #expect(Edge.Set.vertical.contains(.top)) + #expect(Edge.Set.vertical.contains(.bottom)) + #expect(!Edge.Set.vertical.contains(.leading)) + #expect(!Edge.Set.vertical.contains(.trailing)) } } diff --git a/Tests/TUIkitTests/FrameModifierTests.swift b/Tests/TUIkitTests/FrameModifierTests.swift index 77df4d4a..373d2d53 100644 --- a/Tests/TUIkitTests/FrameModifierTests.swift +++ b/Tests/TUIkitTests/FrameModifierTests.swift @@ -27,7 +27,7 @@ struct FrameModifierTests { content: Text("Hi"), minWidth: nil, idealWidth: nil, - maxWidth: .infinity, + maxWidth: Int.max, minHeight: nil, idealHeight: nil, maxHeight: nil, @@ -45,7 +45,7 @@ struct FrameModifierTests { content: Text("Short"), minWidth: nil, idealWidth: nil, - maxWidth: .fixed(10), + maxWidth: 10, minHeight: nil, idealHeight: nil, maxHeight: nil, @@ -173,7 +173,7 @@ struct FrameModifierTests { maxWidth: nil, minHeight: 5, idealHeight: nil, - maxHeight: .fixed(10), + maxHeight: 10, alignment: .top ) let context = testContext() @@ -192,7 +192,7 @@ struct FrameModifierTests { maxWidth: nil, minHeight: nil, idealHeight: nil, - maxHeight: .infinity, + maxHeight: Int.max, alignment: .top ) var context = testContext() diff --git a/Tests/TUIkitTests/LayoutGoldenTests.swift b/Tests/TUIkitTests/LayoutGoldenTests.swift new file mode 100644 index 00000000..39b65ccf --- /dev/null +++ b/Tests/TUIkitTests/LayoutGoldenTests.swift @@ -0,0 +1,113 @@ +// 🖥️ TUIKit — Terminal UI Kit for Swift +// LayoutGoldenTests.swift +// +// Created by LAYERED.work +// License: MIT + +import Testing + +@testable import TUIkit + +@MainActor +@Suite("Layout Golden Tests") +struct LayoutGoldenTests { + + /// Creates an isolated render context. + private func testContext(width: Int, height: Int) -> RenderContext { + RenderContext( + availableWidth: width, + availableHeight: height, + tuiContext: TUIContext() + ) + } + + /// Renders a view and returns the stripped output rows. + private func rows(_ view: some View, width: Int, height: Int) -> [String] { + renderToBuffer(view, context: testContext(width: width, height: height)) + .lines.map(\.stripped) + } + + // MARK: - ZStack Alignment + + @Test("ZStack honors every corner alignment") + func zstackCornerAlignments() { + let base = Text("aaaa\naaaa\naaaa") + + let topLeading = rows( + ZStack(alignment: .topLeading) { base; Text("X") }, + width: 10, height: 5 + ) + #expect(topLeading[0].hasPrefix("X")) + + let bottomTrailing = rows( + ZStack(alignment: .bottomTrailing) { base; Text("X") }, + width: 10, height: 5 + ) + #expect(bottomTrailing[2].hasSuffix("X")) + + let center = rows( + ZStack(alignment: .center) { base; Text("X") }, + width: 10, height: 5 + ) + #expect(center[1].contains("aXaa")) + } + + // MARK: - Frame Alignment + + @Test("Fixed frames center their content by default like SwiftUI") + func fixedFrameCentersByDefault() { + let framed = rows(Text("ab").frame(width: 6, height: 3), width: 10, height: 5) + + #expect(framed.count == 3) + #expect(framed[0].trimmingCharacters(in: .whitespaces).isEmpty) + #expect(framed[1] == " ab ") + #expect(framed[2].trimmingCharacters(in: .whitespaces).isEmpty) + } + + @Test("Frame alignment places content at the requested corner") + func frameAlignmentCorners() { + let bottomTrailing = rows( + Text("ab").frame(width: 6, height: 3, alignment: .bottomTrailing), + width: 10, height: 5 + ) + #expect(bottomTrailing[2] == " ab") + + let topLeading = rows( + Text("ab").frame(width: 6, height: 3, alignment: .topLeading), + width: 10, height: 5 + ) + #expect(topLeading[0] == "ab ") + } + + // MARK: - Nested Proposal Propagation + + @Test("Nested frames propagate reduced proposals to their children") + func nestedProposalPropagation() { + let nested = rows( + VStack { + Text("wide content here") + .frame(maxWidth: .infinity) + } + .frame(width: 8), + width: 20, height: 4 + ) + + // The inner infinity frame fills the outer 8-cell frame, not the + // 20-cell terminal: every row stays within 8 cells. + #expect(nested.allSatisfy { $0.count <= 8 }) + } + + @Test("Padding reduces the space proposed to flexible children") + func paddingReducesProposals() { + let padded = rows( + Text("x") + .frame(maxWidth: .infinity) + .padding(.horizontal, 2), + width: 12, height: 3 + ) + + // 12 cells minus 2 padding per side leaves an 8-cell fill plus + // the re-added padding spaces. + #expect(padded.contains { $0.count == 12 }) + } +} diff --git a/Tools/APICompatibility/Configuration/CompileContracts/LayoutPositive.swift b/Tools/APICompatibility/Configuration/CompileContracts/LayoutPositive.swift index 89f00052..5830a55b 100644 --- a/Tools/APICompatibility/Configuration/CompileContracts/LayoutPositive.swift +++ b/Tools/APICompatibility/Configuration/CompileContracts/LayoutPositive.swift @@ -1,10 +1,95 @@ +import Foundation import TUIkit +// Representative SwiftUI-style layout declarations that must compile +// unchanged under Swift 6.0: stacks with CGFloat spacing, spacer, +// alignment guides, edges, frames, padding, and the custom layout family. + @MainActor func terminalLayout() -> some View { HStack(alignment: .center, spacing: 1) { Text("Leading") - Spacer() + Spacer(minLength: 2) Text("Trailing") } } + +private enum ThirdGuide: AlignmentID { + static func defaultValue(in context: ViewDimensions) -> CGFloat { + context.width / 3 + } +} + +@MainActor +func alignmentAndFrames() -> some View { + VStack(alignment: HorizontalAlignment(ThirdGuide.self), spacing: 0.5) { + Text("wide") + .frame(minWidth: 4, maxWidth: .infinity, alignment: .center) + Text("padded") + .padding(.horizontal, 2) + .padding(EdgeInsets(top: 1, leading: 0, bottom: 1, trailing: 0)) + } + .frame(width: 24, height: 8) +} + +@MainActor +func lazyPinned() -> some View { + LazyVStack(alignment: .leading, spacing: nil, pinnedViews: [.sectionHeaders]) { + Text("row") + } +} + +@MainActor +func adaptive() -> some View { + ViewThatFits(in: .horizontal) { + Text("wide variant") + Text("narrow") + } +} + +@MainActor +func geometry() -> some View { + GeometryReader { proxy in + Text("w:\(Int(proxy.size.width))") + } +} + +private struct RowLayout: Layout { + func sizeThatFits( + proposal: ProposedViewSize, + subviews: Subviews, + cache: inout Void + ) -> CGSize { + var width: CGFloat = 0 + var height: CGFloat = 0 + for subview in subviews { + let size = subview.sizeThatFits(.unspecified) + width += size.width + height = max(height, size.height) + } + return CGSize(width: width, height: height) + } + + func placeSubviews( + in bounds: CGRect, + proposal: ProposedViewSize, + subviews: Subviews, + cache: inout Void + ) { + var x: CGFloat = 0 + for subview in subviews { + let size = subview.sizeThatFits(.unspecified) + subview.place(at: CGPoint(x: x, y: 0), proposal: .unspecified) + x += size.width + } + } +} + +@MainActor +func customLayout() -> some View { + let layout = AnyLayout(RowLayout()) + return layout { + Text("a") + Text("b") + } +} diff --git a/Tools/APICompatibility/Configuration/compatibility-manifest.json b/Tools/APICompatibility/Configuration/compatibility-manifest.json index fd61b019..ae612d36 100644 --- a/Tools/APICompatibility/Configuration/compatibility-manifest.json +++ b/Tools/APICompatibility/Configuration/compatibility-manifest.json @@ -338269,12 +338269,22 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:10TUIkitView0B0P0A0E5frame5width6height9alignmentQrSiSg_AiD9AlignmentVtF" + "symbolID" : "s:10TUIkitView0B0P0A0E5frame5width6height9alignmentQr10Foundation7CGFloatVSg_AlD9AlignmentVtF" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:10TUIkitView0B0P0A0E5frame8minWidth05idealE003maxE00D6Height0fH00gH09alignmentQrSiSg_AmD14FrameDimensionOSgA2mpD9AlignmentVtF" + "symbolID" : "s:10TUIkitView0B0P0A0E5frame5width6height9alignmentQr14CoreFoundation7CGFloatVSg_AlD9AlignmentVtF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:10TUIkitView0B0P0A0E5frame8minWidth05idealE003maxE00D6Height0fH00gH09alignmentQr10Foundation7CGFloatVSg_A5pD9AlignmentVtF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:10TUIkitView0B0P0A0E5frame8minWidth05idealE003maxE00D6Height0fH00gH09alignmentQr14CoreFoundation7CGFloatVSg_A5pD9AlignmentVtF" }, { "classification" : "implementationLeak", @@ -338301,6 +338311,16 @@ "ownerIssue" : "#35", "symbolID" : "s:10TUIkitView0B0P0A0E7overlay9alignment7contentQrAD9AlignmentV_qd__yXEtAaBRd__lF" }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:10TUIkitView0B0P0A0E7paddingyQr10Foundation7CGFloatVF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:10TUIkitView0B0P0A0E7paddingyQr14CoreFoundation7CGFloatVF" + }, { "classification" : "implementationLeak", "ownerIssue" : "#35", @@ -338309,12 +338329,12 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:10TUIkitView0B0P0A0E7paddingyQrAD4EdgeV_SitF" + "symbolID" : "s:10TUIkitView0B0P0A0E7paddingyQrAD4EdgeO3SetV_10Foundation7CGFloatVSgtF" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:10TUIkitView0B0P0A0E7paddingyQrSiF" + "symbolID" : "s:10TUIkitView0B0P0A0E7paddingyQrAD4EdgeO3SetV_14CoreFoundation7CGFloatVSgtF" }, { "classification" : "implementationLeak", @@ -341274,37 +341294,72 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10EdgeInsetsV10horizontal8verticalACSi_Sitcfc" + "symbolID" : "s:6TUIkit10EdgeInsetsV10horizontal8verticalAC10Foundation7CGFloatV_AHtcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit10EdgeInsetsV10horizontal8verticalAC14CoreFoundation7CGFloatV_AHtcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit10EdgeInsetsV3allAC10Foundation7CGFloatV_tcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10EdgeInsetsV3allACSi_tcfc" + "symbolID" : "s:6TUIkit10EdgeInsetsV3allAC14CoreFoundation7CGFloatV_tcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10EdgeInsetsV3top7leading6bottom8trailingACSi_S3itcfc" + "symbolID" : "s:6TUIkit10EdgeInsetsV3top10Foundation7CGFloatVvp" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10EdgeInsetsV3topSivp" + "symbolID" : "s:6TUIkit10EdgeInsetsV3top14CoreFoundation7CGFloatVvp" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10EdgeInsetsV6bottomSivp" + "symbolID" : "s:6TUIkit10EdgeInsetsV3top7leading6bottom8trailingAC10Foundation7CGFloatV_A3Jtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10EdgeInsetsV7leadingSivp" + "symbolID" : "s:6TUIkit10EdgeInsetsV3top7leading6bottom8trailingAC14CoreFoundation7CGFloatV_A3Jtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10EdgeInsetsV8trailingSivp" + "symbolID" : "s:6TUIkit10EdgeInsetsV6bottom10Foundation7CGFloatVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit10EdgeInsetsV6bottom14CoreFoundation7CGFloatVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit10EdgeInsetsV7leading10Foundation7CGFloatVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit10EdgeInsetsV7leading14CoreFoundation7CGFloatVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit10EdgeInsetsV8trailing10Foundation7CGFloatVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit10EdgeInsetsV8trailing14CoreFoundation7CGFloatVvp" }, { "classification" : "implementationLeak", @@ -341349,17 +341404,17 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10LazyHStackV7spacingSivp" + "symbolID" : "s:6TUIkit10LazyHStackV9alignment7spacing11pinnedViews7contentACyxGAA17VerticalAlignmentV_10Foundation7CGFloatVSgAA016PinnedScrollableG0VxyXEtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10LazyHStackV9alignment7spacing7contentACyxGAA17VerticalAlignmentO_SixyXEtcfc" + "symbolID" : "s:6TUIkit10LazyHStackV9alignment7spacing11pinnedViews7contentACyxGAA17VerticalAlignmentV_14CoreFoundation7CGFloatVSgAA016PinnedScrollableG0VxyXEtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10LazyHStackV9alignmentAA17VerticalAlignmentOvp" + "symbolID" : "s:6TUIkit10LazyHStackV9alignmentAA17VerticalAlignmentVvp" }, { "classification" : "implementationLeak", @@ -341384,17 +341439,17 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10LazyVStackV7spacingSivp" + "symbolID" : "s:6TUIkit10LazyVStackV9alignment7spacing11pinnedViews7contentACyxGAA19HorizontalAlignmentV_10Foundation7CGFloatVSgAA016PinnedScrollableG0VxyXEtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10LazyVStackV9alignment7spacing7contentACyxGAA19HorizontalAlignmentO_SixyXEtcfc" + "symbolID" : "s:6TUIkit10LazyVStackV9alignment7spacing11pinnedViews7contentACyxGAA19HorizontalAlignmentV_14CoreFoundation7CGFloatVSgAA016PinnedScrollableG0VxyXEtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit10LazyVStackV9alignmentAA19HorizontalAlignmentOvp" + "symbolID" : "s:6TUIkit10LazyVStackV9alignmentAA19HorizontalAlignmentVvp" }, { "classification" : "implementationLeak", @@ -341476,6 +341531,21 @@ "ownerIssue" : "#35", "symbolID" : "s:6TUIkit10renderOnce7contentyxyXE_t0A4View0E0RzlF" }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit11AlignmentIDP" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit11AlignmentIDP12defaultValue2in10Foundation7CGFloatVAA14ViewDimensionsV_tFZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit11AlignmentIDP12defaultValue2in14CoreFoundation7CGFloatVAA14ViewDimensionsV_tFZ" + }, { "classification" : "implementationLeak", "ownerIssue" : "#35", @@ -341649,12 +341719,12 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit11TableColumnV9alignmentAA19HorizontalAlignmentOvp" + "symbolID" : "s:6TUIkit11TableColumnV9alignmentAA19HorizontalAlignmentVvp" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit11TableColumnV9alignmentyACyxGAA19HorizontalAlignmentOF" + "symbolID" : "s:6TUIkit11TableColumnV9alignmentyACyxGAA19HorizontalAlignmentVF" }, { "classification" : "implementationLeak", @@ -341996,6 +342066,26 @@ "ownerIssue" : "#35", "symbolID" : "s:6TUIkit12VerticalEdgeO6bottomyA2CmF" }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit12ViewThatFitsV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit12ViewThatFitsV14renderToBuffer7context0A4Core05FrameG0V0aB013RenderContextV_tF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit12ViewThatFitsV2in7contentACyxGAA4AxisO3SetV_xyXEtcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit12ViewThatFitsV4bodys5NeverOvp" + }, { "classification" : "implementationLeak", "ownerIssue" : "#35", @@ -342016,6 +342106,46 @@ "ownerIssue" : "#35", "symbolID" : "s:6TUIkit13BadgeModifierVAASQRzrlE2eeoiySbACyxG_AEtFZ" }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit13GeometryProxyV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit13GeometryProxyV4size10Foundation6CGSizeVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit13GeometryProxyV4sizeSo6CGSizeVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit13LayoutSubviewV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit13LayoutSubviewV12sizeThatFitsy10Foundation6CGSizeVAA16ProposedViewSizeVF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit13LayoutSubviewV12sizeThatFitsySo6CGSizeVAA16ProposedViewSizeVF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit13LayoutSubviewV5place2at8proposaly10Foundation7CGPointV_AA16ProposedViewSizeVtF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit13LayoutSubviewV5place2at8proposalySo7CGPointV_AA16ProposedViewSizeVtF" + }, { "classification" : "implementationLeak", "ownerIssue" : "#35", @@ -342114,22 +342244,47 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit14FrameDimensionO" + "symbolID" : "s:6TUIkit14GeometryReaderV" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit14FrameDimensionO3maxACvpZ" + "symbolID" : "s:6TUIkit14GeometryReaderV14renderToBuffer7context0A4Core05FrameF0V0A4View13RenderContextV_tF" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit14FrameDimensionO5fixedyACSicACmF" + "symbolID" : "s:6TUIkit14GeometryReaderV4bodys5NeverOvp" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit14FrameDimensionO8infinityyA2CmF" + "symbolID" : "s:6TUIkit14GeometryReaderV7contentACyxGxAA0B5ProxyVc_tcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14GeometryReaderV7contentyxAA0B5ProxyVcvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14LayoutSubviewsV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14LayoutSubviewsV10startIndexSivp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14LayoutSubviewsV8endIndexSivp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14LayoutSubviewsVyAA0B7SubviewVSicip" }, { "classification" : "implementationLeak", @@ -342346,6 +342501,51 @@ "ownerIssue" : "#35", "symbolID" : "s:6TUIkit14StorageBackendP8setValue_6forKeyyqd___SStSeRd__SERd__lF" }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14ViewDimensionsV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14ViewDimensionsV5width10Foundation7CGFloatVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14ViewDimensionsV5width14CoreFoundation7CGFloatVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14ViewDimensionsV6height10Foundation7CGFloatVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14ViewDimensionsV6height14CoreFoundation7CGFloatVvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14ViewDimensionsVy10Foundation7CGFloatVAA17VerticalAlignmentVcip" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14ViewDimensionsVy10Foundation7CGFloatVAA19HorizontalAlignmentVcip" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14ViewDimensionsVy14CoreFoundation7CGFloatVAA17VerticalAlignmentVcip" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit14ViewDimensionsVy14CoreFoundation7CGFloatVAA19HorizontalAlignmentVcip" + }, { "classification" : "tuiSpecific", "ownerIssue" : "#35", @@ -342966,6 +343166,66 @@ "ownerIssue" : "#35", "symbolID" : "s:6TUIkit16ProgressBarStylea" }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV11unspecifiedACvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV4zeroACvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV5width10Foundation7CGFloatVSgvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV5width14CoreFoundation7CGFloatVSgvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV5width6heightAC10Foundation7CGFloatVSg_AItcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV5width6heightAC14CoreFoundation7CGFloatVSg_AItcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV6height10Foundation7CGFloatVSgvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV6height14CoreFoundation7CGFloatVSgvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeV8infinityACvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeVyAC10Foundation6CGSizeVcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit16ProposedViewSizeVyACSo6CGSizeVcfc" + }, { "classification" : "implementationLeak", "ownerIssue" : "#35", @@ -343144,22 +343404,32 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit17VerticalAlignmentO" + "symbolID" : "s:6TUIkit17VerticalAlignmentV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit17VerticalAlignmentV2eeoiySbAC_ACtFZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit17VerticalAlignmentV3topACvpZ" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit17VerticalAlignmentO3topyA2CmF" + "symbolID" : "s:6TUIkit17VerticalAlignmentV6bottomACvpZ" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit17VerticalAlignmentO6bottomyA2CmF" + "symbolID" : "s:6TUIkit17VerticalAlignmentV6centerACvpZ" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit17VerticalAlignmentO6centeryA2CmF" + "symbolID" : "s:6TUIkit17VerticalAlignmentVyAcA0C2ID_pXpcfc" }, { "classification" : "implementationLeak", @@ -343304,22 +343574,32 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit19HorizontalAlignmentO" + "symbolID" : "s:6TUIkit19HorizontalAlignmentV" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit19HorizontalAlignmentO6centeryA2CmF" + "symbolID" : "s:6TUIkit19HorizontalAlignmentV2eeoiySbAC_ACtFZ" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit19HorizontalAlignmentO7leadingyA2CmF" + "symbolID" : "s:6TUIkit19HorizontalAlignmentV6centerACvpZ" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit19HorizontalAlignmentO8trailingyA2CmF" + "symbolID" : "s:6TUIkit19HorizontalAlignmentV7leadingACvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit19HorizontalAlignmentV8trailingACvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit19HorizontalAlignmentVyAcA0C2ID_pXpcfc" }, { "classification" : "tuiSpecific", @@ -343666,6 +343946,31 @@ "ownerIssue" : "#35", "symbolID" : "s:6TUIkit21InsetGroupedListStyleVACycfc" }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit21PinnedScrollableViewsV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit21PinnedScrollableViewsV14sectionFootersACvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit21PinnedScrollableViewsV14sectionHeadersACvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit21PinnedScrollableViewsV8rawValueACs6UInt32V_tcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit21PinnedScrollableViewsV8rawValues6UInt32Vvp" + }, { "classification" : "tuiSpecific", "ownerIssue" : "#35", @@ -344096,6 +344401,51 @@ "ownerIssue" : "#35", "symbolID" : "s:6TUIkit3AppPxycfc" }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4AxisO" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4AxisO10horizontalyA2CmF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4AxisO3SetV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4AxisO3SetV10horizontalAEvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4AxisO3SetV8rawValueAEs4Int8V_tcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4AxisO3SetV8rawValues4Int8Vvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4AxisO3SetV8verticalAEvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4AxisO8rawValueACSgs4Int8V_tcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4AxisO8verticalyA2CmF" + }, { "classification" : "tuiSpecific", "ownerIssue" : "#35", @@ -344129,52 +344479,92 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV" + "symbolID" : "s:6TUIkit4EdgeO" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4EdgeO3SetV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4EdgeO3SetV10horizontalAEvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4EdgeO3SetV3allAEvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4EdgeO3SetV3topAEvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4EdgeO3SetV6bottomAEvpZ" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4EdgeO3SetV7leadingAEvpZ" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV10horizontalACvpZ" + "symbolID" : "s:6TUIkit4EdgeO3SetV8containsySbACF" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV3allACvpZ" + "symbolID" : "s:6TUIkit4EdgeO3SetV8rawValueAEs4Int8V_tcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV3topACvpZ" + "symbolID" : "s:6TUIkit4EdgeO3SetV8rawValues4Int8Vvp" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV6bottomACvpZ" + "symbolID" : "s:6TUIkit4EdgeO3SetV8trailingAEvpZ" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV7leadingACvpZ" + "symbolID" : "s:6TUIkit4EdgeO3SetV8verticalAEvpZ" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV8rawValueACs5UInt8V_tcfc" + "symbolID" : "s:6TUIkit4EdgeO3SetVyAeCcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV8rawValues5UInt8Vvp" + "symbolID" : "s:6TUIkit4EdgeO3topyA2CmF" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV8trailingACvpZ" + "symbolID" : "s:6TUIkit4EdgeO6bottomyA2CmF" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit4EdgeV8verticalACvpZ" + "symbolID" : "s:6TUIkit4EdgeO7leadingyA2CmF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4EdgeO8rawValueACSgs4Int8V_tcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit4EdgeO8trailingyA2CmF" }, { "classification" : "implementationLeak", @@ -344624,23 +345014,73 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit6HStackV7spacingSivp" + "symbolID" : "s:6TUIkit6HStackV9alignment7spacing7contentACyxGAA17VerticalAlignmentV_10Foundation7CGFloatVSgxyXEtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit6HStackV9alignment7spacing7contentACyxGAA17VerticalAlignmentO_SixyXEtcfc" + "symbolID" : "s:6TUIkit6HStackV9alignment7spacing7contentACyxGAA17VerticalAlignmentV_14CoreFoundation7CGFloatVSgxyXEtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit6HStackV9alignmentAA17VerticalAlignmentOvp" + "symbolID" : "s:6TUIkit6HStackV9alignmentAA17VerticalAlignmentVvp" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", "symbolID" : "s:6TUIkit6HStackVAASQRzrlE2eeoiySbACyxG_AEtFZ" }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutP" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutP12sizeThatFits8proposal8subviews5cache10Foundation6CGSizeVAA16ProposedViewSizeV_AA0B8SubviewsV5CacheQzztF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutP12sizeThatFits8proposal8subviews5cacheSo6CGSizeVAA16ProposedViewSizeV_AA0B8SubviewsV5CacheQzztF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutP13placeSubviews2in8proposal8subviews5cachey10Foundation6CGRectV_AA16ProposedViewSizeVAA0bD0V5CacheQzztF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutP13placeSubviews2in8proposal8subviews5cacheySo6CGRectV_AA16ProposedViewSizeVAA0bD0V5CacheQzztF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutP5CacheQa" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutP8Subviewsa" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutP9makeCache8subviews0D0QzAA0B8SubviewsV_tF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutPAAE14callAsFunctionyQrqd__yXE0A4View0F0Rd__lF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6LayoutPAAyt5CacheRtzrlE04makeC08subviewsyAA0B8SubviewsV_tF" + }, { "classification" : "implementationLeak", "ownerIssue" : "#35", @@ -344709,7 +345149,12 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit6SpacerV9minLengthACSiSg_tcfc" + "symbolID" : "s:6TUIkit6SpacerV9minLengthAC10Foundation7CGFloatVSg_tcfc" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit6SpacerV9minLengthAC14CoreFoundation7CGFloatVSg_tcfc" }, { "classification" : "implementationLeak", @@ -344759,17 +345204,17 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit6VStackV7spacingSivp" + "symbolID" : "s:6TUIkit6VStackV9alignment7spacing7contentACyxGAA19HorizontalAlignmentV_10Foundation7CGFloatVSgxyXEtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit6VStackV9alignment7spacing7contentACyxGAA19HorizontalAlignmentO_SixyXEtcfc" + "symbolID" : "s:6TUIkit6VStackV9alignment7spacing7contentACyxGAA19HorizontalAlignmentV_14CoreFoundation7CGFloatVSgxyXEtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit6VStackV9alignmentAA19HorizontalAlignmentOvp" + "symbolID" : "s:6TUIkit6VStackV9alignmentAA19HorizontalAlignmentVvp" }, { "classification" : "implementationLeak", @@ -345304,12 +345749,12 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit9AlignmentV10horizontal8verticalAcA010HorizontalB0O_AA08VerticalB0Otcfc" + "symbolID" : "s:6TUIkit9AlignmentV10horizontal8verticalAcA010HorizontalB0V_AA08VerticalB0Vtcfc" }, { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit9AlignmentV10horizontalAA010HorizontalB0Ovp" + "symbolID" : "s:6TUIkit9AlignmentV10horizontalAA010HorizontalB0Vvp" }, { "classification" : "implementationLeak", @@ -345359,7 +345804,47 @@ { "classification" : "implementationLeak", "ownerIssue" : "#35", - "symbolID" : "s:6TUIkit9AlignmentV8verticalAA08VerticalB0Ovp" + "symbolID" : "s:6TUIkit9AlignmentV8verticalAA08VerticalB0Vvp" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit9AnyLayoutV" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit9AnyLayoutV12sizeThatFits8proposal8subviews5cache10Foundation6CGSizeVAA16ProposedViewSizeV_AA0C8SubviewsVypztF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit9AnyLayoutV12sizeThatFits8proposal8subviews5cacheSo6CGSizeVAA16ProposedViewSizeV_AA0C8SubviewsVypztF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit9AnyLayoutV13placeSubviews2in8proposal8subviews5cachey10Foundation6CGRectV_AA16ProposedViewSizeVAA0cE0VypztF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit9AnyLayoutV13placeSubviews2in8proposal8subviews5cacheySo6CGRectV_AA16ProposedViewSizeVAA0cE0VypztF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit9AnyLayoutV5Cachea" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit9AnyLayoutV9makeCache8subviewsypAA0C8SubviewsV_tF" + }, + { + "classification" : "implementationLeak", + "ownerIssue" : "#35", + "symbolID" : "s:6TUIkit9AnyLayoutVyACxcAA0C0Rzlufc" }, { "classification" : "implementationLeak", diff --git a/Tools/APICompatibility/Configuration/review-policy.json b/Tools/APICompatibility/Configuration/review-policy.json index cf92a67d..d7550918 100644 --- a/Tools/APICompatibility/Configuration/review-policy.json +++ b/Tools/APICompatibility/Configuration/review-policy.json @@ -18758,16 +18758,6 @@ "ownerIssue": "#35", "symbolID": "s:10TUIkitView0B0P0A0E5badgeyQrqd__SgSyRd__lF" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:10TUIkitView0B0P0A0E5frame5width6height9alignmentQrSiSg_AiD9AlignmentVtF" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:10TUIkitView0B0P0A0E5frame8minWidth05idealE003maxE00D6Height0fH00gH09alignmentQrSiSg_AmD14FrameDimensionOSgA2mpD9AlignmentVtF" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -18798,16 +18788,6 @@ "ownerIssue": "#35", "symbolID": "s:10TUIkitView0B0P0A0E7paddingyQrAD10EdgeInsetsVF" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:10TUIkitView0B0P0A0E7paddingyQrAD4EdgeV_SitF" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:10TUIkitView0B0P0A0E7paddingyQrSiF" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -21378,41 +21358,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit10EdgeInsetsV" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10EdgeInsetsV10horizontal8verticalACSi_Sitcfc" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10EdgeInsetsV3allACSi_tcfc" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10EdgeInsetsV3top7leading6bottom8trailingACSi_S3itcfc" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10EdgeInsetsV3topSivp" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10EdgeInsetsV6bottomSivp" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10EdgeInsetsV7leadingSivp" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10EdgeInsetsV8trailingSivp" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -21453,21 +21398,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit10LazyHStackV7contentxvp" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10LazyHStackV7spacingSivp" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10LazyHStackV9alignment7spacing7contentACyxGAA17VerticalAlignmentO_SixyXEtcfc" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10LazyHStackV9alignmentAA17VerticalAlignmentOvp" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -21488,21 +21418,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit10LazyVStackV7contentxvp" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10LazyVStackV7spacingSivp" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10LazyVStackV9alignment7spacing7contentACyxGAA19HorizontalAlignmentO_SixyXEtcfc" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit10LazyVStackV9alignmentAA19HorizontalAlignmentOvp" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -21733,16 +21648,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit11TableColumnV5widthyACyxGAA0C5WidthOF" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit11TableColumnV9alignmentAA19HorizontalAlignmentOvp" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit11TableColumnV9alignmentyACyxGAA19HorizontalAlignmentOF" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -22188,26 +22093,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit14DimmedModifierVAASQRzrlE2eeoiySbACyxG_AEtFZ" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit14FrameDimensionO" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit14FrameDimensionO3maxACvpZ" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit14FrameDimensionO5fixedyACSicACmF" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit14FrameDimensionO8infinityyA2CmF" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -23203,26 +23088,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit17SwitchToggleStyleVACycfc" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit17VerticalAlignmentO" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit17VerticalAlignmentO3topyA2CmF" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit17VerticalAlignmentO6bottomyA2CmF" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit17VerticalAlignmentO6centeryA2CmF" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -23363,26 +23228,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit19CheckboxToggleStyleVACycfc" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit19HorizontalAlignmentO" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit19HorizontalAlignmentO6centeryA2CmF" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit19HorizontalAlignmentO7leadingyA2CmF" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit19HorizontalAlignmentO8trailingyA2CmF" - }, { "action": "tuiSpecific", "ownerIssue": "#35", @@ -24143,56 +23988,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit4CardVAASQRzSQR_rlE2eeoiySbACyxq_G_AEtFZ" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV10horizontalACvpZ" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV3allACvpZ" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV3topACvpZ" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV6bottomACvpZ" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV7leadingACvpZ" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV8rawValueACs5UInt8V_tcfc" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV8rawValues5UInt8Vvp" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV8trailingACvpZ" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit4EdgeV8verticalACvpZ" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -24613,21 +24408,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit6HStackV7contentxvp" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit6HStackV7spacingSivp" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit6HStackV9alignment7spacing7contentACyxGAA17VerticalAlignmentO_SixyXEtcfc" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit6HStackV9alignmentAA17VerticalAlignmentOvp" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -24698,11 +24478,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit6SpacerV4bodys5NeverOvp" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit6SpacerV9minLengthACSiSg_tcfc" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -24748,21 +24523,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit6VStackV7contentxvp" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit6VStackV7spacingSivp" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit6VStackV9alignment7spacing7contentACyxGAA19HorizontalAlignmentO_SixyXEtcfc" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit6VStackV9alignmentAA19HorizontalAlignmentOvp" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -25298,16 +25058,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit9AlignmentV" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit9AlignmentV10horizontal8verticalAcA010HorizontalB0O_AA08VerticalB0Otcfc" - }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit9AlignmentV10horizontalAA010HorizontalB0Ovp" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -25353,11 +25103,6 @@ "ownerIssue": "#35", "symbolID": "s:6TUIkit9AlignmentV8trailingACvpZ" }, - { - "action": "implementationLeak", - "ownerIssue": "#35", - "symbolID": "s:6TUIkit9AlignmentV8verticalAA08VerticalB0Ovp" - }, { "action": "implementationLeak", "ownerIssue": "#35", @@ -26207,6 +25952,746 @@ "action": "implementationLeak", "ownerIssue": "#35", "symbolID": "s:6TUIkit5ScenePAAE7paletteyQr0A7Styling7Palette_pF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:10TUIkitView0B0P0A0E5frame5width6height9alignmentQr10Foundation7CGFloatVSg_AlD9AlignmentVtF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:10TUIkitView0B0P0A0E5frame5width6height9alignmentQr14CoreFoundation7CGFloatVSg_AlD9AlignmentVtF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:10TUIkitView0B0P0A0E5frame8minWidth05idealE003maxE00D6Height0fH00gH09alignmentQr10Foundation7CGFloatVSg_A5pD9AlignmentVtF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:10TUIkitView0B0P0A0E5frame8minWidth05idealE003maxE00D6Height0fH00gH09alignmentQr14CoreFoundation7CGFloatVSg_A5pD9AlignmentVtF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:10TUIkitView0B0P0A0E7paddingyQr10Foundation7CGFloatVF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:10TUIkitView0B0P0A0E7paddingyQr14CoreFoundation7CGFloatVF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:10TUIkitView0B0P0A0E7paddingyQrAD4EdgeO3SetV_10Foundation7CGFloatVSgtF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:10TUIkitView0B0P0A0E7paddingyQrAD4EdgeO3SetV_14CoreFoundation7CGFloatVSgtF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV10horizontal8verticalAC10Foundation7CGFloatV_AHtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV10horizontal8verticalAC14CoreFoundation7CGFloatV_AHtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV3allAC10Foundation7CGFloatV_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV3allAC14CoreFoundation7CGFloatV_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV3top10Foundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV3top14CoreFoundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV3top7leading6bottom8trailingAC10Foundation7CGFloatV_A3Jtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV3top7leading6bottom8trailingAC14CoreFoundation7CGFloatV_A3Jtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV6bottom10Foundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV6bottom14CoreFoundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV7leading10Foundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV7leading14CoreFoundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV8trailing10Foundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10EdgeInsetsV8trailing14CoreFoundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10LazyHStackV9alignment7spacing11pinnedViews7contentACyxGAA17VerticalAlignmentV_10Foundation7CGFloatVSgAA016PinnedScrollableG0VxyXEtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10LazyHStackV9alignment7spacing11pinnedViews7contentACyxGAA17VerticalAlignmentV_14CoreFoundation7CGFloatVSgAA016PinnedScrollableG0VxyXEtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10LazyHStackV9alignmentAA17VerticalAlignmentVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10LazyVStackV9alignment7spacing11pinnedViews7contentACyxGAA19HorizontalAlignmentV_10Foundation7CGFloatVSgAA016PinnedScrollableG0VxyXEtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10LazyVStackV9alignment7spacing11pinnedViews7contentACyxGAA19HorizontalAlignmentV_14CoreFoundation7CGFloatVSgAA016PinnedScrollableG0VxyXEtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit10LazyVStackV9alignmentAA19HorizontalAlignmentVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit11AlignmentIDP" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit11AlignmentIDP12defaultValue2in10Foundation7CGFloatVAA14ViewDimensionsV_tFZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit11AlignmentIDP12defaultValue2in14CoreFoundation7CGFloatVAA14ViewDimensionsV_tFZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit11TableColumnV9alignmentAA19HorizontalAlignmentVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit11TableColumnV9alignmentyACyxGAA19HorizontalAlignmentVF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit12ViewThatFitsV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit12ViewThatFitsV14renderToBuffer7context0A4Core05FrameG0V0aB013RenderContextV_tF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit12ViewThatFitsV2in7contentACyxGAA4AxisO3SetV_xyXEtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit12ViewThatFitsV4bodys5NeverOvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit13GeometryProxyV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit13GeometryProxyV4size10Foundation6CGSizeVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit13GeometryProxyV4sizeSo6CGSizeVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit13LayoutSubviewV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit13LayoutSubviewV12sizeThatFitsy10Foundation6CGSizeVAA16ProposedViewSizeVF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit13LayoutSubviewV12sizeThatFitsySo6CGSizeVAA16ProposedViewSizeVF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit13LayoutSubviewV5place2at8proposaly10Foundation7CGPointV_AA16ProposedViewSizeVtF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit13LayoutSubviewV5place2at8proposalySo7CGPointV_AA16ProposedViewSizeVtF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14GeometryReaderV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14GeometryReaderV14renderToBuffer7context0A4Core05FrameF0V0A4View13RenderContextV_tF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14GeometryReaderV4bodys5NeverOvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14GeometryReaderV7contentACyxGxAA0B5ProxyVc_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14GeometryReaderV7contentyxAA0B5ProxyVcvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14LayoutSubviewsV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14LayoutSubviewsV10startIndexSivp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14LayoutSubviewsV8endIndexSivp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14LayoutSubviewsVyAA0B7SubviewVSicip" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14ViewDimensionsV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14ViewDimensionsV5width10Foundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14ViewDimensionsV5width14CoreFoundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14ViewDimensionsV6height10Foundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14ViewDimensionsV6height14CoreFoundation7CGFloatVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14ViewDimensionsVy10Foundation7CGFloatVAA17VerticalAlignmentVcip" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14ViewDimensionsVy10Foundation7CGFloatVAA19HorizontalAlignmentVcip" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14ViewDimensionsVy14CoreFoundation7CGFloatVAA17VerticalAlignmentVcip" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit14ViewDimensionsVy14CoreFoundation7CGFloatVAA19HorizontalAlignmentVcip" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV11unspecifiedACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV4zeroACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV5width10Foundation7CGFloatVSgvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV5width14CoreFoundation7CGFloatVSgvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV5width6heightAC10Foundation7CGFloatVSg_AItcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV5width6heightAC14CoreFoundation7CGFloatVSg_AItcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV6height10Foundation7CGFloatVSgvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV6height14CoreFoundation7CGFloatVSgvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeV8infinityACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeVyAC10Foundation6CGSizeVcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit16ProposedViewSizeVyACSo6CGSizeVcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit17VerticalAlignmentV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit17VerticalAlignmentV2eeoiySbAC_ACtFZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit17VerticalAlignmentV3topACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit17VerticalAlignmentV6bottomACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit17VerticalAlignmentV6centerACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit17VerticalAlignmentVyAcA0C2ID_pXpcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit19HorizontalAlignmentV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit19HorizontalAlignmentV2eeoiySbAC_ACtFZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit19HorizontalAlignmentV6centerACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit19HorizontalAlignmentV7leadingACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit19HorizontalAlignmentV8trailingACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit19HorizontalAlignmentVyAcA0C2ID_pXpcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit21PinnedScrollableViewsV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit21PinnedScrollableViewsV14sectionFootersACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit21PinnedScrollableViewsV14sectionHeadersACvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit21PinnedScrollableViewsV8rawValueACs6UInt32V_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit21PinnedScrollableViewsV8rawValues6UInt32Vvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4AxisO" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4AxisO10horizontalyA2CmF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4AxisO3SetV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4AxisO3SetV10horizontalAEvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4AxisO3SetV8rawValueAEs4Int8V_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4AxisO3SetV8rawValues4Int8Vvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4AxisO3SetV8verticalAEvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4AxisO8rawValueACSgs4Int8V_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4AxisO8verticalyA2CmF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV10horizontalAEvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV3allAEvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV3topAEvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV6bottomAEvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV7leadingAEvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV8containsySbACF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV8rawValueAEs4Int8V_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV8rawValues4Int8Vvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV8trailingAEvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetV8verticalAEvpZ" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3SetVyAeCcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO3topyA2CmF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO6bottomyA2CmF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO7leadingyA2CmF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO8rawValueACSgs4Int8V_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit4EdgeO8trailingyA2CmF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6HStackV9alignment7spacing7contentACyxGAA17VerticalAlignmentV_10Foundation7CGFloatVSgxyXEtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6HStackV9alignment7spacing7contentACyxGAA17VerticalAlignmentV_14CoreFoundation7CGFloatVSgxyXEtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6HStackV9alignmentAA17VerticalAlignmentVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutP" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutP12sizeThatFits8proposal8subviews5cache10Foundation6CGSizeVAA16ProposedViewSizeV_AA0B8SubviewsV5CacheQzztF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutP12sizeThatFits8proposal8subviews5cacheSo6CGSizeVAA16ProposedViewSizeV_AA0B8SubviewsV5CacheQzztF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutP13placeSubviews2in8proposal8subviews5cachey10Foundation6CGRectV_AA16ProposedViewSizeVAA0bD0V5CacheQzztF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutP13placeSubviews2in8proposal8subviews5cacheySo6CGRectV_AA16ProposedViewSizeVAA0bD0V5CacheQzztF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutP5CacheQa" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutP8Subviewsa" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutP9makeCache8subviews0D0QzAA0B8SubviewsV_tF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutPAAE14callAsFunctionyQrqd__yXE0A4View0F0Rd__lF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6LayoutPAAyt5CacheRtzrlE04makeC08subviewsyAA0B8SubviewsV_tF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6SpacerV9minLengthAC10Foundation7CGFloatVSg_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6SpacerV9minLengthAC14CoreFoundation7CGFloatVSg_tcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6VStackV9alignment7spacing7contentACyxGAA19HorizontalAlignmentV_10Foundation7CGFloatVSgxyXEtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6VStackV9alignment7spacing7contentACyxGAA19HorizontalAlignmentV_14CoreFoundation7CGFloatVSgxyXEtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit6VStackV9alignmentAA19HorizontalAlignmentVvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AlignmentV10horizontal8verticalAcA010HorizontalB0V_AA08VerticalB0Vtcfc" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AlignmentV10horizontalAA010HorizontalB0Vvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AlignmentV8verticalAA08VerticalB0Vvp" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AnyLayoutV" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AnyLayoutV12sizeThatFits8proposal8subviews5cache10Foundation6CGSizeVAA16ProposedViewSizeV_AA0C8SubviewsVypztF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AnyLayoutV12sizeThatFits8proposal8subviews5cacheSo6CGSizeVAA16ProposedViewSizeV_AA0C8SubviewsVypztF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AnyLayoutV13placeSubviews2in8proposal8subviews5cachey10Foundation6CGRectV_AA16ProposedViewSizeVAA0cE0VypztF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AnyLayoutV13placeSubviews2in8proposal8subviews5cacheySo6CGRectV_AA16ProposedViewSizeVAA0cE0VypztF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AnyLayoutV5Cachea" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AnyLayoutV9makeCache8subviewsypAA0C8SubviewsV_tF" + }, + { + "action": "implementationLeak", + "ownerIssue": "#35", + "symbolID": "s:6TUIkit9AnyLayoutVyACxcAA0C0Rzlufc" } ] }