-
Notifications
You must be signed in to change notification settings - Fork 1
/
NiceTouch.cs
179 lines (150 loc) · 6.02 KB
/
NiceTouch.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#define ERROR_CHECK_NICE_TOUCH
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Godot;
using GodotExtensions;
using NiceTouch.GestureGeneration;
namespace NiceTouch
{
/// <summary>
/// A class that manages an infinite number of touches
/// </summary>
public partial class NiceTouch : Control
{
public event EventHandler<Touch> TouchAdded;
public event EventHandler<Touch> TouchRemoved;
public event EventHandler AfterInput;
readonly Dictionary<int, Touch> _touches = new Dictionary<int, Touch>();
readonly bool _allowMouse = true;
readonly bool _acceptTouch = true;
readonly bool _acceptMouse = true;
static double Time => Godot.Time.GetTicksUsec() / (double)1000000;
static int _incrementingTouchIndex = 0;
readonly Dictionary<int, int> _touchIndices = new Dictionary<int, int>();
readonly HashSet<int> _mouseButtonsPressed = new HashSet<int>();
// todo - ensure correctness with godot upgrade
private const int _mouseButtonMask = (int)MouseButtonMask.Left| (int) MouseButtonMask.Right | (int)MouseButtonMask.Middle |
(int)MouseButtonMask.MbXbutton1 | (int)MouseButtonMask.MbXbutton2;
bool _lastRemovedWasTouch; // hacky bool needed to reign in a godot bug (or feature?)
public override void _Ready()
{
_ = new GestureGenerator(this, new NiceTouchForwarder());
}
public override void _Input(InputEvent input)
{
switch (input)
{
case InputEventMouseButton mouse:
HandleMouseButtonEvent(mouse);
break;
case InputEventMouseMotion mouse:
HandleMouseMotionEvent(mouse);
break;
case InputEventScreenTouch touch:
HandleTouchEvent(touch);
break;
case InputEventScreenDrag drag:
HandleTouchDragEvent(drag);
break;
}
AfterInput?.Invoke(this, EventArgs.Empty);
}
public override void _Process(double delta)
{
double time = Time;
foreach (KeyValuePair<int, Touch> touchEntry in _touches)
{
Touch touch = touchEntry.Value;
if (time - touch.LastUpdateTime > delta)
{
touch.Update(time, touch.Position);
}
}
}
void HandleTouchDragEvent(InputEventScreenDrag drag)
{
if (_acceptTouch)
AcceptEvent();
DragTouch(drag.Index, Time, drag.Position);
}
void HandleTouchEvent(InputEventScreenTouch touch)
{
if (_acceptTouch)
AcceptEvent();
if (touch.Pressed)
{
AddTouch(touch.Index, Time, touch.Position);
return;
}
RemoveTouch(touch.Index, Time, touch.Position);
_lastRemovedWasTouch = true;
}
void HandleMouseMotionEvent(InputEventMouseMotion mouse)
{
if (!_allowMouse) return;
if(_acceptMouse) AcceptEvent();
double time = Time;
foreach (int mouseButton in _mouseButtonsPressed)
{
int index = mouseButton;
DragTouch(index, time, mouse.Position);
}
}
void HandleMouseButtonEvent(InputEventMouseButton mouse)
{
int buttonIndex = MouseToTouchIndex(mouse.ButtonIndex);
if ((_mouseButtonMask & buttonIndex) == 0)
return;
if (!_allowMouse) return;
if(_acceptMouse) AcceptEvent();
// workaround for godot sending a redundant double-click event on double-taps
if (mouse.DoubleClick && _lastRemovedWasTouch)
return;
if (mouse.Pressed)
{
_mouseButtonsPressed.Add(buttonIndex);
AddTouch(buttonIndex, Time, mouse.Position);
}
else
{
_mouseButtonsPressed.Remove(buttonIndex);
RemoveTouch(buttonIndex, Time, mouse.Position);
_lastRemovedWasTouch = false;
}
}
// todo - ensure correctness with godot upgrade
static int MouseToTouchIndex(MouseButton mouseButton) => (int)mouseButton;
void AddTouch(int index, double time, Vector2 position)
{
int touchIndex = _incrementingTouchIndex++;
_touchIndices[index] = touchIndex;
Touch touch = new Touch(time, touchIndex, position);
_touches[touchIndex] = touch;
TouchAdded.Invoke(this, touch);
}
async void RemoveTouch(int index, double time, Vector2 position)
{
int touchIndex = _touchIndices[index];
_touchIndices.Remove(index);
Touch removedTouch = _touches[touchIndex];
// if a touch is removed before any receiving nodes receive it through the main input loop, they won't
// get the touch released event. this ensures that there's at least one frame granted.
// this may cause problems in the efficient rendering mode.... ¯\_(ツ)_/¯
while (Engine.GetProcessFrames() == removedTouch.FrameCreated)
{
await Task.Delay(10);
// for some reason, godot on android freezes with Yield
//await Task.Yield();
}
removedTouch.Update(time, position);
_touches.Remove(touchIndex);
TouchRemoved.Invoke(this, removedTouch);
}
void DragTouch(int index, double time, Vector2 position)
{
int touchIndex = _touchIndices[index];
_touches[touchIndex].Update(time, position);
}
}
}