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