Skip to content

Commit 54ce5f0

Browse files
authored
Add PlacementContext implementation (#292)
1 parent 8eb03b6 commit 54ce5f0

File tree

3 files changed

+232
-10
lines changed

3 files changed

+232
-10
lines changed

Sources/OpenSwiftUICore/Layout/Context/PositionAwarePlacementContext.swift

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
//
2+
// PlacementContext.swift
3+
// OpenSwiftUICore
4+
//
5+
// Audited for iOS 18.0
6+
// Status: Complete
7+
// ID: BA60BF7120E939C5C25B2A488163D4AC
8+
9+
package import Foundation
10+
package import OpenGraphShims
11+
12+
// MARK: - EnvironmentFetch
13+
14+
private struct EnvironmentFetch<Value>: Rule, AsyncAttribute, Hashable {
15+
@Attribute var environment: EnvironmentValues
16+
var keyPath: KeyPath<EnvironmentValues, Value>
17+
18+
var value: Value {
19+
environment[keyPath: keyPath]
20+
}
21+
}
22+
23+
// MARK: - SizeAndSpacingContext
24+
25+
@dynamicMemberLookup
26+
package struct SizeAndSpacingContext {
27+
package var context: AnyRuleContext
28+
29+
var owner: AnyAttribute
30+
31+
@Attribute var environment: EnvironmentValues
32+
33+
package init(context: AnyRuleContext, owner: AnyAttribute? = nil, environment: Attribute<EnvironmentValues>) {
34+
self.context = context
35+
self.owner = owner ?? context.attribute
36+
self._environment = environment
37+
}
38+
39+
package init(_ context: PlacementContext) {
40+
self.context = context.context
41+
self.owner = context.owner
42+
self._environment = context.$environment
43+
}
44+
45+
package subscript<T>(dynamicMember keyPath: KeyPath<EnvironmentValues, T>) -> T {
46+
EnvironmentFetch(environment: $environment, keyPath: keyPath)
47+
.cachedValue(options: [._1], owner: owner)
48+
}
49+
50+
package func update<T>(_ body: () -> T) -> T {
51+
var result: T?
52+
context.update {
53+
result = body()
54+
}
55+
return result!
56+
}
57+
}
58+
59+
// MARK: - PlacementContext
60+
61+
@dynamicMemberLookup
62+
package struct PlacementContext {
63+
var context: AnyRuleContext
64+
65+
var owner: AnyAttribute
66+
67+
@Attribute var environment: EnvironmentValues
68+
69+
enum ParentSize {
70+
case eager(ViewSize)
71+
case lazy(Attribute<ViewSize>)
72+
}
73+
74+
private let parentSize: ParentSize
75+
76+
package init(
77+
context: AnyRuleContext,
78+
owner: AnyAttribute? = nil,
79+
size: Attribute<ViewSize>,
80+
environment: Attribute<EnvironmentValues>,
81+
transform: Attribute<ViewTransform>,
82+
position: Attribute<ViewOrigin>,
83+
safeAreaInsets: OptionalAttribute<SafeAreaInsets>
84+
) {
85+
self.context = context
86+
self.owner = owner ?? context.attribute
87+
self._environment = environment
88+
self.parentSize = .lazy(size)
89+
}
90+
91+
package init(
92+
context: AnyRuleContext,
93+
size: Attribute<ViewSize>,
94+
environment: Attribute<EnvironmentValues>,
95+
transform: Attribute<ViewTransform>,
96+
position: Attribute<ViewOrigin>,
97+
safeAreaInsets: OptionalAttribute<SafeAreaInsets>
98+
) {
99+
self.context = context
100+
self.owner = context.attribute
101+
self._environment = environment
102+
self.parentSize = .lazy(size)
103+
}
104+
105+
package init(base: SizeAndSpacingContext, parentSize: ViewSize) {
106+
self.context = base.context
107+
self.owner = base.owner
108+
self._environment = base.$environment
109+
self.parentSize = .eager(parentSize)
110+
}
111+
112+
package var size: CGSize {
113+
switch parentSize {
114+
case let .eager(viewSize): viewSize.value
115+
case let .lazy(attribute): context[attribute].value
116+
}
117+
}
118+
119+
package var proposedSize: _ProposedSize {
120+
if isLinkedOnOrAfter(.v3) {
121+
switch parentSize {
122+
case let .eager(viewSize): viewSize.proposal
123+
case let .lazy(attribute): context[attribute].proposal
124+
}
125+
} else {
126+
switch parentSize {
127+
case let .eager(viewSize): _ProposedSize(viewSize.value)
128+
case let .lazy(attribute): _ProposedSize(context[attribute].value)
129+
}
130+
}
131+
}
132+
133+
package subscript<T>(dynamicMember keyPath: KeyPath<EnvironmentValues, T>) -> T {
134+
EnvironmentFetch(environment: $environment, keyPath: keyPath)
135+
.cachedValue(options: [._1], owner: owner)
136+
}
137+
}
138+
139+
// MARK: - _PositionAwarePlacementContext
140+
141+
@dynamicMemberLookup
142+
package struct _PositionAwarePlacementContext {
143+
var context: AnyRuleContext
144+
var owner: AnyAttribute
145+
var _size: Attribute<ViewSize>
146+
var _environment: Attribute<EnvironmentValues>
147+
var _transform: Attribute<ViewTransform>
148+
var _position: Attribute<ViewOrigin>
149+
var _safeAreaInsets: OptionalAttribute<SafeAreaInsets>
150+
151+
package init(
152+
context: AnyRuleContext,
153+
owner: AnyAttribute? = nil,
154+
size: Attribute<ViewSize>,
155+
environment: Attribute<EnvironmentValues>,
156+
transform: Attribute<ViewTransform>,
157+
position: Attribute<ViewOrigin>,
158+
safeAreaInsets: OptionalAttribute<SafeAreaInsets>
159+
) {
160+
self.context = context
161+
self.owner = owner ?? context.attribute
162+
self._size = size
163+
self._environment = environment
164+
self._transform = transform
165+
self._position = position
166+
self._safeAreaInsets = safeAreaInsets
167+
}
168+
169+
package init(
170+
context: AnyRuleContext,
171+
size: Attribute<ViewSize>,
172+
environment: Attribute<EnvironmentValues>,
173+
transform: Attribute<ViewTransform>,
174+
position: Attribute<ViewOrigin>,
175+
safeAreaInsets: OptionalAttribute<SafeAreaInsets>
176+
) {
177+
self.context = context
178+
self.owner = context.attribute
179+
self._size = size
180+
self._environment = environment
181+
self._transform = transform
182+
self._position = position
183+
self._safeAreaInsets = safeAreaInsets
184+
}
185+
186+
package var size: CGSize {
187+
context[_size].value
188+
}
189+
190+
package var proposedSize: _ProposedSize {
191+
if isLinkedOnOrAfter(.v3) {
192+
context[_size].proposal
193+
} else {
194+
_ProposedSize(context[_size].value)
195+
}
196+
}
197+
198+
package var unadjustedSafeAreaInsets: SafeAreaInsets? {
199+
guard let attribute = _safeAreaInsets.attribute else {
200+
return nil
201+
}
202+
return context[attribute]
203+
}
204+
205+
package var transform: ViewTransform {
206+
context[_transform].withPosition(context[_position].value)
207+
}
208+
209+
package subscript<T>(dynamicMember keyPath: KeyPath<EnvironmentValues, T>) -> T {
210+
EnvironmentFetch(environment: _environment, keyPath: keyPath)
211+
.cachedValue(options: [._1], owner: owner)
212+
}
213+
}
214+
215+
extension ViewTransformable {
216+
package mutating func convert(from space: CoordinateSpace, to context: _PositionAwarePlacementContext) {
217+
convert(from: space, transform: context.transform)
218+
}
219+
220+
package mutating func convert(from context: _PositionAwarePlacementContext, to space: CoordinateSpace) {
221+
convert(to: space, transform: context.transform)
222+
}
223+
}

Sources/OpenSwiftUICore/OpenGraph/Attribute/AnyAttributeFix.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,13 @@ extension Subgraph {
164164
preconditionFailure("#39")
165165
}
166166
}
167+
168+
extension Rule where Self: Hashable {
169+
package func cachedValue(
170+
options: OGCachedValueOptions = [],
171+
owner: AnyAttribute?
172+
) -> Value {
173+
preconditionFailure("#39")
174+
}
175+
}
167176
#endif

0 commit comments

Comments
 (0)