From 004425e207da72a5ef7f4219b3bc1480b1082fae Mon Sep 17 00:00:00 2001 From: Kyle Date: Fri, 18 Jul 2025 23:47:53 +0800 Subject: [PATCH] Add AnimationModifier --- .../Animation/AnimationModifier.swift | 217 ++++++++++++++++++ .../Transaction/TransactionModifier.swift | 31 --- .../View/Archive/ArchivedViewInput.swift | 12 +- 3 files changed, 227 insertions(+), 33 deletions(-) create mode 100644 Sources/OpenSwiftUICore/Animation/Animation/AnimationModifier.swift diff --git a/Sources/OpenSwiftUICore/Animation/Animation/AnimationModifier.swift b/Sources/OpenSwiftUICore/Animation/Animation/AnimationModifier.swift new file mode 100644 index 000000000..84f8d6e9e --- /dev/null +++ b/Sources/OpenSwiftUICore/Animation/Animation/AnimationModifier.swift @@ -0,0 +1,217 @@ +// +// AnimationModifier.swift +// OpenSwiftUICore +// +// Audited: 6.5.4 +// Status: Blocked by ArichevedView +// ID: 530459AF10BEFD7ED901D8CE93C1E289 (SwiftUICore) + +import OpenGraphShims + +@available(OpenSwiftUI_v1_0, *) +@frozen +public struct _AnimationModifier: ViewModifier, PrimitiveViewModifier where Value: Equatable { + public var animation: Animation? + + public var value: Value + + @inlinable + public init(animation: Animation?, value: Value) { + self.animation = animation + self.value = value + } + + nonisolated static func _makeInputs( + modifier: _GraphValue, + inputs: inout _GraphInputs + ) { + let transactionSeed = GraphHost.currentHost.data.$transactionSeed + let seed = Attribute( + ValueTransactionSeed( + value: modifier.value[offset: { .of(&$0.value) }], + transactionSeed: transactionSeed + ) + ) + seed.flags = .active + inputs.transaction = Attribute( + ChildTransaction( + valueTransactionSeed: seed, + animation: modifier.value[offset: { .of(&$0.animation) }], + transaction: inputs.transaction, + transactionSeed: transactionSeed + ) + ) + } + + nonisolated public static func _makeView( + modifier: _GraphValue, + inputs: _ViewInputs, + body: @escaping (_Graph, _ViewInputs) -> _ViewOutputs + ) -> _ViewOutputs { + let archivedView = inputs.archivedView + if archivedView.isArchived { + // makeArchivedView + _openSwiftUIUnimplementedFailure() + } else { + var inputs = inputs + _makeInputs(modifier: modifier, inputs: &inputs.base) + return body(_Graph(), inputs) + } + } + + nonisolated public static func _makeViewList( + modifier: _GraphValue, + inputs: _ViewListInputs, + body: @escaping (_Graph, _ViewListInputs) -> _ViewListOutputs + ) -> _ViewListOutputs { + let archivedView = inputs.archivedView + if archivedView.isArchived { + // makeArchivedViewList + _openSwiftUIUnimplementedFailure() + } else { + var inputs = inputs + _makeInputs(modifier: modifier, inputs: &inputs.base) + return body(_Graph(), inputs) + } + } +} + +@available(*, unavailable) +extension _AnimationModifier: Sendable {} + +@available(OpenSwiftUI_v3_0, *) +@frozen +public struct _AnimationView: View, PrimitiveView where Content: Equatable, Content: View { + public var content: Content + + public var animation: Animation? + + @inlinable + public init(content: Content, animation: Animation?) { + self.content = content + self.animation = animation + } + + nonisolated static func _makeInputs( + view: _GraphValue, + inputs: inout _GraphInputs + ) -> _GraphValue { + let value = view.value[offset: { .of(&$0.content) }] + let transactionSeed = GraphHost.currentHost.data.$transactionSeed + let seed = Attribute( + ValueTransactionSeed( + value: value, + transactionSeed: transactionSeed + ) + ) + seed.flags = .active + inputs.transaction = Attribute( + ChildTransaction( + valueTransactionSeed: seed, + animation: view.value[offset: { .of(&$0.animation) }], + transaction: inputs.transaction, + transactionSeed: transactionSeed + ) + ) + return _GraphValue(value) + } + + nonisolated public static func _makeView( + view: _GraphValue, + inputs: _ViewInputs + ) -> _ViewOutputs { + var inputs = inputs + let value = _makeInputs(view: view, inputs: &inputs.base) + return Content.makeDebuggableView(view: value, inputs: inputs) + } + + nonisolated public static func _makeViewList( + view: _GraphValue, + inputs: _ViewListInputs + ) -> _ViewListOutputs { + var inputs = inputs + let value = _makeInputs(view: view, inputs: &inputs.base) + return Content.makeDebuggableViewList(view: value, inputs: inputs) + } + + nonisolated public static func _viewListCount(inputs: _ViewListCountInputs) -> Int? { + Content._viewListCount(inputs: inputs) + } +} + +@available(*, unavailable) +extension _AnimationView: Sendable {} + +@available(OpenSwiftUI_v1_0, *) +extension _AnimationModifier: Equatable {} + +@available(OpenSwiftUI_v1_0, *) +extension View { + @inlinable + nonisolated public func animation(_ animation: Animation?, value: V) -> some View where V: Equatable { + modifier(_AnimationModifier(animation: animation, value: value)) + } +} + +@available(OpenSwiftUI_v3_0, *) +extension View where Self: Equatable { + @available(OpenSwiftUI_v3_0, *) + @inlinable + nonisolated public func animation(_ animation: Animation?) -> some View { + _AnimationView(content: self, animation: animation) + } +} + +/// A stateful rule that tracks value changes to determine when to update transactions. +/// +/// This structure maintains state about a value being monitored, comparing new values +/// with the previous ones to detect changes. +struct ValueTransactionSeed: StatefulRule, AsyncAttribute where V: Equatable { + @Attribute var value: V + @Attribute var transactionSeed: UInt32 + var oldValue: V? + + init(value: Attribute, transactionSeed: Attribute, oldValue: V? = nil) { + self._value = value + self._transactionSeed = transactionSeed + self.oldValue = oldValue + } + + typealias Value = UInt32 + + mutating func updateValue() { + let newValue = value + if let oldValue { + guard oldValue != newValue else { + return + } + value = Graph.withoutUpdate { transactionSeed} + } else { + value = .max + } + oldValue = newValue + } +} + +// TODO: Archived stuff + +private struct ChildTransaction: Rule, AsyncAttribute { + @Attribute var valueTransactionSeed: UInt32 + @Attribute var animation: Animation? + @Attribute var transaction: Transaction + @Attribute var transactionSeed: UInt32 + + var value: Transaction { + var transaction = transaction + guard !transaction.disablesAnimations else { + return transaction + } + let oldTransactionSeed = Graph.withoutUpdate { transactionSeed } + guard valueTransactionSeed == oldTransactionSeed else { + return transaction + } + transaction.animation = animation + Swift.assert(transactionSeed == oldTransactionSeed) + return transaction + } +} diff --git a/Sources/OpenSwiftUICore/Animation/Transaction/TransactionModifier.swift b/Sources/OpenSwiftUICore/Animation/Transaction/TransactionModifier.swift index 45098e91c..0a00ea418 100644 --- a/Sources/OpenSwiftUICore/Animation/Transaction/TransactionModifier.swift +++ b/Sources/OpenSwiftUICore/Animation/Transaction/TransactionModifier.swift @@ -96,37 +96,6 @@ public struct _ValueTransactionModifier: ViewModifier, _GraphInputsModifi @available(*, unavailable) extension _ValueTransactionModifier: Sendable {} -/// A stateful rule that tracks value changes to determine when to update transactions. -/// -/// This structure maintains state about a value being monitored, comparing new values -/// with the previous ones to detect changes. -struct ValueTransactionSeed: StatefulRule, AsyncAttribute where V: Equatable { - var _value: Attribute - var _transactionSeed: Attribute - var oldValue: V? - - init(value: Attribute, transactionSeed: Attribute, oldValue: V? = nil) { - self._value = value - self._transactionSeed = transactionSeed - self.oldValue = oldValue - } - - typealias Value = UInt32 - - mutating func updateValue() { - let newValue = _value.value - if let oldValue { - guard oldValue != newValue else { - return - } - value = Graph.withoutUpdate { _transactionSeed.value } - } else { - value = .max - } - oldValue = newValue - } -} - private struct ChildValueTransaction: Rule, AsyncAttribute { @Attribute var valueTransactionSeed: UInt32 @Attribute var transform: (inout Transaction) -> () diff --git a/Sources/OpenSwiftUICore/View/Archive/ArchivedViewInput.swift b/Sources/OpenSwiftUICore/View/Archive/ArchivedViewInput.swift index 92db69a4b..501c635e1 100644 --- a/Sources/OpenSwiftUICore/View/Archive/ArchivedViewInput.swift +++ b/Sources/OpenSwiftUICore/View/Archive/ArchivedViewInput.swift @@ -149,7 +149,7 @@ extension ArchivedViewInput.DeploymentVersion { extension _GraphInputs { @inline(__always) - var archivedView: ArchivedViewInput.Value { + package var archivedView: ArchivedViewInput.Value { get { self[ArchivedViewInput.self] } set { self[ArchivedViewInput.self] = newValue } } @@ -157,7 +157,15 @@ extension _GraphInputs { extension _ViewInputs { @inline(__always) - var archivedView: ArchivedViewInput.Value { + package var archivedView: ArchivedViewInput.Value { + get { self[ArchivedViewInput.self] } + set { self[ArchivedViewInput.self] = newValue } + } +} + +extension _ViewListInputs { + @inline(__always) + package var archivedView: ArchivedViewInput.Value { get { self[ArchivedViewInput.self] } set { self[ArchivedViewInput.self] = newValue } }