Touch and Hover event propagrated by geometry way.(5)
[platform/core/uifw/dali-core.git] / dali / internal / event / events / long-press-gesture / long-press-gesture-recognizer.cpp
1 /*
2  * Copyright (c) 2022 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/long-press-gesture/long-press-gesture-recognizer.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 #include <dali/devel-api/events/touch-point.h>
25 #include <dali/public-api/math/vector2.h>
26
27 #include <dali/integration-api/events/touch-event-integ.h>
28 #include <dali/integration-api/platform-abstraction.h>
29 #include <dali/internal/event/common/thread-local-storage.h>
30 #include <dali/internal/event/events/gesture-requests.h>
31
32 namespace Dali
33 {
34 namespace Internal
35 {
36 namespace
37 {
38 // TODO: Set these according to DPI
39 const float MAXIMUM_MOTION_ALLOWED = 60.0f;
40
41 } // unnamed namespace
42
43 LongPressGestureRecognizer::LongPressGestureRecognizer(Observer& observer, Vector2 screenSize, const LongPressGestureRequest& request, uint32_t minimumHoldingTime)
44 : GestureRecognizer(screenSize, GestureType::LONG_PRESS),
45   mObserver(observer),
46   mState(CLEAR),
47   mMinimumTouchesRequired(request.minTouches),
48   mMaximumTouchesRequired(request.maxTouches),
49   mTouchTime(0),
50   mTimerId(0),
51   mMinimumHoldingTime(minimumHoldingTime)
52 {
53 }
54
55 LongPressGestureRecognizer::~LongPressGestureRecognizer()
56 {
57   if(mTimerId != 0 && ThreadLocalStorage::Created())
58   {
59     Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
60     platformAbstraction.CancelTimer(mTimerId);
61   }
62 }
63
64 void LongPressGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
65 {
66   unsigned int                            pointCount(event.GetPointCount());
67   Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
68   GestureRecognizerPtr                    ptr(this); // To keep us from being destroyed during the life-time of this method
69   switch(mState)
70   {
71     // CLEAR: Wait till one point touches the screen before starting timer.
72     case CLEAR:
73     {
74       const Integration::Point& point = event.points[0];
75
76       if(point.GetState() == PointState::DOWN)
77       {
78         mTouchPositions.clear();
79         mTouchPositions[point.GetDeviceId()] = point.GetScreenPosition();
80
81         mTouchTime = event.time;
82
83         if(mTimerId != 0)
84         {
85           platformAbstraction.CancelTimer(mTimerId);
86         }
87         mTimerId = platformAbstraction.StartTimer(mMinimumHoldingTime, MakeCallback(this, &LongPressGestureRecognizer::TimerCallback));
88
89         // A long press gesture may be possible, tell Core about this and change state to TOUCHED.
90         mState = TOUCHED;
91         EmitGesture(GestureState::POSSIBLE);
92       }
93
94       break;
95     }
96
97     // TOUCHED: Monitor movement and addition/removal of points.
98     case TOUCHED:
99     {
100       if(pointCount > mMaximumTouchesRequired)
101       {
102         // A long press did not occur, tell Core that it was cancelled and change state to FAILED.
103         EmitGesture(GestureState::CANCELLED);
104         mTouchPositions.clear();
105         platformAbstraction.CancelTimer(mTimerId);
106         mTimerId = 0;
107         mState   = FAILED;
108         break;
109       }
110
111       bool endLoop(false);
112
113       for(Integration::PointContainerConstIterator iter = event.points.begin(), endIter = event.points.end();
114           iter != endIter && !endLoop;
115           ++iter)
116       {
117         switch(iter->GetState())
118         {
119           // add point.
120           case PointState::DOWN:
121           {
122             mTouchPositions[iter->GetDeviceId()] = iter->GetScreenPosition();
123             break;
124           }
125
126           // remove point.
127           case PointState::UP:
128           case PointState::INTERRUPTED:
129           {
130             // System has interrupted us, long press is not possible, inform Core
131             EmitGesture(GestureState::CANCELLED);
132             mTouchPositions.clear();
133             platformAbstraction.CancelTimer(mTimerId);
134             mTimerId = 0;
135             mState   = (pointCount == 1) ? CLEAR : FAILED; // Change state to CLEAR if only one point, FAILED otherwise.
136             endLoop  = true;
137             break;
138           }
139
140           case PointState::MOTION:
141           {
142             const Vector2 touchPosition(mTouchPositions[iter->GetDeviceId()] - iter->GetScreenPosition());
143             float         distanceSquared = touchPosition.LengthSquared();
144
145             if(distanceSquared > (MAXIMUM_MOTION_ALLOWED * MAXIMUM_MOTION_ALLOWED))
146             {
147               // We have moved more than the allowable motion for a long press gesture. Inform Core and change state to FAILED.
148               EmitGesture(GestureState::CANCELLED);
149               platformAbstraction.CancelTimer(mTimerId);
150               mTimerId = 0;
151               mState   = FAILED;
152               endLoop  = true;
153             }
154             break;
155           }
156
157           case PointState::STATIONARY:
158           case PointState::LEAVE:
159           {
160             break;
161           }
162         }
163       }
164       break;
165     }
166
167     // FAILED/FINISHED: Monitor the touches, waiting for all touches to be released.
168     case FAILED:
169     case FINISHED:
170     {
171       // eventually the final touch point will be removed, marking the end of this gesture.
172       if(pointCount == 1)
173       {
174         PointState::Type primaryPointState = event.points[0].GetState();
175
176         if((primaryPointState == PointState::UP) || (primaryPointState == PointState::INTERRUPTED))
177         {
178           if(mState == FINISHED)
179           {
180             // When the last touch point is lifted, we should inform the Core that the Long press has finished.
181             EmitGesture(GestureState::FINISHED);
182           }
183           mTouchPositions.clear();
184           mState = CLEAR; // Reset state to clear when last touch point is lifted.
185         }
186       }
187       break;
188     }
189   }
190 }
191
192 void LongPressGestureRecognizer::Update(const GestureRequest& request)
193 {
194   const LongPressGestureRequest& longPress = static_cast<const LongPressGestureRequest&>(request);
195
196   mMinimumTouchesRequired = longPress.minTouches;
197   mMaximumTouchesRequired = longPress.maxTouches;
198 }
199
200 void LongPressGestureRecognizer::SetMinimumHoldingTime(uint32_t time)
201 {
202   mMinimumHoldingTime = time;
203 }
204
205 bool LongPressGestureRecognizer::TimerCallback()
206 {
207   EmitGesture(GestureState::STARTED);
208
209   mState = FINISHED;
210
211   mTimerId = 0;
212
213   return false;
214 }
215
216 void LongPressGestureRecognizer::EmitGesture(GestureState state)
217 {
218   unsigned int touchPoints(static_cast<unsigned int>(mTouchPositions.size()));
219
220   // We should tell Core about the POSSIBLE and CANCELLED states regardless of whether we have satisfied long press requirements.
221   if((state == GestureState::POSSIBLE) ||
222      (state == GestureState::CANCELLED) ||
223      (touchPoints >= mMinimumTouchesRequired))
224   {
225     LongPressGestureEvent longPress(state);
226     longPress.numberOfTouches = touchPoints;
227
228     for(std::map<int, Vector2>::iterator iter = mTouchPositions.begin(), endIter = mTouchPositions.end();
229         iter != endIter;
230         ++iter)
231     {
232       longPress.point += iter->second;
233     }
234     longPress.point /= static_cast<float>(touchPoints);
235
236     longPress.time = mTouchTime;
237     if(state != GestureState::POSSIBLE)
238     {
239       longPress.time += mMinimumHoldingTime;
240     }
241     longPress.sourceType = mSourceType;
242     longPress.sourceData = mSourceData;
243
244     if(mScene)
245     {
246       // Create another handle so the recognizer cannot be destroyed during process function
247       GestureRecognizerPtr recognizerHandle = this;
248
249       mObserver.Process(*mScene, longPress);
250     }
251   }
252 }
253
254 } // namespace Internal
255
256 } // namespace Dali