Skip to content

Commit 6b4e07b

Browse files
committed
Add pan and zoom gesture recognizers
1 parent afc9f71 commit 6b4e07b

File tree

1 file changed

+303
-1
lines changed

1 file changed

+303
-1
lines changed

Sources/GateEngine/System/HID/Touch/GestureRecognizers.swift

Lines changed: 303 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
case recognized
1414
}
1515

16-
nonisolated internal init() {
16+
nonisolated public init() {
1717

1818
}
1919
}
@@ -75,3 +75,305 @@ final public class RotateGestureRecognizer: GestureRecognizer {
7575
}
7676
}
7777
}
78+
79+
80+
final public class PanGestureRecognizer: GestureRecognizer {
81+
public var touchCount: Int = 2
82+
public var mouseButtons: MouseButtons = .none
83+
private var actions: [(_ delta: Position2)->()] = []
84+
var position1: Position2? = nil
85+
var position2: Position2? = nil
86+
87+
public enum MouseButtons {
88+
case none
89+
case any
90+
case exactly(_ buttons: [GateEngine.MouseButton])
91+
case anyOf(_ buttons: [GateEngine.MouseButton])
92+
}
93+
94+
public init(touchCount: Int = 2, mouseButtons: MouseButtons = .none, recognized: @escaping (_ delta: Position2)->()) {
95+
self.touchCount = touchCount
96+
self.mouseButtons = mouseButtons
97+
self.actions = [recognized]
98+
}
99+
100+
var touches: Set<Touch> = [] {
101+
didSet {
102+
if touches.count == touchCount {
103+
self.phase = .recognizing
104+
}else{
105+
self.phase = .unrecognized
106+
position1 = nil
107+
position2 = nil
108+
}
109+
}
110+
}
111+
112+
func performRecognition() {
113+
func avgTouchPosition() -> Position2 {
114+
var p: Position2 = .zero
115+
for touch in touches {
116+
p += touch.position
117+
}
118+
p /= Float(touches.count)
119+
return p
120+
}
121+
122+
if position1 == nil {
123+
position1 = avgTouchPosition()
124+
}else if position2 == nil {
125+
position2 = avgTouchPosition()
126+
127+
let delta = position1! - position2!
128+
self.phase = .recognized
129+
for action in actions {
130+
action(delta)
131+
}
132+
position1 = position2
133+
position2 = nil
134+
}
135+
}
136+
137+
public func touchesBegan(_ touches: Set<Touch>) {
138+
for touch in touches {
139+
self.touches.insert(touch)
140+
}
141+
}
142+
public func touchesMoved(_ touches: Set<Touch>) {
143+
if self.phase == .recognizing || self.phase == .recognized {
144+
performRecognition()
145+
}
146+
}
147+
public func touchesEnded(_ touches: Set<Touch>) {
148+
for touch in touches {
149+
self.touches.remove(touch)
150+
}
151+
}
152+
public func touchesCanceled(_ touches: Set<Touch>) {
153+
for touch in touches {
154+
self.touches.remove(touch)
155+
}
156+
}
157+
158+
var surfaceTouches: Set<SurfaceTouch> = [] {
159+
didSet {
160+
if surfaceTouches.count == touchCount {
161+
self.phase = .recognizing
162+
}else{
163+
self.phase = .unrecognized
164+
position1 = nil
165+
position2 = nil
166+
}
167+
}
168+
}
169+
170+
func performSurfaceRecognition() {
171+
let touches = surfaceTouches
172+
173+
func avgTouchPosition() -> Position2 {
174+
var p: Position2 = .zero
175+
for touch in touches {
176+
p += touch.position
177+
}
178+
p /= Float(touches.count)
179+
return p
180+
}
181+
182+
if position1 == nil {
183+
position1 = avgTouchPosition()
184+
}else if position2 == nil {
185+
position2 = avgTouchPosition()
186+
187+
#if os(macOS) || os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
188+
// Good for Magic Trackpad
189+
let delta = (position1! - position2!) * 750
190+
#else
191+
let delta = (position1! - position2!)
192+
#endif
193+
self.phase = .recognized
194+
for action in actions {
195+
action(delta)
196+
}
197+
position1 = position2
198+
position2 = nil
199+
}
200+
}
201+
202+
public func surfaceTouchesBegan(_ touches: Set<SurfaceTouch>, mouse: Mouse) {
203+
for touch in touches {
204+
surfaceTouches.insert(touch)
205+
}
206+
}
207+
public func surfaceTouchesMoved(_ touches: Set<SurfaceTouch>, mouse: Mouse) {
208+
if self.phase == .recognizing || self.phase == .recognized {
209+
switch mouseButtons {
210+
case .none:
211+
break
212+
case .any:
213+
if mouse.buttons.values.first(where: {$0.isPressed == true}) == nil {
214+
if phase == .recognized {
215+
self.phase = .recognizing
216+
}
217+
position1 = nil
218+
position2 = nil
219+
return
220+
}
221+
case .exactly(let buttons):
222+
for button in buttons {
223+
if mouse.buttons[button]?.isPressed == false {
224+
if phase == .recognized {
225+
self.phase = .recognizing
226+
}
227+
position1 = nil
228+
position2 = nil
229+
return
230+
}
231+
}
232+
case .anyOf(let buttons):
233+
var hit = false
234+
for button in buttons {
235+
if mouse.buttons[button]?.isPressed == true {
236+
hit = true
237+
break
238+
}
239+
}
240+
if buttons.isEmpty == false {
241+
if hit == false {
242+
if phase == .recognized {
243+
self.phase = .recognizing
244+
}
245+
position1 = nil
246+
position2 = nil
247+
return
248+
}
249+
}
250+
}
251+
performSurfaceRecognition()
252+
}
253+
}
254+
public func surfaceTouchesEnded(_ touches: Set<SurfaceTouch>, mouse: Mouse) {
255+
for touch in touches {
256+
surfaceTouches.remove(touch)
257+
}
258+
}
259+
public func surfaceTouchesCanceled(_ touches: Set<SurfaceTouch>, mouse: Mouse) {
260+
for touch in touches {
261+
surfaceTouches.remove(touch)
262+
}
263+
}
264+
}
265+
266+
final public class ZoomGestureRecognizer: GestureRecognizer {
267+
public var touchCount: Int = 2
268+
private var actions: [(_ delta: Float)->()] = []
269+
var distance1: Float? = nil
270+
var distance2: Float? = nil
271+
272+
public init(touchCount: Int = 2, recognized: @escaping (_ delta: Float)->()) {
273+
self.touchCount = touchCount
274+
self.actions = [recognized]
275+
}
276+
277+
var touches: Set<Touch> = [] {
278+
didSet {
279+
if touches.count == touchCount {
280+
self.phase = .recognizing
281+
}else{
282+
self.phase = .unrecognized
283+
distance1 = nil
284+
distance2 = nil
285+
}
286+
}
287+
}
288+
289+
func performRecognition() {
290+
let touches = Array(touches)
291+
292+
if distance1 == nil {
293+
distance1 = touches[0].position.distance(from: touches[1].position)
294+
}else if distance2 == nil {
295+
distance2 = touches[0].position.distance(from: touches[1].position)
296+
297+
let delta = -(distance1! - distance2!)
298+
self.phase = .recognized
299+
for action in actions {
300+
action(delta)
301+
}
302+
distance1 = distance2
303+
distance2 = nil
304+
}
305+
}
306+
307+
public func touchesBegan(_ touches: Set<Touch>) {
308+
for touch in touches {
309+
self.touches.insert(touch)
310+
}
311+
}
312+
public func touchesMoved(_ touches: Set<Touch>) {
313+
if self.phase == .recognizing || self.phase == .recognized {
314+
performRecognition()
315+
}
316+
}
317+
public func touchesEnded(_ touches: Set<Touch>) {
318+
for touch in touches {
319+
self.touches.remove(touch)
320+
}
321+
}
322+
public func touchesCanceled(_ touches: Set<Touch>) {
323+
for touch in touches {
324+
self.touches.remove(touch)
325+
}
326+
}
327+
328+
var surfaceTouches: Set<SurfaceTouch> = [] {
329+
didSet {
330+
if surfaceTouches.count == touchCount {
331+
self.phase = .recognizing
332+
}else{
333+
self.phase = .unrecognized
334+
distance1 = nil
335+
distance2 = nil
336+
}
337+
}
338+
}
339+
340+
func performSurfaceRecognition() {
341+
let touches = Array(surfaceTouches)
342+
343+
if distance1 == nil {
344+
distance1 = touches[0].position.distance(from: touches[1].position)
345+
}else if distance2 == nil {
346+
distance2 = touches[0].position.distance(from: touches[1].position)
347+
348+
let delta: Float = -(distance1! - distance2!) * 750
349+
self.phase = .recognized
350+
for action in actions {
351+
action(delta)
352+
}
353+
354+
distance1 = distance2
355+
distance2 = nil
356+
}
357+
}
358+
359+
public func surfaceTouchesBegan(_ touches: Set<SurfaceTouch>, mouse: Mouse) {
360+
for touch in touches {
361+
surfaceTouches.insert(touch)
362+
}
363+
}
364+
public func surfaceTouchesMoved(_ touches: Set<SurfaceTouch>, mouse: Mouse) {
365+
if self.phase == .recognizing || self.phase == .recognized {
366+
performSurfaceRecognition()
367+
}
368+
}
369+
public func surfaceTouchesEnded(_ touches: Set<SurfaceTouch>, mouse: Mouse) {
370+
for touch in touches {
371+
surfaceTouches.remove(touch)
372+
}
373+
}
374+
public func surfaceTouchesCanceled(_ touches: Set<SurfaceTouch>, mouse: Mouse) {
375+
for touch in touches {
376+
surfaceTouches.remove(touch)
377+
}
378+
}
379+
}

0 commit comments

Comments
 (0)