[Tizen] Add FeedTouch api to GestureDetector.
[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
70   switch(mState)
71   {
72     // CLEAR: Wait till one point touches the screen before starting timer.
73     case CLEAR:
74     {
75       const Integration::Point& point = event.points[0];
76
77       if(point.GetState() == PointState::DOWN)
78       {
79         mTouchPositions.clear();
80         mTouchPositions[point.GetDeviceId()] = point.GetScreenPosition();
81
82         mTouchTime = event.time;
83
84         if(mTimerId != 0)
85         {
86           platformAbstraction.CancelTimer(mTimerId);
87         }
88         mTimerId = platformAbstraction.StartTimer(mMinimumHoldingTime, MakeCallback(this, &LongPressGestureRecognizer::TimerCallback));
89
90         // A long press gesture may be possible, tell Core about this and change state to TOUCHED.
91         mState = TOUCHED;
92         EmitGesture(GestureState::POSSIBLE);
93       }
94
95       break;
96     }
97
98     // TOUCHED: Monitor movement and addition/removal of points.
99     case TOUCHED:
100     {
101       if(pointCount > mMaximumTouchesRequired)
102       {
103         // A long press did not occur, tell Core that it was cancelled and change state to FAILED.
104         EmitGesture(GestureState::CANCELLED);
105         mTouchPositions.clear();
106         platformAbstraction.CancelTimer(mTimerId);
107         mTimerId = 0;
108         mState   = FAILED;
109         break;
110       }
111
112       bool endLoop(false);
113
114       for(Integration::PointContainerConstIterator iter = event.points.begin(), endIter = event.points.end();
115           iter != endIter && !endLoop;
116           ++iter)
117       {
118         switch(iter->GetState())
119         {
120           // add point.
121           case PointState::DOWN:
122           {
123             mTouchPositions[iter->GetDeviceId()] = iter->GetScreenPosition();
124             break;
125           }
126
127           // remove point.
128           case PointState::UP:
129           case PointState::INTERRUPTED:
130           {
131             // System has interrupted us, long press is not possible, inform Core
132             EmitGesture(GestureState::CANCELLED);
133             mTouchPositions.clear();
134             platformAbstraction.CancelTimer(mTimerId);
135             mTimerId = 0;
136             mState   = (pointCount == 1) ? CLEAR : FAILED; // Change state to CLEAR if only one point, FAILED otherwise.
137             endLoop  = true;
138             break;
139           }
140
141           case PointState::MOTION:
142           {
143             const Vector2 touchPosition(mTouchPositions[iter->GetDeviceId()] - iter->GetScreenPosition());
144             float         distanceSquared = touchPosition.LengthSquared();
145
146             if(distanceSquared > (MAXIMUM_MOTION_ALLOWED * MAXIMUM_MOTION_ALLOWED))
147             {
148               // We have moved more than the allowable motion for a long press gesture. Inform Core and change state to FAILED.
149               EmitGesture(GestureState::CANCELLED);
150               platformAbstraction.CancelTimer(mTimerId);
151               mTimerId = 0;
152               mState   = FAILED;
153               endLoop  = true;
154             }
155             break;
156           }
157
158           case PointState::STATIONARY:
159           case PointState::LEAVE:
160           {
161             break;
162           }
163         }
164       }
165       break;
166     }
167
168     // FAILED/FINISHED: Monitor the touches, waiting for all touches to be released.
169     case FAILED:
170     case FINISHED:
171     {
172       // eventually the final touch point will be removed, marking the end of this gesture.
173       if(pointCount == 1)
174       {
175         PointState::Type primaryPointState = event.points[0].GetState();
176
177         if((primaryPointState == PointState::UP) || (primaryPointState == PointState::INTERRUPTED))
178         {
179           if(mState == FINISHED)
180           {
181             // When the last touch point is lifted, we should inform the Core that the Long press has finished.
182             EmitGesture(GestureState::FINISHED);
183           }
184           mTouchPositions.clear();
185           mState = CLEAR; // Reset state to clear when last touch point is lifted.
186         }
187       }
188       break;
189     }
190   }
191 }
192
193 void LongPressGestureRecognizer::Update(const GestureRequest& request)
194 {
195   const LongPressGestureRequest& longPress = static_cast<const LongPressGestureRequest&>(request);
196
197   mMinimumTouchesRequired = longPress.minTouches;
198   mMaximumTouchesRequired = longPress.maxTouches;
199 }
200
201 void LongPressGestureRecognizer::SetMinimumHoldingTime(uint32_t time)
202 {
203   mMinimumHoldingTime = time;
204 }
205
206 bool LongPressGestureRecognizer::TimerCallback()
207 {
208   EmitGesture(GestureState::STARTED);
209
210   mState = FINISHED;
211
212   mTimerId = 0;
213
214   return false;
215 }
216
217 void LongPressGestureRecognizer::EmitGesture(GestureState state)
218 {
219   unsigned int touchPoints(static_cast<unsigned int>(mTouchPositions.size()));
220
221   // We should tell Core about the POSSIBLE and CANCELLED states regardless of whether we have satisfied long press requirements.
222   if((state == GestureState::POSSIBLE) ||
223      (state == GestureState::CANCELLED) ||
224      (touchPoints >= mMinimumTouchesRequired))
225   {
226     LongPressGestureEvent longPress(state);
227     longPress.numberOfTouches = touchPoints;
228
229     for(std::map<int, Vector2>::iterator iter = mTouchPositions.begin(), endIter = mTouchPositions.end();
230         iter != endIter;
231         ++iter)
232     {
233       longPress.point += iter->second;
234     }
235     longPress.point /= static_cast<float>(touchPoints);
236
237     longPress.time = mTouchTime;
238     if(state != GestureState::POSSIBLE)
239     {
240       longPress.time += mMinimumHoldingTime;
241     }
242     longPress.sourceType = mSourceType;
243     longPress.sourceData = mSourceData;
244     longPress.renderTask = mRenderTask;
245
246     if(mScene)
247     {
248       // Create another handle so the recognizer cannot be destroyed during process function
249       GestureRecognizerPtr recognizerHandle = this;
250
251       mObserver.Process(*mScene, longPress, mActor.GetActor());
252     }
253   }
254 }
255
256 } // namespace Internal
257
258 } // namespace Dali