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