Skip to content

Commit 2a5a23f

Browse files
authored
Update UnaryLayout (#311)
* Update OptionSet usage * Add UnaryLayout makeViewImpl implementation * Fix LayoutTrace crash issue
1 parent 2a5322a commit 2a5a23f

File tree

7 files changed

+238
-50
lines changed

7 files changed

+238
-50
lines changed

Sources/OpenSwiftUICore/Graph/GraphInputs.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public struct _GraphInputs {
129129
get { cachedEnvironment.wrappedValue.environment }
130130
set {
131131
cachedEnvironment = MutableBox(CachedEnvironment(newValue))
132-
changedDebugProperties.formUnion(.environment)
132+
changedDebugProperties.insert(.environment)
133133
}
134134
}
135135

Sources/OpenSwiftUICore/Layout/GeometryReader.swift

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,4 @@
22
// GeometryReader.swift
33
// OpenSwiftUICore
44
//
5-
// Status: WIP
6-
7-
extension UnaryLayout where Self.PlacementContextType == _PositionAwarePlacementContext {
8-
package static func makeViewImpl(
9-
modifier: _GraphValue<Self>,
10-
inputs: _ViewInputs,
11-
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
12-
) -> _ViewOutputs {
13-
preconditionFailure("TODO")
14-
}
15-
}
16-
17-
extension UnaryLayout where Self.PlacementContextType == PlacementContext {
18-
package static func makeViewImpl(
19-
modifier: _GraphValue<Self>,
20-
inputs: _ViewInputs,
21-
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
22-
) -> _ViewOutputs {
23-
preconditionFailure("TODO")
24-
}
25-
}
5+
// Status: Empty
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
//
2+
// UnaryLayoutComputer.swift
3+
// OpenSwiftUICore
4+
//
5+
// Status: Blocked by _PositionAwarePlacementContext
6+
// ID: 1C3B77B617AD058A6802F719E38F5D79 (SwiftUICore?)
7+
8+
import Foundation
9+
package import OpenGraphShims
10+
11+
// MARK: - UnaryLayout + _PositionAwarePlacementContext [6.4.41] [TODO]
12+
13+
extension UnaryLayout where Self.PlacementContextType == _PositionAwarePlacementContext {
14+
package static func makeViewImpl(
15+
modifier: _GraphValue<Self>,
16+
inputs: _ViewInputs,
17+
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
18+
) -> _ViewOutputs {
19+
preconditionFailure("TODO")
20+
}
21+
}
22+
23+
// MARK: - UnaryLayout + PlacementContext [6.4.41]
24+
25+
extension UnaryLayout where PlacementContextType == PlacementContext {
26+
package static func makeViewImpl(
27+
modifier: _GraphValue<Self>,
28+
inputs: _ViewInputs,
29+
body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs
30+
) -> _ViewOutputs {
31+
guard inputs.requestsLayoutComputer || inputs.needsGeometry else {
32+
return body(_Graph(), inputs)
33+
}
34+
let animatedModifier = makeAnimatable(value: modifier, inputs: inputs.base)
35+
let computer = Attribute( UnaryLayoutComputer(
36+
layout: animatedModifier,
37+
environment: inputs.environment,
38+
childLayoutComputer: OptionalAttribute()
39+
))
40+
var inputs = inputs
41+
let geometry: Attribute<ViewGeometry>!
42+
if inputs.needsGeometry {
43+
geometry = Attribute(UnaryChildGeometry<Self>(
44+
parentSize: inputs.size,
45+
layoutDirection: inputs.layoutDirection,
46+
parentLayoutComputer: computer
47+
))
48+
inputs.size = geometry.size()
49+
inputs.position = Attribute(LayoutPositionQuery(
50+
parentPosition: inputs.position,
51+
localPosition: geometry.origin()
52+
))
53+
inputs.requestsLayoutComputer = true
54+
} else {
55+
geometry = nil
56+
}
57+
let requestsLayoutComputer = inputs.requestsLayoutComputer
58+
var outputs = body(_Graph(), inputs)
59+
if inputs.needsGeometry {
60+
computer.mutateBody(as: UnaryLayoutComputer<Self>.self, invalidating: true) { computer in
61+
computer.$childLayoutComputer = outputs.layoutComputer
62+
}
63+
geometry.mutateBody(as: UnaryChildGeometry<Self>.self, invalidating: true) { geometry in
64+
geometry.$childLayoutComputer = outputs.layoutComputer
65+
}
66+
}
67+
if requestsLayoutComputer {
68+
outputs.layoutComputer = computer
69+
}
70+
return outputs
71+
}
72+
}
73+
74+
private struct UnaryChildGeometry<L>: Rule, AsyncAttribute, CustomStringConvertible
75+
where L: UnaryLayout, L.PlacementContextType == PlacementContext {
76+
@Attribute var parentSize: ViewSize
77+
@Attribute var layoutDirection: LayoutDirection
78+
@Attribute var parentLayoutComputer: LayoutComputer
79+
@OptionalAttribute var childLayoutComputer: LayoutComputer?
80+
81+
var value: ViewGeometry {
82+
let parentSize = parentSize
83+
let computer = parentLayoutComputer
84+
let placement = computer.withMutableEngine(type: UnaryLayoutEngine<L>.self) { engine in
85+
engine.childPlacement(at: parentSize)
86+
}
87+
let childProxy = LayoutProxy(
88+
context: AnyRuleContext(context),
89+
layoutComputer: $childLayoutComputer
90+
)
91+
return childProxy.finallyPlaced(
92+
at: placement,
93+
in: parentSize.value,
94+
layoutDirection: layoutDirection
95+
)
96+
}
97+
98+
var description: String {
99+
"\(L.self) → ChildGeometry"
100+
}
101+
}
102+
103+
private struct UnaryLayoutComputer<L>: StatefulRule, AsyncAttribute, CustomStringConvertible
104+
where L: UnaryLayout, L.PlacementContextType == PlacementContext {
105+
@Attribute var layout: L
106+
@Attribute var environment: EnvironmentValues
107+
@OptionalAttribute var childLayoutComputer: LayoutComputer?
108+
109+
typealias Value = LayoutComputer
110+
111+
mutating func updateValue() {
112+
let context = AnyRuleContext(context)
113+
let engine = UnaryLayoutEngine(
114+
layout: layout,
115+
layoutContext: SizeAndSpacingContext(context: context, environment: $environment),
116+
child: LayoutProxy(context: context, layoutComputer: $childLayoutComputer)
117+
)
118+
update(to: engine)
119+
}
120+
121+
var description: String {
122+
"\(L.self) → LayoutComputer"
123+
}
124+
}
125+
126+
private struct UnaryLayoutEngine<L>: LayoutEngine
127+
where L: UnaryLayout, L.PlacementContextType == PlacementContext {
128+
let layout: L
129+
130+
let layoutContext: SizeAndSpacingContext
131+
132+
let child: LayoutProxy
133+
134+
var dimensionsCache: ViewSizeCache = .init()
135+
136+
var placementCache: Cache3<ViewSize, _Placement> = .init()
137+
138+
init(layout: L, layoutContext: SizeAndSpacingContext, child: LayoutProxy) {
139+
self.layout = layout
140+
self.layoutContext = layoutContext
141+
self.child = child
142+
}
143+
144+
mutating func childPlacement(at size: ViewSize) -> _Placement {
145+
let layout = layout
146+
let child = child
147+
let context = PlacementContext(base: layoutContext, parentSize: size)
148+
return placementCache.get(size) {
149+
layout.placement(of: child, in: context)
150+
}
151+
}
152+
153+
func layoutPriority() -> Double {
154+
layout.layoutPriority(child: child)
155+
}
156+
157+
func ignoresAutomaticPadding() -> Bool {
158+
layout.ignoresAutomaticPadding(child: child)
159+
}
160+
161+
func spacing() -> Spacing {
162+
layout.spacing(in: layoutContext, child: child)
163+
}
164+
165+
mutating func sizeThatFits(_ proposedSize: _ProposedSize) -> CGSize {
166+
let layout = layout
167+
let context = layoutContext
168+
let child = child
169+
return dimensionsCache.get(proposedSize) {
170+
layout.sizeThatFits(
171+
in: proposedSize,
172+
context: context,
173+
child: child
174+
)
175+
}
176+
}
177+
178+
mutating func explicitAlignment(_ k: AlignmentKey, at viewSize: ViewSize) -> CGFloat? {
179+
let placement = childPlacement(at: viewSize)
180+
let dimensions = child.dimensions(in: placement.proposedSize_)
181+
let alignment = child.explicitAlignment(k, at: dimensions.size)
182+
if let alignment {
183+
return placement.frameOrigin(childSize: dimensions.size.value)[k.axis] + alignment
184+
} else {
185+
return alignment
186+
}
187+
}
188+
}
189+
190+
// MARK: - LayoutPositionQuery [6.4.41]
191+
192+
package struct LayoutPositionQuery: Rule, AsyncAttribute {
193+
@Attribute private var parentPosition: ViewOrigin
194+
@Attribute private var localPosition: ViewOrigin
195+
196+
package init(
197+
parentPosition: Attribute<ViewOrigin>,
198+
localPosition: Attribute<ViewOrigin>,
199+
) {
200+
_parentPosition = parentPosition
201+
_localPosition = localPosition
202+
}
203+
204+
package var value: ViewOrigin {
205+
let localPosition = localPosition
206+
let parentPosition = parentPosition
207+
return ViewOrigin(
208+
x: localPosition.x + parentPosition.x,
209+
y: localPosition.y + parentPosition.y
210+
)
211+
}
212+
}

Sources/OpenSwiftUICore/Tracing/LayoutTrace.swift

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,12 @@ package struct LayoutTrace {
5151
preconditionFailure("TODO")
5252
}
5353

54-
func traceCacheLookup(_ proposal: _ProposedSize, _ hit: Bool) {
55-
guard let recorder else {
56-
return
57-
}
58-
recorder.cacheLookup = (proposal, hit)
59-
}
60-
61-
func traceCacheLookup(_ proposal: CGSize, _ hit: Bool) {
62-
guard let recorder else {
63-
return
64-
}
65-
recorder.cacheLookup = (.init(proposal), hit)
66-
}
67-
6854
func traceChildGeometries(_ attribute: AnyAttribute?, at parentSize: ViewSize, origin: CGPoint, _ block: () -> [ViewGeometry]) -> [ViewGeometry] {
69-
preconditionFailure("TODO")
55+
activateFrameIfNeeded()
56+
CoreGlue.shared.startChildGeometries(.init(recorder: self, parentSize: parentSize, origin: origin, attributeID: attribute?.rawValue ?? .zero))
57+
let geometries = block()
58+
CoreGlue.shared.endChildGeometries(.init(recorder: self, geometries: geometries))
59+
return geometries
7060
}
7161

7262
func traceContentDescription(_ attribute: AnyAttribute?, _ description: String) {
@@ -88,12 +78,18 @@ extension LayoutTrace {
8878

8979
@inline(__always)
9080
package static func traceCacheLookup(_ proposal: _ProposedSize, _ hit: Bool) {
91-
recorder!.traceCacheLookup(proposal, hit)
81+
guard let recorder else {
82+
return
83+
}
84+
recorder.cacheLookup = (proposal, hit)
9285
}
9386

9487
@inline(__always)
9588
package static func traceCacheLookup(_ proposal: CGSize, _ hit: Bool) {
96-
recorder!.traceCacheLookup(proposal, hit)
89+
guard let recorder else {
90+
return
91+
}
92+
recorder.cacheLookup = (.init(proposal), hit)
9793
}
9894

9995
@inline(__always)

Sources/OpenSwiftUICore/Util/CoreGlue.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,27 +141,29 @@ extension CoreGlue {
141141
}
142142

143143
public struct StartChildGeometriesParameters {
144-
// package var recorder: LayoutTrace.Recorder
144+
package var recorder: LayoutTrace.Recorder
145145

146146
package var parentSize: ViewSize
147147

148148
package var origin: CGPoint
149149

150150
package var attributeID: UInt32
151151

152-
package init(parentSize: ViewSize, origin: CGPoint, attributeID: UInt32) {
152+
package init(recorder: LayoutTrace.Recorder, parentSize: ViewSize, origin: CGPoint, attributeID: UInt32) {
153+
self.recorder = recorder
153154
self.parentSize = parentSize
154155
self.origin = origin
155156
self.attributeID = attributeID
156157
}
157158
}
158159

159160
public struct EndChildGeometriesParameters {
160-
// package var recorder: LayoutTrace.Recorder
161+
package var recorder: LayoutTrace.Recorder
161162

162163
package var geometries: [ViewGeometry]
163164

164-
package init(geometries: [ViewGeometry]) {
165+
package init(recorder: LayoutTrace.Recorder, geometries: [ViewGeometry]) {
166+
self.recorder = recorder
165167
self.geometries = geometries
166168
}
167169
}

Sources/OpenSwiftUICore/View/Input/ViewInputs.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public struct _ViewInputs {
5454
get { base.phase }
5555
set {
5656
base.phase = newValue
57-
base.changedDebugProperties.formUnion(.phase)
57+
base.changedDebugProperties.insert(.phase)
5858
}
5959
}
6060

@@ -65,21 +65,21 @@ public struct _ViewInputs {
6565

6666
package var transform: Attribute<ViewTransform> {
6767
didSet {
68-
base.changedDebugProperties.formUnion(.transform)
68+
base.changedDebugProperties.insert(.transform)
6969
}
7070
}
7171

7272
package var position: Attribute<ViewOrigin> {
7373
didSet {
74-
base.changedDebugProperties.formUnion(.position)
74+
base.changedDebugProperties.insert(.position)
7575
}
7676
}
7777

7878
package var containerPosition: Attribute<ViewOrigin>
7979

8080
package var size: Attribute<ViewSize> {
8181
didSet {
82-
base.changedDebugProperties.formUnion(.size)
82+
base.changedDebugProperties.insert(.size)
8383
}
8484
}
8585

Sources/OpenSwiftUICore/View/Input/ViewOutputs.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ public struct _ViewOutputs {
1919
}
2020
set {
2121
_layoutComputer = OptionalAttribute(newValue)
22-
if !preferences.debugProperties.contains(.layoutComputer) {
23-
preferences.debugProperties.formUnion(.layoutComputer)
24-
}
22+
preferences.debugProperties.insert(.layoutComputer)
2523
}
2624
}
2725

0 commit comments

Comments
 (0)