Skip to content

Commit ff45976

Browse files
authored
Add SceneModifier support (#752)
1 parent 3bd3e5c commit ff45976

File tree

6 files changed

+251
-45
lines changed

6 files changed

+251
-45
lines changed

Sources/OpenSwiftUI/App/App/AppGraph.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,10 @@ protocol AppGraphObserver: AnyObject {
145145

146146
// MARK: - AppBodyAccessor
147147

148-
private struct AppBodyAccessor<Container: App>: BodyAccessor {
149-
typealias Body = Container.Body
148+
private struct AppBodyAccessor<Application>: BodyAccessor where Application: App {
149+
typealias Container = Application
150+
151+
typealias Body = Application.Body
150152

151153
func updateBody(of container: Container, changed: Bool) {
152154
guard changed else {

Sources/OpenSwiftUI/Modifier/SceneModifier/PrimitiveSceneModifier.swift

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
//
2+
// SceneModifier.swift
3+
// OpenSwiftUI
4+
//
5+
// Audited for 6.5.4
6+
// Status: Complete
7+
// ID: 88477CCB0AEDAD452D0818B33FCEDC5F (SwiftUI)
8+
9+
import OpenAttributeGraphShims
10+
import OpenSwiftUICore
11+
12+
// MARK: - _SceneModifier
13+
14+
/// A modifier that can be applied to a scene or other scene modifier,
15+
/// producing a different version of the original value.
16+
@available(OpenSwiftUI_v2_0, *)
17+
public protocol _SceneModifier {
18+
19+
/// The type of scene representing the body of `Self`.
20+
associatedtype Body: Scene
21+
22+
/// Returns the current body of `self`. `content` is a proxy for
23+
/// the scene that will have the modifier represented by `Self`
24+
/// applied to it.
25+
@SceneBuilder
26+
func body(content: SceneContent) -> Body
27+
28+
/// The content scene type passed to `body()`.
29+
typealias SceneContent = _SceneModifier_Content<Self>
30+
31+
static func _makeScene(
32+
modifier: _GraphValue<Self>,
33+
inputs: _SceneInputs,
34+
body: @escaping (_Graph, _SceneInputs) -> _SceneOutputs
35+
) -> _SceneOutputs
36+
}
37+
38+
// MARK: - PrimitiveSceneModifier
39+
40+
protocol PrimitiveSceneModifier: _SceneModifier where Body == Never {}
41+
42+
extension _SceneModifier {
43+
func sceneBodyError() -> Never {
44+
preconditionFailure("body() should not be called on \(self).")
45+
}
46+
}
47+
48+
@available(OpenSwiftUI_v2_0, *)
49+
extension _SceneModifier where Body == Never {
50+
public func body(content: SceneContent) -> Body {
51+
sceneBodyError()
52+
}
53+
}
54+
55+
// MARK: - _GraphInputsModifier + _SceneModifier
56+
57+
@available(OpenSwiftUI_v2_0, *)
58+
extension _SceneModifier where Self: _GraphInputsModifier, Body == Never {
59+
public static func _makeScene(
60+
modifier: _GraphValue<Self>,
61+
inputs: _SceneInputs,
62+
body: @escaping (_Graph, _SceneInputs) -> _SceneOutputs
63+
) -> _SceneOutputs {
64+
var inputs = inputs
65+
_makeInputs(modifier: modifier, inputs: &inputs.base)
66+
return body(.init(), inputs)
67+
}
68+
}
69+
70+
// MARK: - EmptyModifier + _SceneModifier
71+
72+
@available(OpenSwiftUI_v2_0, *)
73+
extension EmptyModifier: _SceneModifier {
74+
@MainActor
75+
@preconcurrency
76+
public static func _makeScene(
77+
modifier: _GraphValue<EmptyModifier>,
78+
inputs: _SceneInputs,
79+
body: @escaping (_Graph, _SceneInputs) -> _SceneOutputs
80+
) -> _SceneOutputs {
81+
body(.init(), inputs)
82+
}
83+
}
84+
85+
// MARK: - Scene + modifier
86+
87+
@available(OpenSwiftUI_v2_0, *)
88+
extension Scene {
89+
/// Returns a new scene representing `self` with `modifier` applied
90+
/// to it.
91+
@inlinable
92+
@MainActor
93+
@preconcurrency
94+
internal func modifier<T>(_ modifier: T) -> ModifiedContent<Self, T> {
95+
return .init(content: self, modifier: modifier)
96+
}
97+
}
98+
99+
@_spi(Private)
100+
@available(OpenSwiftUI_v4_0, *)
101+
extension Scene {
102+
@_spi(Private)
103+
nonisolated public func sceneModifier<T>(_ modifier: T) -> ModifiedContent<Self, T> {
104+
self.modifier(modifier)
105+
}
106+
}
107+
108+
// MARK: - ModifiedContent + Scene & _SceneModifier
109+
110+
@available(OpenSwiftUI_v2_0, *)
111+
extension ModifiedContent: Scene where Content: Scene, Modifier: _SceneModifier {
112+
nonisolated public static func _makeScene(
113+
scene: _GraphValue<Self>,
114+
inputs: _SceneInputs
115+
) -> _SceneOutputs {
116+
Modifier._makeScene(
117+
modifier: scene[offset: { .of(&$0.modifier) }],
118+
inputs: inputs
119+
) {
120+
Content._makeScene(
121+
scene: scene[offset: { .of(&$0.content) }],
122+
inputs: $1
123+
)
124+
}
125+
}
126+
127+
@MainActor
128+
@preconcurrency
129+
public var body: Body {
130+
sceneBodyError()
131+
}
132+
}
133+
134+
@available(OpenSwiftUI_v2_0, *)
135+
extension ModifiedContent: _SceneModifier where Content: _SceneModifier, Modifier: _SceneModifier {
136+
public static func _makeScene(
137+
modifier: _GraphValue<Self>,
138+
inputs: _SceneInputs,
139+
body: @escaping (_Graph, _SceneInputs) -> _SceneOutputs
140+
) -> _SceneOutputs {
141+
Modifier._makeScene(
142+
modifier: modifier[offset: { .of(&$0.modifier) }],
143+
inputs: inputs
144+
) {
145+
Content._makeScene(
146+
modifier: modifier[offset: { .of(&$0.content) }],
147+
inputs: $1,
148+
body: body
149+
)
150+
}
151+
}
152+
}
153+
154+
// MARK: - _SceneModifier + concat
155+
156+
@available(OpenSwiftUI_v2_0, *)
157+
extension _SceneModifier {
158+
/// Returns a new modifier that is the result of concatenating
159+
/// `self` with `modifier`.
160+
@inlinable
161+
internal func concat<T>(_ modifier: T) -> ModifiedContent<Self, T> {
162+
return .init(content: self, modifier: modifier)
163+
}
164+
}
165+
166+
// MARK: - _SceneModifier_Content
167+
168+
@available(OpenSwiftUI_v2_0, *)
169+
@MainActor
170+
@preconcurrency
171+
public struct _SceneModifier_Content<Modifier>: PrimitiveScene where Modifier: _SceneModifier {
172+
nonisolated public static func _makeScene(
173+
scene: _GraphValue<Self>,
174+
inputs: _SceneInputs
175+
) -> _SceneOutputs {
176+
var inputs = inputs
177+
guard let body = inputs.popLast(BodyInput.self) else {
178+
return .init()
179+
}
180+
return body(.init(), inputs)
181+
}
182+
183+
fileprivate struct BodyInput: SceneInput {
184+
typealias BodyInputElement = (_Graph, _SceneInputs) -> _SceneOutputs
185+
186+
static var defaultValue: Stack<BodyInputElement> { .init() }
187+
}
188+
}
189+
190+
@available(*, unavailable)
191+
extension _SceneModifier_Content: Sendable {}
192+
193+
// MARK: - _SceneModifier default implementation
194+
195+
@available(OpenSwiftUI_v2_0, *)
196+
extension _SceneModifier {
197+
public static func _makeScene(
198+
modifier: _GraphValue<Self>,
199+
inputs: _SceneInputs,
200+
body: @escaping (_Graph, _SceneInputs) -> _SceneOutputs
201+
) -> _SceneOutputs {
202+
let fields = DynamicPropertyCache.fields(of: Self.self)
203+
var inputs = inputs
204+
let (scene, buffer) = makeBody(modifier: modifier, inputs: &inputs.base, fields: fields)
205+
inputs.append(body, to: _SceneModifier_Content<Self>.BodyInput.self)
206+
let outputs = Body._makeScene(scene: scene, inputs: inputs)
207+
if let buffer {
208+
buffer.traceMountedProperties(to: modifier, fields: fields)
209+
}
210+
return outputs
211+
}
212+
213+
private static func makeBody(
214+
modifier: _GraphValue<Self>,
215+
inputs: inout _GraphInputs,
216+
fields: DynamicPropertyCache.Fields
217+
) -> (_GraphValue<Body>, _DynamicPropertyBuffer?) {
218+
precondition(
219+
Metadata(Self.self).isValueType,
220+
"view modifiers must be value types: \(Self.self)"
221+
)
222+
let accessor = AppModifierBodyAccessor<Self>()
223+
return accessor.makeBody(container: modifier, inputs: &inputs, fields: fields)
224+
}
225+
}
226+
227+
// MARK: - AppModifierBodyAccessor
228+
229+
private struct AppModifierBodyAccessor<Modifier>: BodyAccessor where Modifier: _SceneModifier {
230+
typealias Container = Modifier
231+
232+
typealias Body = Modifier.Body
233+
234+
func updateBody(of container: Container, changed: Bool) {
235+
guard changed else {
236+
return
237+
}
238+
setBody {
239+
container.body(content: .init())
240+
}
241+
}
242+
}

Sources/OpenSwiftUI/Modifier/SceneModifier/TODO/_SceneModifier.swift

Lines changed: 0 additions & 27 deletions
This file was deleted.

Sources/OpenSwiftUI/Modifier/SceneModifier/TODO/_SceneModifier_Content.swift

Lines changed: 0 additions & 5 deletions
This file was deleted.

Sources/OpenSwiftUICore/Modifier/CustomViewModifier.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,15 +329,17 @@ private struct BodyCountInput<V>: ViewInput {
329329

330330
// MARK: - ModifierBodyAccessor
331331

332-
private struct ModifierBodyAccessor<Container>: BodyAccessor where Container: ViewModifier {
333-
typealias Body = Container.Body
332+
private struct ModifierBodyAccessor<Modifier>: BodyAccessor where Modifier: ViewModifier {
333+
typealias Container = Modifier
334+
335+
typealias Body = Modifier.Body
334336

335337
func updateBody(of container: Container, changed: Bool) {
336338
guard changed else {
337339
return
338340
}
339341
setBody {
340-
container.body(content: Container.Content())
342+
container.body(content: .init())
341343
}
342344
}
343345
}

0 commit comments

Comments
 (0)