-
Notifications
You must be signed in to change notification settings - Fork 1
/
NiceTouchForwarder.Logic.cs
154 lines (125 loc) · 6.25 KB
/
NiceTouchForwarder.Logic.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#define ERROR_CHECK_NICE_TOUCH
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Godot;
using GodotExtensions;
using NiceTouch.GestureGeneration;
using NiceTouch.GestureReceiving;
namespace NiceTouch
{
public partial class NiceTouchForwarder
{
void BeginSingleTouch(Touch touch)
{
var args = new TouchBegin(touch);
args.Accepted += OnSingleTouchAccepted;
bool recycledAvailable = _recycledInterpreterCollections.Count > 0;
HashSet<IGestureInterpreter> collection =
recycledAvailable ? _recycledInterpreterCollections.Pop() : new HashSet<IGestureInterpreter>();
_claimedTouches.Add(touch, collection);
Input.ParseInputEvent(args); //unfortunately limited to not triggering _GuiInput /:
}
void OnSingleTouchAccepted(object sender, TouchBegin.TouchBeginEventArgs args)
{
var thisEvent = (TouchBegin)sender;
Touch touch = thisEvent.Touch;
_unclaimedTouches.Remove(touch);
IGestureInterpreter interpreter = args.Interpreter;
bool hasKey = _claimedTouches.TryGetValue(touch, out HashSet<IGestureInterpreter> claimers);
if (!hasKey)
{
#if ERROR_CHECK_NICE_TOUCH
GDLogger.Log(this, $"Touch was removed before it was accepted. Ignoring");
#endif
return;
}
bool hasInterpreterTouchCollection =
_touchesClaimedByInterpreters.TryGetValue(interpreter, out HashSet<Touch> interpreterTouchCollection);
if (!hasInterpreterTouchCollection)
{
interpreterTouchCollection = new HashSet<Touch>();
_touchesClaimedByInterpreters.Add(interpreter, interpreterTouchCollection);
}
interpreterTouchCollection.Add(touch);
claimers.Add(interpreter);
args.Interpreter.OnTouchBegin(thisEvent.Touch);
}
async void EndSingleTouch(Touch touch)
{
HashSet<IGestureInterpreter> claimers = _claimedTouches[touch];
foreach (IGestureInterpreter interpreter in claimers)
{
interpreter.OnTouchEnd(touch);
}
// give ample time for the touch to be processed as a gesture before removing
// todo: is it worth just having this (or something else) be a raw touch event rather than being handled?
if(touch.InConsiderationForMultiGesture)
await Task.Delay((int)(GestureSettings.LiftTimeMs * 1.5));
foreach (IGestureInterpreter interpreter in claimers)
{
_touchesClaimedByInterpreters[interpreter].Remove(touch);
}
claimers.Clear();
_claimedTouches.Remove(touch);
_recycledInterpreterCollections.Push(claimers);
_unclaimedTouches.Add(touch);
}
/// <summary>
/// Outputs lists of <see cref="IGestureInterpreter"/>s that can make use of the full gesture and can make use
/// of part of it
/// </summary>
void FilterTouches<T>(ref T gesture, out List<IGestureInterpreter> fullReceivers, out List<InterpreterWithGestureTouches> partialReceivers) where T : IMultiFingerGesture
{
partialReceivers = GetInterpretersContainingTouchesInGesture(ref gesture);
fullReceivers = FilterUsersWithTouches(ref partialReceivers, gesture.TouchCount);
}
/// <summary>
/// Returns a list of <see cref="InterpreterWithGestureTouches"/> - the <see cref="IGestureInterpreter"/>s
/// that own a touch contained in the provided gesture
/// </summary> // todo: `in` keyword in c# 7
List<InterpreterWithGestureTouches> GetInterpretersContainingTouchesInGesture<T>(ref T gesture) where T : IMultiFingerGesture
{
_interpreterWithGestureTouchesPool.RefreshPool();
foreach (KeyValuePair<IGestureInterpreter, HashSet<Touch>> interpreterTouchKvp in _touchesClaimedByInterpreters)
{
HashSet<Touch> touches = interpreterTouchKvp.Value;
InterpreterWithGestureTouches interpreterWithGestureTouches =
_interpreterWithGestureTouchesPool.New(interpreterTouchKvp.Key);
foreach (Touch touch in gesture.Touches)
{
if (touches.Contains(touch))
interpreterWithGestureTouches.Touches.Add(touch);
}
if (interpreterWithGestureTouches.Touches.Count == 0)
continue;
_interpreterWithGestureTouchesPool.UsersWithTouches.Add(interpreterWithGestureTouches);
}
return _interpreterWithGestureTouchesPool.UsersWithTouches;
}
/// <summary>
/// Returns a list of <see cref="IGestureInterpreter"/> that should receive the entire gesture.
/// </summary>
/// <param name="distributed">All users that contain a touch in the gesture being considered.
/// Passing by ref only to communicate the fact that this collection will be modified.</param>
/// <param name="touchCount">The number of touches in the gesture being considered</param>
List<IGestureInterpreter> FilterUsersWithTouches(ref List<InterpreterWithGestureTouches> distributed, int touchCount)
{
_fullGestureReceivers.Clear();
for(int i = 0; i < distributed.Count; i++)
{
InterpreterWithGestureTouches ut = distributed[i];
Debug.Assert(ut.Touches.Count != 0);
if (ut.Touches.Count != touchCount) continue;
// if we have all the touches, remove from distributed (incomplete) list
// and add to the full gesture receivers list
_fullGestureReceivers.Add(ut.Interpreter);
distributed.RemoveAt(i);
i--;
}
return _fullGestureReceivers;
}
readonly List<IGestureInterpreter> _fullGestureReceivers = new List<IGestureInterpreter>();
}
}