2 * Copyright (c) 2019 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/accessibility/common/accessibility-gesture-detector.h>
24 #include <dali/public-api/events/touch-point.h>
26 #include <dali/integration-api/events/touch-event-integ.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 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 const unsigned int MINIMUM_TOUCHES_BEFORE_PAN(1);
47 const unsigned int MAXIMUM_TOUCHES_BEFORE_PAN(1);
48 } // unnamed namespace
51 AccessibilityGestureDetector::AccessibilityGestureDetector()
54 mGestureHandler(nullptr),
56 mThresholdAdjustmentsRemaining( 0 ),
57 mThresholdTotalAdjustments( MINIMUM_MOTION_DISTANCE_BEFORE_PAN * MINIMUM_MOTION_DISTANCE_TO_THRESHOLD_ADJUSTMENTS_RATIO ),
58 mPrimaryTouchDownTime( 0 ),
59 mMinimumTouchesRequired( MINIMUM_TOUCHES_BEFORE_PAN ),
60 mMaximumTouchesRequired( MAXIMUM_TOUCHES_BEFORE_PAN ),
61 mMinimumDistanceSquared( MINIMUM_MOTION_DISTANCE_BEFORE_PAN_SQUARED ),
62 mMinimumMotionEvents( MINIMUM_MOTION_EVENTS_BEFORE_PAN ),
67 AccessibilityGestureDetector::~AccessibilityGestureDetector()
71 void AccessibilityGestureDetector::SetGestureHandler(AccessibilityGestureHandler& handler)
73 mGestureHandler = &handler;
76 void AccessibilityGestureDetector::EmitPan(const AccessibilityGestureEvent gesture)
80 if(gesture.state == AccessibilityGestureEvent::Started)
87 mGestureHandler->HandlePanGesture(gesture);
89 if( (gesture.state == AccessibilityGestureEvent::Finished) ||
90 (gesture.state == AccessibilityGestureEvent::Cancelled) )
98 void AccessibilityGestureDetector::SendEvent(const Integration::TouchEvent& event)
100 PointState::Type primaryPointState(event.points[0].GetState());
102 if (primaryPointState == PointState::INTERRUPTED)
104 if ( ( mState == Started ) || ( mState == Possible ) )
106 // If our pan had started and we are interrupted, then tell Core that pan is cancelled.
107 mTouchEvents.push_back(event);
108 SendPan(AccessibilityGestureEvent::Cancelled, event);
110 mState = Clear; // We should change our state to Clear.
111 mTouchEvents.clear();
119 if ( ( primaryPointState == PointState::DOWN ) || ( primaryPointState == PointState::STATIONARY ) )
121 mPrimaryTouchDownLocation = event.points[0].GetScreenPosition();
122 mPrimaryTouchDownTime = event.time;
124 if (event.GetPointCount() == mMinimumTouchesRequired)
126 // We have satisfied the minimum touches required for a pan, tell core that a gesture may be possible and change our state accordingly.
128 SendPan(AccessibilityGestureEvent::Possible, event);
131 mTouchEvents.push_back(event);
138 unsigned int pointCount(event.GetPointCount());
139 if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
141 if (primaryPointState == PointState::MOTION)
143 mTouchEvents.push_back(event);
146 Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
148 if ( ( mMotionEvents >= mMinimumMotionEvents ) &&
149 ( delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) ) )
151 // 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.
153 SendPan(AccessibilityGestureEvent::Started, event);
156 else if (primaryPointState == PointState::UP)
158 Vector2 delta(event.points[0].GetScreenPosition() - mPrimaryTouchDownLocation);
159 if (delta.LengthSquared() >= static_cast<float>( mMinimumDistanceSquared ) )
161 SendPan(AccessibilityGestureEvent::Started, event);
162 mTouchEvents.push_back(event);
163 SendPan(AccessibilityGestureEvent::Finished, event);
167 // If we have lifted the primary touch point then tell core the pan is cancelled and change our state to Clear.
168 SendPan(AccessibilityGestureEvent::Cancelled, event);
171 mTouchEvents.clear();
176 // We do not satisfy pan conditions, tell Core our Gesture has been cancelled.
177 SendPan(AccessibilityGestureEvent::Cancelled, event);
179 if (pointCount == 1 && primaryPointState == PointState::UP)
181 // If we have lifted the primary touch point, then change our state to Clear...
183 mTouchEvents.clear();
187 // ...otherwise change it to Failed.
196 mTouchEvents.push_back(event);
198 unsigned int pointCount(event.GetPointCount());
199 if ( (pointCount >= mMinimumTouchesRequired)&&(pointCount <= mMaximumTouchesRequired) )
201 switch (primaryPointState)
203 case PointState::MOTION:
204 // Pan is continuing, tell Core.
205 SendPan(AccessibilityGestureEvent::Continuing, event);
209 // Pan is finally finished when our primary point is lifted, tell Core and change our state to Clear.
210 SendPan(AccessibilityGestureEvent::Finished, event);
212 mTouchEvents.clear();
215 case PointState::STATIONARY:
216 if (pointCount == mMinimumTouchesRequired)
218 Integration::PointContainerConstIterator iter = event.points.begin() + 1; // We already know the state of the first point
219 for(; iter != event.points.end(); ++iter)
221 if(iter->GetState() == PointState::UP)
223 // The number of touch points will be less than the minimum required. Inform core and change our state to Finished.
224 SendPan(AccessibilityGestureEvent::Finished, event);
238 // We have gone outside of the pan requirements, inform Core that the gesture is finished.
239 SendPan(AccessibilityGestureEvent::Finished, event);
241 if (pointCount == 1 && primaryPointState == PointState::UP)
243 // If this was the primary point being released, then we change our state back to Clear...
245 mTouchEvents.clear();
249 // ...otherwise we change it to Finished.
259 if (primaryPointState == PointState::UP)
261 // Change our state back to clear when the primary touch point is released.
263 mTouchEvents.clear();
271 void AccessibilityGestureDetector::SendPan(AccessibilityGestureEvent::State state, const Integration::TouchEvent& currentEvent)
273 AccessibilityGestureEvent gesture(state);
274 gesture.currentPosition = currentEvent.points[0].GetScreenPosition();
275 gesture.numberOfTouches = currentEvent.GetPointCount();
277 if ( mTouchEvents.size() > 1 )
279 // Get the second last event in the queue, the last one is the current event
280 const Integration::TouchEvent& previousEvent( *( mTouchEvents.rbegin() + 1 ) );
282 Vector2 previousPosition( mPreviousPosition );
283 uint32_t previousTime( previousEvent.time );
285 // If we've just started then we want to remove the threshold from Core calculations.
286 if ( state == AccessibilityGestureEvent::Started )
288 previousPosition = mPrimaryTouchDownLocation;
289 previousTime = mPrimaryTouchDownTime;
291 // If it's a slow pan, we do not want to phase in the threshold over the first few pan-events
292 // A slow pan is defined as one that starts the specified number of milliseconds after the down-event
293 if ( ( currentEvent.time - previousTime ) > MINIMUM_TIME_BEFORE_THRESHOLD_ADJUSTMENTS )
295 mThresholdAdjustmentsRemaining = mThresholdTotalAdjustments;
296 mThresholdAdjustmentPerFrame = ( gesture.currentPosition - previousPosition ) / static_cast<float>( mThresholdTotalAdjustments );
300 mThresholdAdjustmentsRemaining = 0;
301 mThresholdAdjustmentPerFrame = Vector2::ZERO;
305 gesture.previousPosition = previousPosition;
306 gesture.timeDelta = currentEvent.time - previousTime;
308 // Apply the threshold with a phased approach
309 if ( mThresholdAdjustmentsRemaining > 0 )
311 --mThresholdAdjustmentsRemaining;
312 gesture.currentPosition -= mThresholdAdjustmentPerFrame * static_cast<float>( mThresholdAdjustmentsRemaining );
315 mPreviousPosition = gesture.currentPosition;
319 gesture.previousPosition = gesture.currentPosition;
320 gesture.timeDelta = 0;
323 gesture.time = currentEvent.time;
328 } // namespace Adaptor
330 } // namespace Internal