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/long-press-gesture/long-press-gesture-recognizer.h>
24 #include <dali/public-api/events/touch-point.h>
25 #include <dali/public-api/math/vector2.h>
27 #include <dali/internal/event/common/thread-local-storage.h>
28 #include <dali/internal/event/events/gesture-requests.h>
29 #include <dali/integration-api/events/touch-event-integ.h>
30 #include <dali/integration-api/platform-abstraction.h>
40 // TODO: Set these according to DPI
41 const float MAXIMUM_MOTION_ALLOWED = 60.0f;
43 } // unnamed namespace
45 LongPressGestureRecognizer::LongPressGestureRecognizer(Observer& observer, Vector2 screenSize, const LongPressGestureRequest& request, uint32_t minimumHoldingTime )
46 : GestureRecognizer( screenSize, Gesture::LongPress ),
47 mObserver( observer ),
49 mMinimumTouchesRequired( request.minTouches ),
50 mMaximumTouchesRequired( request.maxTouches ),
53 mMinimumHoldingTime( minimumHoldingTime )
57 LongPressGestureRecognizer::~LongPressGestureRecognizer()
59 if( mTimerId != 0 && ThreadLocalStorage::Created() )
61 Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
62 platformAbstraction.CancelTimer(mTimerId);
66 void LongPressGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
68 unsigned int pointCount( event.GetPointCount() );
69 Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
70 GestureRecognizerPtr ptr(this); // To keep us from being destroyed during the life-time of this method
74 // Clear: Wait till one point touches the screen before starting timer.
77 const Integration::Point& point = event.points[0];
79 if ( point.GetState() == PointState::DOWN )
81 mTouchPositions.clear();
82 mTouchPositions[point.GetDeviceId()] = point.GetScreenPosition();
84 mTouchTime = event.time;
88 platformAbstraction.CancelTimer(mTimerId);
90 mTimerId = platformAbstraction.StartTimer( mMinimumHoldingTime, MakeCallback( this, &LongPressGestureRecognizer::TimerCallback ) );
92 // A long press gesture may be possible, tell Core about this and change state to Touched.
94 EmitGesture( Gesture::Possible );
100 // Touched: Monitor movement and addition/removal of points.
103 if (pointCount > mMaximumTouchesRequired)
105 // A long press did not occur, tell Core that it was cancelled and change state to Failed.
106 EmitGesture( Gesture::Cancelled );
107 mTouchPositions.clear();
108 platformAbstraction.CancelTimer(mTimerId);
116 for ( Integration::PointContainerConstIterator iter = event.points.begin(), endIter = event.points.end();
117 iter != endIter && !endLoop; ++iter)
119 switch( iter->GetState() )
122 case PointState::DOWN:
124 mTouchPositions[iter->GetDeviceId()] = iter->GetScreenPosition();
130 case PointState::INTERRUPTED:
132 // System has interrupted us, long press is not possible, inform Core
133 EmitGesture( Gesture::Cancelled );
134 mTouchPositions.clear();
135 platformAbstraction.CancelTimer(mTimerId);
137 mState = ( pointCount == 1 ) ? Clear : Failed; // Change state to Clear if only one point, Failed otherwise.
142 case PointState::MOTION:
144 const Vector2 touchPosition( mTouchPositions[iter->GetDeviceId()] - iter->GetScreenPosition() );
145 float distanceSquared = touchPosition.LengthSquared();
147 if (distanceSquared > ( MAXIMUM_MOTION_ALLOWED * MAXIMUM_MOTION_ALLOWED ) )
149 // We have moved more than the allowable motion for a long press gesture. Inform Core and change state to Failed.
150 EmitGesture( Gesture::Cancelled );
151 platformAbstraction.CancelTimer(mTimerId);
159 case PointState::STATIONARY:
160 case PointState::LEAVE:
169 // Failed/Finished: Monitor the touches, waiting for all touches to be released.
173 // eventually the final touch point will be removed, marking the end of this gesture.
174 if ( pointCount == 1 )
176 PointState::Type primaryPointState = event.points[0].GetState();
178 if ( (primaryPointState == PointState::UP) || (primaryPointState == PointState::INTERRUPTED) )
180 if(mState == Finished)
182 // When the last touch point is lifted, we should inform the Core that the Long press has finished.
183 EmitGesture(Gesture::Finished);
185 mTouchPositions.clear();
186 mState = Clear; // Reset state to clear when last touch point is lifted.
194 void LongPressGestureRecognizer::Update(const GestureRequest& request)
196 const LongPressGestureRequest& longPress = static_cast<const LongPressGestureRequest&>(request);
198 mMinimumTouchesRequired = longPress.minTouches;
199 mMaximumTouchesRequired = longPress.maxTouches;
202 void LongPressGestureRecognizer::SetMinimumHoldingTime( uint32_t time )
204 mMinimumHoldingTime = time;
208 bool LongPressGestureRecognizer::TimerCallback()
210 EmitGesture(Gesture::Started);
219 void LongPressGestureRecognizer::EmitGesture(Gesture::State state)
221 unsigned int touchPoints ( static_cast<unsigned int>( mTouchPositions.size() ) );
223 // We should tell Core about the Possible and Cancelled states regardless of whether we have satisfied long press requirements.
224 if ( (state == Gesture::Possible) ||
225 (state == Gesture::Cancelled) ||
226 (touchPoints >= mMinimumTouchesRequired) )
228 LongPressGestureEvent longPress( state );
229 longPress.numberOfTouches = touchPoints;
231 for (std::map<int, Vector2>::iterator iter = mTouchPositions.begin(), endIter = mTouchPositions.end();
232 iter != endIter; ++iter)
234 longPress.point += iter->second;
236 longPress.point /= static_cast<float>( touchPoints );
238 longPress.time = mTouchTime;
239 if ( state != Gesture::Possible )
241 longPress.time += mMinimumHoldingTime;
246 // Create another handle so the recognizer cannot be destroyed during process function
247 GestureRecognizerPtr recognizerHandle = this;
249 mObserver.Process(*mScene, longPress);
254 } // namespace Internal