1+ import Foundation
2+
13public enum LayoutSystem {
24 static func width( forHeight height: Int , aspectRatio: Double ) -> Int {
35 roundSize ( Double ( height) * aspectRatio)
@@ -11,6 +13,18 @@ public enum LayoutSystem {
1113 Int ( size. rounded ( . towardZero) )
1214 }
1315
16+ /// Clamps a value to a range given optional lower and upper bounds.
17+ static func clamp< T: Comparable > ( _ value: T , minimum: T ? = nil , maximum: T ? = nil ) -> T {
18+ var value = value
19+ if let minimum {
20+ value = max ( value, minimum)
21+ }
22+ if let maximum {
23+ value = min ( value, maximum)
24+ }
25+ return value
26+ }
27+
1428 static func aspectRatio( of frame: SIMD2 < Double > ) -> Double {
1529 if frame. x == 0 || frame. y == 0 {
1630 // Even though we could technically compute an aspect ratio when the
@@ -47,15 +61,20 @@ public enum LayoutSystem {
4761 public struct LayoutableChild {
4862 private var computeLayout :
4963 @MainActor (
50- _ proposedSize: SIMD2 < Int > ,
64+ _ proposedSize: SizeProposal ,
5165 _ environment: EnvironmentValues
5266 ) -> ViewLayoutResult
67+
5368 private var _commit : @MainActor ( ) -> ViewLayoutResult
69+
5470 var tag : String ?
5571
5672 public init (
57- computeLayout: @escaping @MainActor ( SIMD2 < Int > , EnvironmentValues ) -> ViewLayoutResult ,
58- commit: @escaping @MainActor ( ) -> ViewLayoutResult ,
73+ computeLayout: @escaping @MainActor (
74+ SizeProposal ,
75+ EnvironmentValues
76+ ) -> ViewLayoutResult ,
77+ commit: @escaping ( ) -> ViewLayoutResult ,
5978 tag: String ? = nil
6079 ) {
6180 self . computeLayout = computeLayout
@@ -65,7 +84,7 @@ public enum LayoutSystem {
6584
6685 @MainActor
6786 public func computeLayout(
68- proposedSize: SIMD2 < Int > ,
87+ proposedSize: SizeProposal ,
6988 environment: EnvironmentValues ,
7089 dryRun: Bool = false
7190 ) -> ViewLayoutResult {
@@ -87,7 +106,7 @@ public enum LayoutSystem {
87106 public static func computeStackLayout< Backend: AppBackend > (
88107 container: Backend . Widget ,
89108 children: [ LayoutableChild ] ,
90- proposedSize: SIMD2 < Int > ,
109+ proposedSize: SizeProposal ,
91110 environment: EnvironmentValues ,
92111 backend: Backend ,
93112 inheritStackLayoutParticipation: Bool = false
@@ -106,7 +125,7 @@ public enum LayoutSystem {
106125 var isHidden = [ Bool] ( repeating: false , count: children. count)
107126 let flexibilities = children. enumerated ( ) . map { i, child in
108127 let result = child. computeLayout (
109- proposedSize: proposedSize ,
128+ proposedSize: . ideal ,
110129 environment: environment
111130 )
112131 isHidden [ i] = !result. participatesInStackLayouts
@@ -121,11 +140,19 @@ public enum LayoutSystem {
121140 !hidden
122141 } . count
123142 let totalSpacing = max ( visibleChildrenCount - 1 , 0 ) * spacing
124- let sortedChildren = zip ( children. enumerated ( ) , flexibilities)
125- . sorted { first, second in
126- first. 1 <= second. 1
127- }
128- . map ( \. 0 )
143+
144+ let sortedChildren : [ ( offset: Int , element: LayoutSystem . LayoutableChild ) ]
145+ if orientation == . vertical && proposedSize. height == nil
146+ || orientation == . horizontal && proposedSize. width == nil
147+ {
148+ sortedChildren = Array ( children. enumerated ( ) )
149+ } else {
150+ sortedChildren = zip ( children. enumerated ( ) , flexibilities)
151+ . sorted { first, second in
152+ first. 1 <= second. 1
153+ }
154+ . map ( \. 0 )
155+ }
129156
130157 var spaceUsedAlongStackAxis = 0
131158 var childrenRemaining = visibleChildrenCount
@@ -152,25 +179,39 @@ public enum LayoutSystem {
152179 continue
153180 }
154181
155- let proposedWidth : Double
156- let proposedHeight : Double
182+ let proposedWidth : Double ?
183+ let proposedHeight : Double ?
157184 switch orientation {
158185 case . horizontal:
159- proposedWidth =
160- Double ( max ( proposedSize. x - spaceUsedAlongStackAxis - totalSpacing, 0 ) )
161- / Double( childrenRemaining)
162- proposedHeight = Double ( proposedSize. y)
186+ if let parentProposedWidth = proposedSize. width {
187+ proposedWidth =
188+ Double (
189+ max (
190+ parentProposedWidth - spaceUsedAlongStackAxis - totalSpacing,
191+ 0
192+ ) ) / Double( childrenRemaining)
193+ } else {
194+ proposedWidth = nil
195+ }
196+ proposedHeight = proposedSize. height. map ( Double . init)
163197 case . vertical:
164- proposedHeight =
165- Double ( max ( proposedSize. y - spaceUsedAlongStackAxis - totalSpacing, 0 ) )
166- / Double( childrenRemaining)
167- proposedWidth = Double ( proposedSize. x)
198+ if let parentProposedHeight = proposedSize. height {
199+ proposedHeight =
200+ Double (
201+ max (
202+ parentProposedHeight - spaceUsedAlongStackAxis - totalSpacing,
203+ 0
204+ ) ) / Double( childrenRemaining)
205+ } else {
206+ proposedHeight = nil
207+ }
208+ proposedWidth = proposedSize. width. map ( Double . init)
168209 }
169210
170211 let childResult = child. computeLayout (
171- proposedSize: SIMD2 < Int > (
172- Int ( proposedWidth . rounded ( . towardZero) ) ,
173- Int ( proposedHeight . rounded ( . towardZero) )
212+ proposedSize: SizeProposal (
213+ proposedWidth . map { Int ( $0 . rounded ( . towardZero) ) } ,
214+ proposedHeight . map { Int ( $0 . rounded ( . towardZero) ) }
174215 ) ,
175216 environment: environment
176217 )
0 commit comments