Skip to content

Commit eda9a66

Browse files
committed
Gestures: add GestureRecognizer and GestureRecognizerDelegate
This adds the two base types for the hierarchy to support implementing gesture recognizers. This is meant to provide the proper interaction detection for Label components. Currently, Label incorrectly derives from Control, when it should derive from View. This works towards a solution for #442.
1 parent 8c52287 commit eda9a66

File tree

2 files changed

+297
-0
lines changed

2 files changed

+297
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/**
2+
* Copyright © 2021 Saleem Abdulrasool <[email protected]>
3+
* All rights reserved.
4+
*
5+
* SPDX-License-Identifier: BSD-3-Clause
6+
**/
7+
8+
// Class constrain the callable to ensure that the value is heap allocated,
9+
// permitting us to perform pointer equality for the callback to serve as
10+
// identity. Using COW, we can perform pointer equality on the types as a
11+
// rough equivalency for the pointer equality for the bound action, which
12+
// determines if the action is equivalent or not.
13+
private protocol GestureRecognizerCallable: AnyObject {
14+
func callAsFunction(_: GestureRecognizer)
15+
}
16+
17+
private class GestureRecognizerCallback<Target: AnyObject>: GestureRecognizerCallable {
18+
private enum Signature {
19+
case zero((Target) -> () -> Void)
20+
case one((Target) -> (_: GestureRecognizer) -> Void)
21+
}
22+
23+
private unowned(unsafe) let instance: Target
24+
private let method: Signature
25+
26+
public init(binding: @escaping (Target) -> (GestureRecognizer) -> Void, on: Target) {
27+
self.instance = on
28+
self.method = .one(binding)
29+
}
30+
31+
public init(binding: @escaping (Target) -> () -> Void, on: Target) {
32+
self.instance = on
33+
self.method = .zero(binding)
34+
}
35+
36+
public func callAsFunction(_ gestureRecognizer: GestureRecognizer) {
37+
switch self.method {
38+
case let .zero(action):
39+
action(self.instance)()
40+
case let .one(action):
41+
action(self.instance)(gestureRecognizer)
42+
}
43+
}
44+
}
45+
46+
public class GestureRecognizer {
47+
private var actions: [GestureRecognizerCallable]
48+
49+
// MARK - Initializing a Gesture Recognizer
50+
51+
/// Initializes an allocated gesture-recognizer object with a target and an
52+
/// action selector.
53+
///
54+
/// The valid signatures for `action` are:
55+
/// - `(Target) -> () -> Void` aka `()`
56+
/// - `(Target) -> (_: GestureRecognizer) -> Void` aka `(_: GestureRecognizer)`
57+
///
58+
/// Although the signature permits nullable types, the values may not be nil.
59+
60+
// public init(target: Any?, action: Selector?)
61+
62+
public init<Target: AnyObject>(target: Target,
63+
action: @escaping (Target) -> () -> Void) {
64+
self.actions = [GestureRecognizerCallback(binding: action, on: target)]
65+
}
66+
67+
public init<Target: AnyObject>(target: Target,
68+
action: @escaping (Target) -> (_: GestureRecognizer) -> Void) {
69+
self.actions = [GestureRecognizerCallback(binding: action, on: target)]
70+
}
71+
72+
// MARK - Managing Gesture-Related Interactions
73+
74+
/// The delegate of the gesture recognizer.
75+
public weak var delegate: GestureRecognizerDelegate?
76+
77+
// MARK - Adding and Removing Targets and Actions
78+
79+
/// Adds a target and an action to a gesture-recognizer object.
80+
///
81+
/// The valid signatures for `action` are:
82+
/// - `(Target) -> () -> Void` aka `()`
83+
/// - `(Target) -> (_: GestureRecognizer) -> Void` aka `(_: GestureRecognizer)`
84+
85+
// public func addTarget(_ target: Any, action: Selector)
86+
87+
public func addTarget<Target: AnyObject>(_ target: Target,
88+
action: @escaping (Target) -> () -> Void) {
89+
self.actions.append(GestureRecognizerCallback(binding: action, on: target))
90+
}
91+
92+
public func addTarget<Target: AnyObject>(_ target: Target,
93+
action: @escaping (Target) -> (_: GestureRecognizer) -> Void) {
94+
self.actions.append(GestureRecognizerCallback(binding: action, on: target))
95+
}
96+
97+
/// Removes a target and an action from a gesture-recognizer object.
98+
99+
// public func removeTarget(_ target: Any?, action: Selector?)
100+
101+
public func removeTarget<Target: AnyObject>(_ target: Target,
102+
action: @escaping (Target) -> () -> Void) {
103+
let callable: GestureRecognizerCallable =
104+
GestureRecognizerCallback(binding: action, on: target)
105+
self.actions.removeAll(where: { $0 === callable })
106+
}
107+
108+
public func removeTarget<Target: AnyObject>(_ target: Target,
109+
action: @escaping (Target) -> (_: GestureRecognizer) -> Void) {
110+
let callable: GestureRecognizerCallable =
111+
GestureRecognizerCallback(binding: action, on: target)
112+
self.actions.removeAll(where: { $0 === callable })
113+
}
114+
115+
// MARK - Getting the Touches and Location of a Gesture
116+
117+
/// Returns the point computed as the location in a given view of the gesture
118+
/// represented by the receiver.
119+
public func location(in view: View?) -> Point {
120+
fatalError("\(#function) not yet implemented")
121+
}
122+
123+
/// Returns the location of one of the gesture’s touches in the local
124+
/// coordinate system of a given view.
125+
public func location(ofTouch touchIndex: Int, in view: View?) -> Point {
126+
fatalError("\(#function) not yet implemented")
127+
}
128+
129+
/// Returns the number of touches involved in the gesture represented by the
130+
/// receiver.
131+
public private(set) var numberOfTouches: Int = 0
132+
133+
// MARK - Debugging Gesture Recognizers
134+
135+
/// The name associated with the gesture recognizer.
136+
public var name: String?
137+
138+
// MARK - Constants
139+
140+
/// The current state a gesture recognizer is in.
141+
public enum State: Int {
142+
/// The gesture recognizer has not yet recognized its gesture, but may be
143+
/// evaluating touch events. This is the default state.
144+
case possible
145+
146+
/// The gesture recognizer has received touch objects recognized as a
147+
/// continuous gesture. It sends its action message (or messages) at the next
148+
/// cycle of the run loop.
149+
case began
150+
151+
/// The gesture recognizer has received touches recognized as a change to a
152+
/// continuous gesture. It sends its action message (or messages) at the next
153+
/// cycle of the run loop.
154+
case changed
155+
156+
/// The gesture recognizer has received touches recognized as the end of a
157+
/// continuous gesture. It sends its action message (or messages) at the next
158+
/// cycle of the run loop and resets its state to
159+
/// `GestureRecognizer.State.possible`.
160+
case ended
161+
162+
/// The gesture recognizer has received touches resulting in the cancellation
163+
/// of a continuous gesture. It sends its action message (or messages) at the
164+
/// next cycle of the run loop and resets its state to
165+
/// `GestureRecognizer.State.possible`.
166+
case cancelled
167+
168+
/// The gesture recognizer has received a multi-touch sequence that it cannot
169+
/// recognize as its gesture. No action message is sent and the gesture
170+
/// recognizer is reset to `GestureRecognizer.State.possible`.
171+
case failed
172+
}
173+
174+
// MARK - Initializers
175+
176+
public /* convenience */ init() {
177+
fatalError("\(#function) not yet implemented")
178+
}
179+
180+
// MARK - Instance Methods
181+
182+
public func shouldReceive(_ event: Event) -> Bool {
183+
fatalError("\(#function) not yet implemented")
184+
}
185+
}
186+
187+
extension GestureRecognizer.State {
188+
/// The gesture recognizer has received a multi-touch sequence that it
189+
/// recognizes as its gesture. It sends its action message (or messages) at
190+
/// the next cycle of the run loop and resets its state to
191+
/// `GestureRecognizer.State.possible`.
192+
public static var recognized: GestureRecognizer.State {
193+
.ended
194+
}
195+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/**
2+
* Copyright © 2021 Saleem Abdulrasool <[email protected]>
3+
* All rights reserved.
4+
*
5+
* SPDX-License-Identifier: BSD-3-Clause
6+
**/
7+
8+
/// A set of methods implemented by the delegate of a gesture recognizer to
9+
/// fine-tune an application’s gesture-recognition behavior.
10+
public protocol GestureRecognizerDelegate: AnyObject {
11+
// MARK - Regulating Gesture Recognition
12+
13+
/// Asks the delegate if a gesture recognizer should begin interpreting
14+
/// touches.
15+
func gestureRecognizerShouldBegin(_ gestureRecognizer: GestureRecognizer)
16+
-> Bool
17+
18+
/// Asks the delegate if a gesture recognizer should receive an object
19+
/// representing a touch.
20+
func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
21+
shouldReceive touch: Touch) -> Bool
22+
23+
/// Asks the delegate if a gesture recognizer should receive an object
24+
/// representing a press.
25+
#if NOT_YET_IMPLEMENTED
26+
func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
27+
shouldReceive press: Press) -> Bool
28+
#endif
29+
30+
/// Asks the delegate if a gesture recognizer should receive an object
31+
/// representing a touch or press event.
32+
func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
33+
shouldReceive event: Event) -> Bool
34+
35+
// MARK - Controlling Simultaneous Gesture Recognition
36+
37+
/// Asks the delegate if two gesture recognizers should be allowed to
38+
/// recognize gestures simultaneously.
39+
func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
40+
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: GestureRecognizer)
41+
-> Bool
42+
43+
// MARK - Setting Up Failure Requirements
44+
45+
/// Asks the delegate if a gesture recognizer should require another gesture
46+
/// recognizer to fail.
47+
func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
48+
shouldRequireFailureOf otherGestureRecognizer: GestureRecognizer)
49+
-> Bool
50+
51+
/// Asks the delegate if a gesture recognizer should be required to fail by
52+
/// another gesture recognizer.
53+
func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
54+
shouldBeRequiredToFailBy otherGestureRecognizer: GestureRecognizer)
55+
-> Bool
56+
}
57+
58+
extension GestureRecognizerDelegate {
59+
public func gestureRecognizerShouldBegin(_ gestureRecognizer: GestureRecognizer)
60+
-> Bool {
61+
return true
62+
}
63+
64+
public func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
65+
shouldReceive touch: Touch) -> Bool {
66+
return true
67+
}
68+
69+
#if NOT_YET_IMPLEMENTED
70+
public func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
71+
shouldReceive press: Press) -> Bool {
72+
return true
73+
}
74+
#endif
75+
76+
public func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
77+
shouldReceive event: Event) -> Bool {
78+
return true
79+
}
80+
}
81+
82+
extension GestureRecognizerDelegate {
83+
public func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
84+
shouldRecognizeSimultaneouslyWith otherGestureRecognizer: GestureRecognizer)
85+
-> Bool {
86+
return false
87+
}
88+
}
89+
90+
extension GestureRecognizerDelegate {
91+
public func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
92+
shouldRequireFailureOf otherGestureRecognizer: GestureRecognizer)
93+
-> Bool {
94+
return false
95+
}
96+
97+
public func gestureRecognizer(_ gestureRecognizer: GestureRecognizer,
98+
shouldBeRequiredToFailBy otherGestureRecognizer: GestureRecognizer)
99+
-> Bool {
100+
return false
101+
}
102+
}

0 commit comments

Comments
 (0)