2 * Copyright (c) 2020 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 <dali/internal/event/events/pan-gesture/pan-gesture-recognizer.h>
24 #include <dali/devel-api/events/touch-point.h>
26 #include <dali/integration-api/events/touch-event-integ.h>
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>
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 MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS( 100 );
44 const unsigned int MINIMUM_MOTION_EVENTS_BEFORE_PAN(2);
45 } // unnamed namespace
47 PanGestureRecognizer::PanGestureRecognizer( Observer& observer, Vector2 screenSize, const PanGestureRequest& request, int32_t minimumDistance, int32_t minimumPanEvents )
48 : GestureRecognizer( screenSize, GestureType::PAN ),
49 mObserver( observer ),
51 mThresholdAdjustmentsRemaining( 0 ),
52 mThresholdTotalAdjustments( static_cast<unsigned int>( MINIMUM_MOTION_DISTANCE_BEFORE_PAN * MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO ) ),
53 mPrimaryTouchDownTime( 0 ),
54 mMinimumTouchesRequired( request.minTouches ),
55 mMaximumTouchesRequired( request.maxTouches ),
56 mMinimumDistanceSquared( static_cast<unsigned int>( MINIMUM_MOTION_DISTANCE_BEFORE_PAN_SQUARED ) ),
57 mMinimumMotionEvents( MINIMUM_MOTION_EVENTS_BEFORE_PAN ),
60 if ( minimumDistance >= 0 )
62 mMinimumDistanceSquared = minimumDistance * minimumDistance;
64 // Usually, we do not want to apply the threshold straight away, but phased over the first few pans
65 // Set our distance to threshold adjustments ratio here.
66 float fMinimumDistance = static_cast<float>( minimumDistance );
67 mThresholdTotalAdjustments = static_cast<unsigned int>( fMinimumDistance * MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO );
70 if ( minimumPanEvents >= 1 )
72 mMinimumMotionEvents = minimumPanEvents - 1; // Down is the first event
76 PanGestureRecognizer::~PanGestureRecognizer() = default;
78 void PanGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
80 PointState::Type primaryPointState(event.points[0].GetState());
81 GestureRecognizerPtr ptr(this); // To keep us from being destroyed during the life-time of this method
83 if (primaryPointState == PointState::INTERRUPTED)
85 if ( ( mState == STARTED ) || ( mState == POSSIBLE ) )
87 // If our pan had started and we are interrupted, then tell Core that pan is cancelled.
88 mTouchEvents.push_back(event);
89 SendPan(GestureState::CANCELLED, event);
91 mState = CLEAR; // We should change our state to CLEAR.
100 if ( ( primaryPointState == PointState::DOWN ) || ( primaryPointState == PointState::STATIONARY ) || ( primaryPointState == PointState::MOTION ))
102 mPrimaryTouchDownLocation = event.points[0].GetScreenPosition();
103 mPrimaryTouchDownTime = event.time;
105 if (event.GetPointCount() == mMinimumTouchesRequired)
107 // We have satisfied the minimum touches required for a pan, tell core that a gesture may be possible and change our state accordingly.
109 SendPan(GestureState::POSSIBLE, event);
112 mTouchEvents.push_back(event);
119 unsigned int pointCount(event.GetPointCount());
120 if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
122 if (primaryPointState == PointState::MOTION)
124 mTouchEvents.push_back(event);
127 Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
129 if ( ( mMotionEvents >= mMinimumMotionEvents ) &&
130 ( delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) ) )
132 // 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 SendPan(GestureState::STARTED, event);
137 else if (primaryPointState == PointState::UP)
139 Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
140 if (delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) )
142 SendPan(GestureState::STARTED, event);
143 mTouchEvents.push_back(event);
144 SendPan(GestureState::FINISHED, event);
148 // If we have lifted the primary touch point then tell core the pan is cancelled and change our state to CLEAR.
149 SendPan(GestureState::CANCELLED, event);
152 mTouchEvents.clear();
157 // We do not satisfy pan conditions, tell Core our Gesture has been cancelled.
158 SendPan(GestureState::CANCELLED, event);
160 if (pointCount == 1 && primaryPointState == PointState::UP)
162 // If we have lifted the primary touch point, then change our state to CLEAR...
164 mTouchEvents.clear();
168 // ...otherwise change it to FAILED.
177 mTouchEvents.push_back(event);
179 unsigned int pointCount(event.GetPointCount());
180 if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
182 switch (primaryPointState)
184 case PointState::MOTION:
185 // Pan is continuing, tell Core.
186 SendPan(GestureState::CONTINUING, event);
190 // Pan is finally finished when our primary point is lifted, tell Core and change our state to CLEAR.
192 SendPan(GestureState::FINISHED, event);
193 mTouchEvents.clear();
196 case PointState::STATIONARY:
197 if (pointCount == mMinimumTouchesRequired)
199 Integration::PointContainerConstIterator iter = event.points.begin() + 1; // We already know the state of the first point
200 for(; iter != event.points.end(); ++iter)
202 if(iter->GetState() == PointState::UP)
204 // The number of touch points will be less than the minimum required. Inform core and change our state to FINISHED.
205 SendPan(GestureState::FINISHED, event);
219 // We have gone outside of the pan requirements, inform Core that the gesture is finished.
220 SendPan(GestureState::FINISHED, event);
222 if (pointCount == 1 && primaryPointState == PointState::UP)
224 // If this was the primary point being released, then we change our state back to CLEAR...
226 mTouchEvents.clear();
230 // ...otherwise we change it to FINISHED.
240 if (primaryPointState == PointState::UP)
242 // Change our state back to clear when the primary touch point is released.
244 mTouchEvents.clear();
252 void PanGestureRecognizer::Update(const GestureRequest& request)
254 const PanGestureRequest& pan = static_cast<const PanGestureRequest&>(request);
256 mMinimumTouchesRequired = pan.minTouches;
257 mMaximumTouchesRequired = pan.maxTouches;
260 void PanGestureRecognizer::SendPan(GestureState state, const Integration::TouchEvent& currentEvent)
262 PanGestureEvent gesture(state);
263 gesture.currentPosition = currentEvent.points[0].GetScreenPosition();
264 gesture.numberOfTouches = currentEvent.GetPointCount();
266 if ( mTouchEvents.size() > 1 )
268 // Get the second last event in the queue, the last one is the current event
269 const Integration::TouchEvent& previousEvent( *( mTouchEvents.rbegin() + 1 ) );
271 Vector2 previousPosition( mPreviousPosition );
272 uint32_t previousTime( previousEvent.time );
274 // If we've just started then we want to remove the threshold from Core calculations.
275 if ( state == GestureState::STARTED )
277 previousPosition = mPrimaryTouchDownLocation;
278 previousTime = mPrimaryTouchDownTime;
280 // If it's a slow pan, we do not want to phase in the threshold over the first few pan-events
281 // A slow pan is defined as one that starts the specified number of milliseconds after the down-event
282 if ( ( currentEvent.time - previousTime ) > MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS )
284 mThresholdAdjustmentsRemaining = mThresholdTotalAdjustments;
285 mThresholdAdjustmentPerFrame = ( gesture.currentPosition - previousPosition ) / static_cast<float>( mThresholdTotalAdjustments );
289 mThresholdAdjustmentsRemaining = 0;
290 mThresholdAdjustmentPerFrame = Vector2::ZERO;
294 gesture.previousPosition = previousPosition;
295 gesture.timeDelta = currentEvent.time - previousTime;
297 // Apply the threshold with a phased approach
298 if ( mThresholdAdjustmentsRemaining > 0 )
300 --mThresholdAdjustmentsRemaining;
301 gesture.currentPosition -= mThresholdAdjustmentPerFrame * static_cast<float>( mThresholdAdjustmentsRemaining );
304 mPreviousPosition = gesture.currentPosition;
308 gesture.previousPosition = gesture.currentPosition;
309 gesture.timeDelta = 0;
312 gesture.time = currentEvent.time;
316 // Create another handle so the recognizer cannot be destroyed during process function
317 GestureRecognizerPtr recognizerHandle = this;
319 mObserver.Process(*mScene, gesture);
323 } // namespace Internal