Skip to content

Commit a1777ac

Browse files
authored
Add exclusive gesture implementation (#356)
1 parent 4a8fc9f commit a1777ac

File tree

2 files changed

+193
-2
lines changed

2 files changed

+193
-2
lines changed

Sources/OpenSwiftUICore/Event/Event_TODO.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// FIXME
22

3-
package struct ExclusiveGesture<A, B>: PrimitiveGesture {}
4-
53
package struct _MapGesture<Content, Value>: PrimitiveGesture where Content: Gesture {
64
nonisolated package static func _makeGesture(gesture: _GraphValue<_MapGesture<Content, Value>>, inputs: _GestureInputs) -> _GestureOutputs<Never> {
75
preconditionFailure("TODO")
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
//
2+
// ExclusiveGesture.swift
3+
// OpenSwiftUICore
4+
//
5+
// Status: Complete
6+
// ID: C6A5F4DE707A20D3CFD8B7768E28573B (SwiftUICore)
7+
8+
import OpenGraphShims
9+
10+
@available(OpenSwiftUI_v1_0, *)
11+
extension Gesture {
12+
/// Combines two gestures exclusively to create a new gesture where only one
13+
/// gesture succeeds, giving precedence to the first gesture.
14+
///
15+
/// - Parameter other: A gesture you combine with your gesture, to create a
16+
/// new, combined gesture.
17+
///
18+
/// - Returns: A gesture that's the result of combining two gestures where
19+
/// only one of them can succeed. OpenSwiftUI gives precedence to the first
20+
/// gesture.
21+
@inlinable
22+
public func exclusively<Other>(before other: Other) -> ExclusiveGesture<Self, Other> where Other: Gesture {
23+
return ExclusiveGesture(self, other)
24+
}
25+
}
26+
27+
/// A gesture that consists of two gestures where only one of them can succeed.
28+
///
29+
/// The `ExclusiveGesture` gives precedence to its first gesture.
30+
@available(OpenSwiftUI_v1_0, *)
31+
@frozen
32+
public struct ExclusiveGesture<First, Second>: PrimitiveGesture, Gesture where First: Gesture, Second: Gesture {
33+
/// The value of an exclusive gesture that indicates which of two gestures
34+
/// succeeded.
35+
@frozen
36+
public enum Value {
37+
/// The first of two gestures succeeded.
38+
case first(First.Value)
39+
40+
/// The second of two gestures succeeded.
41+
case second(Second.Value)
42+
}
43+
44+
/// The first of two gestures.
45+
public var first: First
46+
47+
/// The second of two gestures.
48+
public var second: Second
49+
50+
/// Creates a gesture from two gestures where only one of them succeeds.
51+
///
52+
/// - Parameters:
53+
/// - first: The first of two gestures. This gesture has precedence over
54+
/// the other gesture.
55+
/// - second: The second of two gestures.
56+
@inlinable
57+
public init(_ first: First, _ second: Second) {
58+
(self.first, self.second) = (first, second)
59+
}
60+
61+
nonisolated public static func _makeGesture(
62+
gesture: _GraphValue<Self>,
63+
inputs: _GestureInputs
64+
) -> _GestureOutputs<Value> {
65+
var inputs = inputs
66+
let outputs1 = First.makeDebuggableGesture(
67+
gesture: gesture[offset: { .of(&$0.first) }],
68+
inputs: inputs
69+
)
70+
inputs.inheritedPhase = Attribute(ExclusiveState(state: inputs.inheritedPhase, phase: outputs1.phase))
71+
let outputs2 = Second.makeDebuggableGesture(
72+
gesture: gesture[offset: { .of(&$0.second) }],
73+
inputs: inputs
74+
)
75+
let phase = Attribute(ExclusivePhase<First, Second>(
76+
phase1: outputs1.phase,
77+
phase2: outputs2.phase
78+
))
79+
var outputs = _GestureOutputs(phase: phase)
80+
outputs.wrapDebugOutputs(
81+
Self.self,
82+
kind: .combiner,
83+
inputs: inputs,
84+
combiningOutputs: (outputs1, outputs2)
85+
)
86+
for key in inputs.preferences.keys {
87+
func project<K>(_ key: K.Type) where K: PreferenceKey {
88+
outputs[anyKey: key] = Attribute(ExclusivePreference<First, Second, K>(
89+
value1: .init(base: AnyOptionalAttribute(outputs1[anyKey: key])),
90+
value2: .init(base: AnyOptionalAttribute(outputs2[anyKey: key])),
91+
phase1: outputs1.phase,
92+
phase2: outputs2.phase
93+
)).identifier
94+
}
95+
project(key)
96+
}
97+
return outputs
98+
99+
}
100+
}
101+
102+
@available(OpenSwiftUI_v1_0, *)
103+
extension ExclusiveGesture.Value: Sendable where First.Value: Sendable, Second.Value: Sendable {}
104+
105+
@available(*, unavailable)
106+
extension ExclusiveGesture: Sendable {}
107+
108+
@available(OpenSwiftUI_v1_0, *)
109+
extension ExclusiveGesture: PrimitiveDebuggableGesture {}
110+
111+
@available(OpenSwiftUI_v1_0, *)
112+
extension ExclusiveGesture.Value: Equatable where First.Value: Equatable, Second.Value: Equatable {}
113+
114+
extension ExclusiveGesture.Value: Hashable where First.Value: Hashable, Second.Value: Hashable {}
115+
116+
private struct ExclusivePreference<First, Second, Key>: Rule where First: Gesture, Second: Gesture, Key: PreferenceKey {
117+
@OptionalAttribute var value1: Key.Value?
118+
@OptionalAttribute var value2: Key.Value?
119+
@Attribute var phase1: GesturePhase<First.Value>
120+
@Attribute var phase2: GesturePhase<Second.Value>
121+
122+
typealias Value = Key.Value
123+
124+
var value: Value {
125+
switch (phase1, phase2) {
126+
case (.active, _), (.ended, _), (.possible(.some), _), (.possible(.none), .failed): value1 ?? Key.defaultValue
127+
case (_, .active), (_, .ended), (_, .possible(.some)), (.failed, .possible(.none)): value2 ?? Key.defaultValue
128+
case (.possible(.none), .possible(.none)): mergedValue()
129+
case (.failed, .failed): Key.defaultValue
130+
}
131+
}
132+
133+
@inline(__always)
134+
func mergedValue() -> Key.Value {
135+
if let value1, let value2 {
136+
var result = value1
137+
Key.reduce(value: &result) { value2 }
138+
return result
139+
} else {
140+
return value1 ?? value2 ?? Key.defaultValue
141+
}
142+
}
143+
}
144+
145+
private struct ExclusivePhase<First, Second>: Rule where First: Gesture, Second: Gesture {
146+
@Attribute var phase1: GesturePhase<First.Value>
147+
@Attribute var phase2: GesturePhase<Second.Value>
148+
149+
typealias Value = GesturePhase<ExclusiveGesture<First, Second>.Value>
150+
151+
var value: Value {
152+
switch (phase1, phase2) {
153+
case let (.active(value1), _):
154+
.active(.first(value1))
155+
case let (.possible, .active(value2)):
156+
.active(.second(value2))
157+
case let (.possible(.some(value1)), _):
158+
.possible(.first(value1))
159+
case let (.possible, .possible(.some(value2))):
160+
.possible(.second(value2))
161+
case (.possible, _):
162+
.possible(nil)
163+
case let (.ended(value1), _):
164+
.ended(.first(value1))
165+
case let (.failed, .active(value2)):
166+
.active(.second(value2))
167+
case let (.failed, .possible(.some(value2))):
168+
.possible(.second(value2))
169+
case let (.failed, .ended(value2)):
170+
.ended(.second(value2))
171+
case (.failed, _):
172+
.failed
173+
}
174+
}
175+
}
176+
177+
private struct ExclusiveState<V>: Rule {
178+
@Attribute var state: _GestureInputs.InheritedPhase
179+
@Attribute var phase: GesturePhase<V>
180+
181+
typealias Value = _GestureInputs.InheritedPhase
182+
183+
var value: Value {
184+
var result = state
185+
if !phase.isFailed {
186+
result.subtract(.failed)
187+
}
188+
if phase.isActive {
189+
result.insert(.active)
190+
}
191+
return result
192+
}
193+
}

0 commit comments

Comments
 (0)