ab8c1a8ca264964a189bd4912f8452620d293f2f
[platform/core/uifw/dali-core.git] / dali / internal / event / events / tap-gesture / tap-gesture-recognizer.cpp
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/events/tap-gesture/tap-gesture-recognizer.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 #include <dali/public-api/math/vector2.h>
25
26 // INTERNAL INCLUDES
27 #include <dali/integration-api/events/touch-event-integ.h>
28 #include <dali/integration-api/platform-abstraction.h>
29 #include <dali/internal/event/common/scene-impl.h>
30 #include <dali/internal/event/common/thread-local-storage.h>
31 #include <dali/internal/event/events/gesture-requests.h>
32
33 namespace Dali
34 {
35 namespace Internal
36 {
37 namespace
38 {
39 // TODO: Set these according to DPI
40 const float         MAXIMUM_MOTION_ALLOWED = 20.0f;
41 const unsigned long MAXIMUM_TIME_ALLOWED   = 500u;
42 const uint32_t      WAIT_TIME              = 330u;
43 } // unnamed namespace
44
45 TapGestureRecognizer::TapGestureRecognizer(Observer& observer, Vector2 screenSize, const TapGestureRequest& request)
46 : GestureRecognizer(screenSize, GestureType::TAP),
47   mObserver(observer),
48   mState(CLEAR),
49   mMinimumTapsRequired(request.minTaps),
50   mMaximumTapsRequired(request.maxTaps),
51   mTapsRegistered(0),
52   mTouchPosition(),
53   mTouchTime(0u),
54   mLastTapTime(0u),
55   mEventTime(0u),
56   mGestureSourceType(GestureSourceType::INVALID),
57   mTimerId(0)
58 {
59 }
60
61 TapGestureRecognizer::~TapGestureRecognizer()
62 {
63   if(mTimerId != 0 && ThreadLocalStorage::Created())
64   {
65     Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
66     platformAbstraction.CancelTimer(mTimerId);
67   }
68 }
69
70 void TapGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
71 {
72   GestureRecognizerPtr ptr(this); // To keep us from being destroyed during the life-time of this method
73
74   if(event.GetPointCount() == 1)
75   {
76     const Integration::Point&               point               = event.points[0];
77     PointState::Type                        pointState          = point.GetState();
78     Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
79
80     MouseButton::Type mouseButton = point.GetMouseButton();
81     switch(mouseButton)
82     {
83       case MouseButton::INVALID:
84       {
85         mGestureSourceType = GestureSourceType::INVALID;
86         break;
87       }
88       case MouseButton::PRIMARY:
89       {
90         mGestureSourceType = GestureSourceType::PRIMARY;
91         break;
92       }
93       case MouseButton::SECONDARY:
94       {
95         mGestureSourceType = GestureSourceType::SECONDARY;
96         break;
97       }
98       case MouseButton::TERTIARY:
99       {
100         mGestureSourceType = GestureSourceType::TERTIARY;
101         break;
102       }
103       default:
104       {
105         mGestureSourceType = GestureSourceType::INVALID;
106         break;
107       }
108     }
109
110     switch(mState)
111     {
112       case CLEAR:
113       {
114         if(pointState == PointState::DOWN)
115         {
116           SetupForTouchDown(event, point);
117         }
118         break;
119       }
120
121       case TOUCHED:
122       {
123         uint32_t deltaBetweenTouchDownTouchUp = event.time - mTouchTime;
124
125         if(pointState == PointState::UP)
126         {
127           if(deltaBetweenTouchDownTouchUp < MAXIMUM_TIME_ALLOWED)
128           {
129             if(mMaximumTapsRequired > mMinimumTapsRequired)
130             {
131               mEventTime = event.time;
132               mTimerId   = platformAbstraction.StartTimer(WAIT_TIME, MakeCallback(this, &TapGestureRecognizer::TimerCallback));
133             }
134
135             mLastTapTime = mTouchTime;
136             EmitSingleTap(event.time, point);
137             mState = REGISTERED;
138           }
139           else
140           {
141             mState = CLEAR;
142           }
143         }
144         else if(pointState == PointState::INTERRUPTED)
145         {
146           mState = CLEAR;
147         }
148         break;
149       }
150
151       case REGISTERED:
152       {
153         if(pointState == PointState::UP)
154         {
155           // This is a possible multiple tap, so has it been quick enough?
156           uint32_t timeDelta                    = event.time - mLastTapTime;
157           uint32_t deltaBetweenTouchDownTouchUp = event.time - mTouchTime;
158           if(timeDelta > MAXIMUM_TIME_ALLOWED) // If exceeded time between taps then just a single tap
159           {
160             mLastTapTime = event.time;
161             EmitSingleTap(event.time, point);
162           }
163           else if(deltaBetweenTouchDownTouchUp < MAXIMUM_TIME_ALLOWED)
164           {
165             ++mTapsRegistered;
166             if(mMaximumTapsRequired > mMinimumTapsRequired)
167             {
168               mEventTime = event.time;
169               mTimerId   = platformAbstraction.StartTimer(WAIT_TIME, MakeCallback(this, &TapGestureRecognizer::TimerCallback));
170             }
171             else
172             {
173               EmitGesture(GestureState::STARTED, event.time);
174             }
175           }
176           else // Delta between touch down and touch up too long to be considered a TAP event
177           {
178             mState = CLEAR;
179           }
180         }
181         else if(pointState == PointState::DOWN)
182         {
183           const Vector2& screen(point.GetScreenPosition());
184           Vector2        distanceDelta(std::abs(mTouchPosition.x - screen.x),
185                                 std::abs(mTouchPosition.y - screen.y));
186
187           uint32_t timeDelta = event.time - mLastTapTime;
188
189           if(distanceDelta.x > MAXIMUM_MOTION_ALLOWED ||
190              distanceDelta.y > MAXIMUM_MOTION_ALLOWED ||
191              timeDelta > MAXIMUM_TIME_ALLOWED)
192           {
193             SetupForTouchDown(event, point);
194           }
195           else
196           {
197             EmitPossibleState(event);
198           }
199
200           if(mTimerId != 0)
201           {
202             platformAbstraction.CancelTimer(mTimerId);
203             mTimerId = 0;
204           }
205         }
206         break;
207       }
208
209       case FAILED:
210       default:
211       {
212         mState = CLEAR;
213         break;
214       }
215     }
216   }
217   else
218   {
219     mState = FAILED;
220
221     // We have entered a multi-touch event so emit registered gestures if required.
222     EmitGesture(GestureState::STARTED, event.time);
223   }
224 }
225
226 bool TapGestureRecognizer::TimerCallback()
227 {
228   EmitGesture(GestureState::STARTED, mEventTime);
229
230   mTimerId = 0;
231   return false;
232 }
233
234 void TapGestureRecognizer::SetupForTouchDown(const Integration::TouchEvent& event, const Integration::Point& point)
235 {
236   mTouchPosition  = point.GetScreenPosition();
237   mTouchTime      = event.time;
238   mLastTapTime    = 0u;
239   mTapsRegistered = 0;
240   mState          = TOUCHED;
241
242   EmitPossibleState(event);
243 }
244
245 void TapGestureRecognizer::EmitPossibleState(const Integration::TouchEvent& event)
246 {
247   TapGestureEvent tapEvent(GestureState::POSSIBLE);
248   tapEvent.point             = mTouchPosition;
249   tapEvent.time              = event.time;
250   tapEvent.gestureSourceType = mGestureSourceType;
251
252   ProcessEvent(tapEvent);
253 }
254
255 void TapGestureRecognizer::Update(const GestureRequest& request)
256 {
257   const TapGestureRequest& tap = static_cast<const TapGestureRequest&>(request);
258
259   mMinimumTapsRequired = tap.minTaps;
260   mMaximumTapsRequired = tap.maxTaps;
261 }
262
263 void TapGestureRecognizer::EmitGesture(GestureState state, uint32_t time)
264 {
265   if((state == GestureState::CANCELLED) ||
266      (mTapsRegistered >= mMinimumTapsRequired && mTapsRegistered <= mMaximumTapsRequired))
267
268   {
269     TapGestureEvent event(state);
270     EmitTap(time, event);
271   }
272 }
273
274 void TapGestureRecognizer::EmitSingleTap(uint32_t time, const Integration::Point& point)
275 {
276   TapGestureEvent event(GestureState::STARTED);
277   const Vector2&  screen(point.GetScreenPosition());
278   Vector2         distanceDelta(std::abs(mTouchPosition.x - screen.x),
279                         std::abs(mTouchPosition.y - screen.y));
280
281   mTapsRegistered = 1u;
282   if(distanceDelta.x > MAXIMUM_MOTION_ALLOWED ||
283      distanceDelta.y > MAXIMUM_MOTION_ALLOWED)
284   {
285     event.state = GestureState::CANCELLED;
286     if(mTimerId != 0)
287     {
288       Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
289       platformAbstraction.CancelTimer(mTimerId);
290       mTimerId = 0;
291     }
292   }
293   if(mTimerId == 0)
294   {
295     EmitTap(time, event);
296   }
297 }
298
299 void TapGestureRecognizer::EmitTap(uint32_t time, TapGestureEvent& event)
300 {
301   event.numberOfTaps      = mTapsRegistered;
302   event.point             = mTouchPosition;
303   event.time              = time;
304   event.gestureSourceType = mGestureSourceType;
305
306   ProcessEvent(event);
307 }
308
309 void TapGestureRecognizer::ProcessEvent(TapGestureEvent& event)
310 {
311   if(mScene)
312   {
313     // Create another handle so the recognizer cannot be destroyed during process function
314     GestureRecognizerPtr recognizerHandle = this;
315     mObserver.Process(*mScene, event);
316   }
317 }
318
319 } // namespace Internal
320
321 } // namespace Dali