Merge "use modern construct '= default' for special functions." into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / event / events / pan-gesture / pan-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/pan-gesture/pan-gesture-recognizer.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 #include <dali/devel-api/events/touch-point.h>
25
26 #include <dali/integration-api/events/touch-event-integ.h>
27
28 // INTERNAL INCLUDES
29 #include <dali/internal/event/common/scene-impl.h>
30 #include <dali/internal/event/events/pan-gesture/pan-gesture-event.h>
31 #include <dali/internal/event/events/gesture-requests.h>
32 namespace Dali
33 {
34
35 namespace Internal
36 {
37
38 namespace
39 {
40 const float MINIMUM_MOTION_DISTANCE_BEFORE_PAN( 15.0f );
41 const float MINIMUM_MOTION_DISTANCE_BEFORE_PAN_SQUARED( MINIMUM_MOTION_DISTANCE_BEFORE_PAN * MINIMUM_MOTION_DISTANCE_BEFORE_PAN );
42 const float MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO( 2.0f / 3.0f );
43 const unsigned long MAXIMUM_TIME_DIFF_ALLOWED( 500 );
44 const unsigned long MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS( 100 );
45 const unsigned int MINIMUM_MOTION_EVENTS_BEFORE_PAN(2);
46 } // unnamed namespace
47
48 PanGestureRecognizer::PanGestureRecognizer( Observer& observer, Vector2 screenSize, const PanGestureRequest& request, int32_t minimumDistance, int32_t minimumPanEvents )
49 : GestureRecognizer( screenSize, GestureType::PAN ),
50   mObserver( observer ),
51   mState( CLEAR ),
52   mThresholdAdjustmentsRemaining( 0 ),
53   mThresholdTotalAdjustments( static_cast<unsigned int>( MINIMUM_MOTION_DISTANCE_BEFORE_PAN * MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO ) ),
54   mPrimaryTouchDownTime( 0 ),
55   mMinimumTouchesRequired( request.minTouches ),
56   mMaximumTouchesRequired( request.maxTouches ),
57   mMinimumDistanceSquared( static_cast<unsigned int>( MINIMUM_MOTION_DISTANCE_BEFORE_PAN_SQUARED ) ),
58   mMinimumMotionEvents( MINIMUM_MOTION_EVENTS_BEFORE_PAN ),
59   mMotionEvents( 0 )
60 {
61   if ( minimumDistance >= 0 )
62   {
63     mMinimumDistanceSquared = minimumDistance * minimumDistance;
64
65     // Usually, we do not want to apply the threshold straight away, but phased over the first few pans
66     // Set our distance to threshold adjustments ratio here.
67     float fMinimumDistance = static_cast<float>( minimumDistance );
68     mThresholdTotalAdjustments = static_cast<unsigned int>( fMinimumDistance * MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO );
69   }
70
71   if ( minimumPanEvents >= 1 )
72   {
73     mMinimumMotionEvents = minimumPanEvents - 1; // Down is the first event
74   }
75 }
76
77 PanGestureRecognizer::~PanGestureRecognizer() = default;
78
79 void PanGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
80 {
81   PointState::Type primaryPointState(event.points[0].GetState());
82   GestureRecognizerPtr ptr(this); // To keep us from being destroyed during the life-time of this method
83
84   if (primaryPointState == PointState::INTERRUPTED)
85   {
86     if ( ( mState == STARTED ) || ( mState == POSSIBLE ) )
87     {
88       // If our pan had started and we are interrupted, then tell Core that pan is cancelled.
89       mTouchEvents.push_back(event);
90       SendPan(GestureState::CANCELLED, event);
91     }
92     mState = CLEAR; // We should change our state to CLEAR.
93     mTouchEvents.clear();
94   }
95   else
96   {
97     switch (mState)
98     {
99       case CLEAR:
100       {
101         if ( ( primaryPointState == PointState::DOWN ) || ( primaryPointState == PointState::STATIONARY ) || ( primaryPointState == PointState::MOTION ))
102         {
103           mPrimaryTouchDownLocation = event.points[0].GetScreenPosition();
104           mPrimaryTouchDownTime = event.time;
105           mMotionEvents = 0;
106           if (event.GetPointCount() == mMinimumTouchesRequired)
107           {
108             // We have satisfied the minimum touches required for a pan, tell core that a gesture may be possible and change our state accordingly.
109             mState = POSSIBLE;
110             SendPan(GestureState::POSSIBLE, event);
111           }
112
113           mTouchEvents.push_back(event);
114         }
115         break;
116       }
117
118       case POSSIBLE:
119       {
120         unsigned int pointCount(event.GetPointCount());
121         if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
122         {
123           if (primaryPointState == PointState::MOTION)
124           {
125             mTouchEvents.push_back(event);
126             mMotionEvents++;
127
128             Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
129
130             if ( ( mMotionEvents >= mMinimumMotionEvents ) &&
131                  ( delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) ) )
132             {
133               // If the touch point(s) have moved enough distance to be considered a pan, then tell Core that the pan gesture has started and change our state accordingly.
134               mState = STARTED;
135               SendPan(GestureState::STARTED, event);
136             }
137           }
138           else if (primaryPointState == PointState::UP)
139           {
140             Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
141             if (delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) )
142             {
143               SendPan(GestureState::STARTED, event);
144               mTouchEvents.push_back(event);
145               SendPan(GestureState::FINISHED, event);
146             }
147             else
148             {
149               // If we have lifted the primary touch point then tell core the pan is cancelled and change our state to CLEAR.
150               SendPan(GestureState::CANCELLED, event);
151             }
152             mState = CLEAR;
153             mTouchEvents.clear();
154           }
155         }
156         else
157         {
158           // We do not satisfy pan conditions, tell Core our Gesture has been cancelled.
159           SendPan(GestureState::CANCELLED, event);
160
161           if (pointCount == 1 && primaryPointState == PointState::UP)
162           {
163             // If we have lifted the primary touch point, then change our state to CLEAR...
164             mState = CLEAR;
165             mTouchEvents.clear();
166           }
167           else
168           {
169             // ...otherwise change it to FAILED.
170             mState = FAILED;
171           }
172         }
173         break;
174       }
175
176       case STARTED:
177       {
178         mTouchEvents.push_back(event);
179
180         unsigned int pointCount(event.GetPointCount());
181         if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
182         {
183           switch (primaryPointState)
184           {
185             case PointState::MOTION:
186               // Pan is continuing, tell Core.
187               SendPan(GestureState::CONTINUING, event);
188               break;
189
190             case PointState::UP:
191               // Pan is finally finished when our primary point is lifted, tell Core and change our state to CLEAR.
192               mState = CLEAR;
193               SendPan(GestureState::FINISHED, event);
194               mTouchEvents.clear();
195               break;
196
197             case PointState::STATIONARY:
198               if (pointCount == mMinimumTouchesRequired)
199               {
200                 Integration::PointContainerConstIterator iter = event.points.begin() + 1; // We already know the state of the first point
201                 for(; iter != event.points.end(); ++iter)
202                 {
203                   if(iter->GetState() == PointState::UP)
204                   {
205                     // The number of touch points will be less than the minimum required.  Inform core and change our state to FINISHED.
206                     SendPan(GestureState::FINISHED, event);
207                     mState = FINISHED;
208                     break;
209                   }
210                 }
211               }
212               break;
213
214             default:
215               break;
216           }
217         }
218         else
219         {
220           // We have gone outside of the pan requirements, inform Core that the gesture is finished.
221           SendPan(GestureState::FINISHED, event);
222
223           if (pointCount == 1 && primaryPointState == PointState::UP)
224           {
225             // If this was the primary point being released, then we change our state back to CLEAR...
226             mState = CLEAR;
227             mTouchEvents.clear();
228           }
229           else
230           {
231             // ...otherwise we change it to FINISHED.
232             mState = FINISHED;
233           }
234         }
235         break;
236       }
237
238       case FINISHED:
239       case FAILED:
240       {
241         if (primaryPointState == PointState::UP)
242         {
243           // Change our state back to clear when the primary touch point is released.
244           mState = CLEAR;
245           mTouchEvents.clear();
246         }
247         break;
248       }
249     }
250   }
251 }
252
253 void PanGestureRecognizer::Update(const GestureRequest& request)
254 {
255   const PanGestureRequest& pan = static_cast<const PanGestureRequest&>(request);
256
257   mMinimumTouchesRequired = pan.minTouches;
258   mMaximumTouchesRequired = pan.maxTouches;
259 }
260
261 void PanGestureRecognizer::SendPan(GestureState state, const Integration::TouchEvent& currentEvent)
262 {
263   PanGestureEvent gesture(state);
264   gesture.currentPosition = currentEvent.points[0].GetScreenPosition();
265   gesture.numberOfTouches = currentEvent.GetPointCount();
266
267   if ( mTouchEvents.size() > 1 )
268   {
269     // Get the second last event in the queue, the last one is the current event
270     const Integration::TouchEvent& previousEvent( *( mTouchEvents.rbegin() + 1 ) );
271
272     Vector2 previousPosition( mPreviousPosition );
273     uint32_t previousTime( previousEvent.time );
274
275     // If we've just started then we want to remove the threshold from Core calculations.
276     if ( state == GestureState::STARTED )
277     {
278       previousPosition = mPrimaryTouchDownLocation;
279       previousTime = mPrimaryTouchDownTime;
280
281       // If it's a slow pan, we do not want to phase in the threshold over the first few pan-events
282       // A slow pan is defined as one that starts the specified number of milliseconds after the down-event
283       if ( ( currentEvent.time - previousTime ) > MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS )
284       {
285         mThresholdAdjustmentsRemaining = mThresholdTotalAdjustments;
286         mThresholdAdjustmentPerFrame = ( gesture.currentPosition - previousPosition ) / static_cast<float>( mThresholdTotalAdjustments );
287       }
288       else
289       {
290         mThresholdAdjustmentsRemaining = 0;
291         mThresholdAdjustmentPerFrame = Vector2::ZERO;
292       }
293     }
294
295     gesture.previousPosition = previousPosition;
296     gesture.timeDelta = currentEvent.time - previousTime;
297
298     // Apply the threshold with a phased approach
299     if ( mThresholdAdjustmentsRemaining > 0 )
300     {
301       --mThresholdAdjustmentsRemaining;
302       gesture.currentPosition -= mThresholdAdjustmentPerFrame * static_cast<float>( mThresholdAdjustmentsRemaining );
303     }
304
305     mPreviousPosition = gesture.currentPosition;
306   }
307   else
308   {
309     gesture.previousPosition = gesture.currentPosition;
310     gesture.timeDelta = 0;
311   }
312
313   gesture.time = currentEvent.time;
314
315   if( mScene )
316   {
317     // Create another handle so the recognizer cannot be destroyed during process function
318     GestureRecognizerPtr recognizerHandle = this;
319
320     mObserver.Process(*mScene, gesture);
321   }
322 }
323
324 } // namespace Internal
325
326 } // namespace Dali