diff --git a/Sources/SwiftCrossUI/Scenes/Modifiers/SceneEnvironmentModifier.swift b/Sources/SwiftCrossUI/Scenes/Modifiers/SceneEnvironmentModifier.swift new file mode 100644 index 00000000000..95f00a8c9e1 --- /dev/null +++ b/Sources/SwiftCrossUI/Scenes/Modifiers/SceneEnvironmentModifier.swift @@ -0,0 +1,84 @@ +extension Scene { + /// Modifies the scene's environment. + /// + /// - Parameters: + /// - keyPath: The key path to the environment value to update. + /// - newValue: The new value. + public func environment( + _ keyPath: WritableKeyPath, + _ newValue: T + ) -> some Scene { + SceneEnvironmentModifier(self) { environment in + environment.with(keyPath, newValue) + } + } + + /// Modifies the scene's environment. + /// + /// - Parameters: + /// - keyPath: The key path to the environment value to update. + /// - transform: A closure that transforms the environment at `keyPath`. + public func transformEnvironment( + _ keyPath: WritableKeyPath, + transform: @escaping (inout T) -> Void + ) -> some Scene { + SceneEnvironmentModifier(self) { environment in + var value = environment[keyPath: keyPath] + transform(&value) + return environment.with(keyPath, value) + } + } +} + +struct SceneEnvironmentModifier: Scene { + typealias Node = SceneEnvironmentModifierNode + + var content: Content + var modification: (EnvironmentValues) -> EnvironmentValues + + var commands: Commands { content.commands } + + init( + _ content: Content, + modification: @escaping (EnvironmentValues) -> EnvironmentValues + ) { + self.content = content + self.modification = modification + } +} + +final class SceneEnvironmentModifierNode: SceneGraphNode { + typealias NodeScene = SceneEnvironmentModifier + + var modification: (EnvironmentValues) -> EnvironmentValues + var contentNode: Content.Node + + init( + from scene: NodeScene, + backend: Backend, + environment: EnvironmentValues + ) { + self.modification = scene.modification + contentNode = Content.Node( + from: scene.content, + backend: backend, + environment: modification(environment) + ) + } + + func update( + _ newScene: NodeScene?, + backend: Backend, + environment: EnvironmentValues + ) { + if let newScene { + self.modification = newScene.modification + } + + contentNode.update( + newScene?.content, + backend: backend, + environment: modification(environment) + ) + } +}