2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "pan-gesture-detector-base.h"
24 #include <dali/public-api/events/touch-point.h>
26 #include <dali/integration-api/events/gesture-requests.h>
27 #include <dali/integration-api/events/touch-event-integ.h>
30 #include <base/environment-options.h>
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
51 PanGestureDetectorBase::PanGestureDetectorBase(Vector2 screenSize, const Integration::PanGestureRequest& request, EnvironmentOptions* environmentOptions)
52 : GestureDetector( screenSize, Gesture::Pan ),
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 ),
63 if ( environmentOptions )
65 int minimumDistance = environmentOptions->GetMinimumPanDistance();
66 if ( minimumDistance >= 0 )
68 mMinimumDistanceSquared = minimumDistance * minimumDistance;
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 mThresholdTotalAdjustments = minimumDistance * MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO;
75 int minimumEvents = environmentOptions->GetMinimumPanEvents();
76 if ( minimumEvents >= 1 )
78 mMinimumMotionEvents = minimumEvents - 1; // Down is the first event
83 PanGestureDetectorBase::~PanGestureDetectorBase()
87 void PanGestureDetectorBase::SendEvent(const Integration::TouchEvent& event)
89 PointState::Type primaryPointState(event.points[0].GetState());
91 if (primaryPointState == PointState::INTERRUPTED)
93 if ( ( mState == Started ) || ( mState == Possible ) )
95 // If our pan had started and we are interrupted, then tell Core that pan is cancelled.
96 mTouchEvents.push_back(event);
97 SendPan(Gesture::Cancelled, event);
99 mState = Clear; // We should change our state to Clear.
100 mTouchEvents.clear();
108 if (primaryPointState == PointState::DOWN)
110 mPrimaryTouchDownLocation = event.points[0].GetScreenPosition();
111 mPrimaryTouchDownTime = event.time;
113 if (event.GetPointCount() == mMinimumTouchesRequired)
115 // We have satisfied the minimum touches required for a pan, tell core that a gesture may be possible and change our state accordingly.
117 SendPan(Gesture::Possible, event);
120 mTouchEvents.push_back(event);
127 unsigned int pointCount(event.GetPointCount());
128 if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
130 if (primaryPointState == PointState::MOTION)
132 mTouchEvents.push_back(event);
135 Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
137 if ( ( mMotionEvents >= mMinimumMotionEvents ) &&
138 ( delta.LengthSquared() >= mMinimumDistanceSquared ) )
140 // 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 SendPan(Gesture::Started, event);
145 else if (primaryPointState == PointState::UP)
147 Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
148 if(delta.LengthSquared() >= mMinimumDistanceSquared)
150 SendPan(Gesture::Started, event);
151 mTouchEvents.push_back(event);
152 SendPan(Gesture::Finished, event);
156 // If we have lifted the primary touch point then tell core the pan is cancelled and change our state to Clear.
157 SendPan(Gesture::Cancelled, event);
160 mTouchEvents.clear();
165 // We do not satisfy pan conditions, tell Core our Gesture has been cancelled.
166 SendPan(Gesture::Cancelled, event);
168 if (pointCount == 1 && primaryPointState == PointState::UP)
170 // If we have lifted the primary touch point, then change our state to Clear...
172 mTouchEvents.clear();
176 // ...otherwise change it to Failed.
185 mTouchEvents.push_back(event);
187 unsigned int pointCount(event.GetPointCount());
188 if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
190 switch (primaryPointState)
192 case PointState::MOTION:
193 // Pan is continuing, tell Core.
194 SendPan(Gesture::Continuing, event);
198 // Pan is finally finished when our primary point is lifted, tell Core and change our state to Clear.
199 SendPan(Gesture::Finished, event);
201 mTouchEvents.clear();
204 case PointState::STATIONARY:
205 if (pointCount == mMinimumTouchesRequired)
207 Integration::PointContainerConstIterator iter = event.points.begin() + 1; // We already know the state of the first point
208 for(; iter != event.points.end(); ++iter)
210 if(iter->GetState() == PointState::UP)
212 // The number of touch points will be less than the minimum required. Inform core and change our state to Finished.
213 SendPan(Gesture::Finished, event);
227 // We have gone outside of the pan requirements, inform Core that the gesture is finished.
228 SendPan(Gesture::Finished, event);
230 if (pointCount == 1 && primaryPointState == PointState::UP)
232 // If this was the primary point being released, then we change our state back to Clear...
234 mTouchEvents.clear();
238 // ...otherwise we change it to Finished.
248 if (primaryPointState == PointState::UP)
250 // Change our state back to clear when the primary touch point is released.
252 mTouchEvents.clear();
260 void PanGestureDetectorBase::Update(const Integration::GestureRequest& request)
262 const Integration::PanGestureRequest& pan = static_cast<const Integration::PanGestureRequest&>(request);
264 mMinimumTouchesRequired = pan.minTouches;
265 mMaximumTouchesRequired = pan.maxTouches;
268 void PanGestureDetectorBase::SendPan(Gesture::State state, const Integration::TouchEvent& currentEvent)
270 Integration::PanGestureEvent gesture(state);
271 gesture.currentPosition = currentEvent.points[0].GetScreenPosition();
272 gesture.numberOfTouches = currentEvent.GetPointCount();
274 if ( mTouchEvents.size() > 1 )
276 // Get the second last event in the queue, the last one is the current event
277 const Integration::TouchEvent& previousEvent( *( mTouchEvents.rbegin() + 1 ) );
279 Vector2 previousPosition( mPreviousPosition );
280 unsigned long previousTime( previousEvent.time );
282 // If we've just started then we want to remove the threshold from Core calculations.
283 if ( state == Gesture::Started )
285 previousPosition = mPrimaryTouchDownLocation;
286 previousTime = mPrimaryTouchDownTime;
288 // If it's a slow pan, we do not want to phase in the threshold over the first few pan-events
289 // A slow pan is defined as one that starts the specified number of milliseconds after the down-event
290 if ( ( currentEvent.time - previousTime ) > MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS )
292 mThresholdAdjustmentsRemaining = mThresholdTotalAdjustments;
293 mThresholdAdjustmentPerFrame = ( gesture.currentPosition - previousPosition ) / mThresholdTotalAdjustments;
297 mThresholdAdjustmentsRemaining = 0;
298 mThresholdAdjustmentPerFrame = Vector2::ZERO;
302 gesture.previousPosition = previousPosition;
303 gesture.timeDelta = currentEvent.time - previousTime;
305 // Apply the threshold with a phased approach
306 if ( mThresholdAdjustmentsRemaining > 0 )
308 --mThresholdAdjustmentsRemaining;
309 gesture.currentPosition -= mThresholdAdjustmentPerFrame * mThresholdAdjustmentsRemaining;
312 mPreviousPosition = gesture.currentPosition;
316 gesture.previousPosition = gesture.currentPosition;
317 gesture.timeDelta = 0;
320 gesture.time = currentEvent.time;
325 } // namespace Adaptor
327 } // namespace Internal