From c55f0d5a0dc64010018eac333f7b8f7386d164b6 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 29 Jun 2025 16:26:57 +0800 Subject: [PATCH 1/2] Add API interface for MapGesture and RepeatGesture --- .../OpenSwiftUICore/Event/Event_TODO.swift | 6 -- .../Event/Gesture/MapGesture.swift | 68 +++++++++++++++++++ .../Event/Gesture/RepeatGesture.swift | 59 ++++++++++++++++ 3 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 Sources/OpenSwiftUICore/Event/Gesture/MapGesture.swift create mode 100644 Sources/OpenSwiftUICore/Event/Gesture/RepeatGesture.swift diff --git a/Sources/OpenSwiftUICore/Event/Event_TODO.swift b/Sources/OpenSwiftUICore/Event/Event_TODO.swift index 5adca6098..d14f76656 100644 --- a/Sources/OpenSwiftUICore/Event/Event_TODO.swift +++ b/Sources/OpenSwiftUICore/Event/Event_TODO.swift @@ -1,9 +1,3 @@ // FIXME -package struct _MapGesture: PrimitiveGesture where Content: Gesture { - nonisolated package static func _makeGesture(gesture: _GraphValue<_MapGesture>, inputs: _GestureInputs) -> _GestureOutputs { - preconditionFailure("TODO") - } -} - package struct AnyGesture: PrimitiveGesture {} diff --git a/Sources/OpenSwiftUICore/Event/Gesture/MapGesture.swift b/Sources/OpenSwiftUICore/Event/Gesture/MapGesture.swift new file mode 100644 index 000000000..dd5638cf2 --- /dev/null +++ b/Sources/OpenSwiftUICore/Event/Gesture/MapGesture.swift @@ -0,0 +1,68 @@ +// +// MapGesture.swift +// OpenSwiftUICore +// +// Status: Unimplmented +// ID: EA8BFBF553A9179E7F3A85C72F795A9F (SwiftUICore) + +package struct MapGesture: GestureModifier { + package var body: (GesturePhase) -> GesturePhase + + package init(_ body: @escaping (GesturePhase) -> GesturePhase) { + self.body = body + } + + package init(_ body: @escaping (From) -> To) { + self.body = { $0.map(body) } + } + + package static func _makeGesture( + modifier: _GraphValue, + inputs: _GestureInputs, + body: (_GestureInputs) -> _GestureOutputs + ) -> _GestureOutputs { + + + +// ModifierGesture>.makeDebuggableGesture( +// gesture: modifier[offset: { .of(&$0.body) }], +// inputs: inputs +// ) + preconditionFailure("TODO") + } + + package typealias BodyValue = From + + package typealias Value = To +} + +extension Gesture { + package func mapPhase( + _ body: @escaping (GesturePhase) -> GesturePhase + ) -> ModifierGesture, Self> { + modifier(MapGesture(body)) + } + + public func map(_ body: @escaping (Value) -> T) -> _MapGesture { + preconditionFailure("TODO") + } + + package func discrete(_ enabled: Bool = true) -> ModifierGesture, Self> { + preconditionFailure("TODO") + } +} + +@available(OpenSwiftUI_v1_0, *) +public struct _MapGesture: PrimitiveGesture where Content: Gesture { + public static func _makeGesture( + gesture: _GraphValue<_MapGesture>, + inputs: _GestureInputs + ) -> _GestureOutputs { + preconditionFailure("TODO") + } + + public typealias Body = Never +} + +@available(*, unavailable) +extension _MapGesture: Sendable {} diff --git a/Sources/OpenSwiftUICore/Event/Gesture/RepeatGesture.swift b/Sources/OpenSwiftUICore/Event/Gesture/RepeatGesture.swift new file mode 100644 index 000000000..2f0aef826 --- /dev/null +++ b/Sources/OpenSwiftUICore/Event/Gesture/RepeatGesture.swift @@ -0,0 +1,59 @@ +// +// RepeatGesture.swift +// OpenSwiftUICore +// +// Status: Unimplmented +// ID: BECD07FC80B4CA0BF429B041392E806A (SwiftUICore) + +import OpenGraphShims + +// MARK: - RepeatGesture [6.5.4] + +extension Gesture { + package func repeatCount( + _ count: Int, + maximumDelay: Double = 0.35 + ) -> ModifierGesture, Self> { + modifier(RepeatGesture(count: count, maximumDelay: maximumDelay)) + } +} + +package struct RepeatGesture: GestureModifier { + package var count: Int + package var maximumDelay: Double + + package init(count: Int, maximumDelay: Double = 0.35) { + self.count = count + self.maximumDelay = maximumDelay + } + + package static func _makeGesture( + modifier: _GraphValue>, + inputs: _GestureInputs, + body: (_GestureInputs) -> _GestureOutputs + ) -> _GestureOutputs { + openSwiftUIUnimplementedFailure() + } +} + +private struct RepeatPhase: ResettableGestureRule { + @Attribute var modifier: RepeatGesture + @Attribute var phase: GesturePhase + @Attribute var time: Time + @Attribute var resetSeed: UInt32 + @Attribute var resetDelta: UInt32 + var useGestureGraph: Bool + var deadline: Time? + var index: UInt32 + var lastResetSeed: UInt32 + + typealias PhaseValue = V + typealias Value = GesturePhase + + mutating func updateValue() { + guard resetIfNeeded() else { + return + } + openSwiftUIUnimplementedFailure() + } +} From f1042281089a4f0f26cf62a970d1edfd5d271adf Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 29 Jun 2025 16:27:18 +0800 Subject: [PATCH 2/2] Add AnyGesture implementation --- .../OpenSwiftUICore/Event/Event_TODO.swift | 3 - .../Event/Gesture/AnyGesture.swift | 178 ++++++++++++++++++ 2 files changed, 178 insertions(+), 3 deletions(-) delete mode 100644 Sources/OpenSwiftUICore/Event/Event_TODO.swift create mode 100644 Sources/OpenSwiftUICore/Event/Gesture/AnyGesture.swift diff --git a/Sources/OpenSwiftUICore/Event/Event_TODO.swift b/Sources/OpenSwiftUICore/Event/Event_TODO.swift deleted file mode 100644 index d14f76656..000000000 --- a/Sources/OpenSwiftUICore/Event/Event_TODO.swift +++ /dev/null @@ -1,3 +0,0 @@ -// FIXME - -package struct AnyGesture: PrimitiveGesture {} diff --git a/Sources/OpenSwiftUICore/Event/Gesture/AnyGesture.swift b/Sources/OpenSwiftUICore/Event/Gesture/AnyGesture.swift new file mode 100644 index 000000000..75f2d70eb --- /dev/null +++ b/Sources/OpenSwiftUICore/Event/Gesture/AnyGesture.swift @@ -0,0 +1,178 @@ +// +// AnyGesture.swift +// OpenSwiftUICore +// +// Status: Complete +// ID: 9726BF9F3BA5F571B5F201AD7C8C86F0 (SwiftUICore) + +import OpenGraphShims + +// MARK: - AnyGesture [6.5.4] + +/// A type-erased gesture. +@available(OpenSwiftUI_v1_0, *) +@frozen +public struct AnyGesture: PrimitiveGesture, Gesture { + fileprivate var storage: AnyGestureStorageBase + + /// Creates an instance from another gesture. + /// + /// - Parameter gesture: A gesture that you use to create a new gesture. + public init(_ gesture: T) where Value == T.Value, T: Gesture { + storage = AnyGestureStorage(gesture: gesture) + } + + nonisolated public static func _makeGesture( + gesture: _GraphValue, + inputs: _GestureInputs + ) -> _GestureOutputs { + let outputs: _GestureOutputs = inputs.makeIndirectOutputs() + let info = Attribute(AnyGestureInfo( + gesture: gesture.value, + inputs: inputs, + outputs: outputs, + parentSubgraph: Subgraph.current! + )) + info.setFlags(.active, mask: .mask) + outputs.setIndirectDependency(info.identifier) + return outputs + } +} + +@available(*, unavailable) +extension AnyGesture: Sendable {} + +@usableFromInline +class AnyGestureStorageBase { + + fileprivate func matches(_ other: AnyGestureStorageBase) -> Bool { + preconditionFailure("") + } + + fileprivate func makeChild( + uniqueId: UInt32, + container: Attribute.Value>, + inputs: _GestureInputs + ) -> _GestureOutputs { + preconditionFailure("") + } + + fileprivate func updateChild(context: AnyRuleContext) { + preconditionFailure("") + } +} + +@available(*, unavailable) +extension AnyGestureStorageBase: Sendable {} + +private final class AnyGestureStorage: AnyGestureStorageBase where G: Gesture { + var gesture: G + + init(gesture: G) { + self.gesture = gesture + } + + override func matches(_ other: AnyGestureStorageBase) -> Bool { + other is AnyGestureStorage + } + + override func makeChild( + uniqueId: UInt32, + container: Attribute.Value>, + inputs: _GestureInputs + ) -> _GestureOutputs { + let child = Attribute(AnyGestureChild(info: container, uniqueId: uniqueId)) + return G.makeDebuggableGesture(gesture: _GraphValue(child), inputs: inputs) + } + + override func updateChild(context: AnyRuleContext) { + context.unsafeCast(to: G.self).value = gesture + } +} + +private struct AnyGestureInfo: StatefulRule { + @Attribute var gesture: AnyGesture + var inputs: _GestureInputs + var outputs: _GestureOutputs + let parentSubgraph: Subgraph + var oldInfo: Value? + + struct Value { + var item: AnyGestureStorageBase + var subgraph: Subgraph + var uniqueId: UInt32 + } + + mutating func updateValue() { + let newInfo: Value + if let oldInfo, oldInfo.item.matches(gesture.storage) { + newInfo = Value(item: gesture.storage, subgraph: oldInfo.subgraph, uniqueId: oldInfo.uniqueId) + } else { + let uniqueId: UInt32 + if let oldInfo { + eraseItem(info: oldInfo) + uniqueId = oldInfo.uniqueId &+ 1 + } else { + uniqueId = 0 + } + newInfo = makeItem(gesture.storage, uniqueId: uniqueId) + } + value = newInfo + oldInfo = newInfo + } + + func makeItem( + _ storage: AnyGestureStorageBase, + uniqueId: UInt32 + ) -> Value { + let childGraph = Subgraph(graph: parentSubgraph.graph) + parentSubgraph.addChild(childGraph) + return childGraph.apply { + var childInputs = inputs + childInputs.copyCaches() + childInputs.resetSeed = Attribute(AnyResetSeed( + resetSeed: inputs.resetSeed, + info: attribute + )) + let childOutputs = storage.makeChild( + uniqueId: uniqueId, + container: attribute, + inputs: childInputs + ) + outputs.attachIndirectOutputs(childOutputs) + return Value(item: storage, subgraph: childGraph, uniqueId: uniqueId) + } + } + + func eraseItem(info: Value) { + outputs.detachIndirectOutputs() + let subgraph = info.subgraph + subgraph.willInvalidate(isInserted: true) + subgraph.invalidate() + } +} + +private struct AnyGestureChild: StatefulRule where G: Gesture { + @Attribute var info: AnyGestureInfo.Value + let uniqueId: UInt32 + + typealias Value = G + + func updateValue() { + guard uniqueId == info.uniqueId else { + return + } + info.item.updateChild(context: AnyRuleContext(context)) + } +} + +private struct AnyResetSeed: Rule { + @Attribute var resetSeed: UInt32 + @Attribute var info: AnyGestureInfo.Value + + var value: UInt32 { + let resetSeed = resetSeed + let uniqueId = info.uniqueId + return uniqueId &+ resetSeed + } +}