2 * Copyright (c) 2022 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/devel-api/events/touch-point.h>
25 #include <dali/public-api/math/vector2.h>
27 #include <dali/integration-api/events/touch-event-integ.h>
28 #include <dali/integration-api/platform-abstraction.h>
29 #include <dali/internal/event/common/thread-local-storage.h>
30 #include <dali/internal/event/events/gesture-requests.h>
38 // TODO: Set these according to DPI
39 const float MAXIMUM_MOTION_ALLOWED = 60.0f;
41 } // unnamed namespace
43 LongPressGestureRecognizer::LongPressGestureRecognizer(Observer& observer, Vector2 screenSize, const LongPressGestureRequest& request, uint32_t minimumHoldingTime)
44 : GestureRecognizer(screenSize, GestureType::LONG_PRESS),
47 mMinimumTouchesRequired(request.minTouches),
48 mMaximumTouchesRequired(request.maxTouches),
51 mMinimumHoldingTime(minimumHoldingTime)
55 LongPressGestureRecognizer::~LongPressGestureRecognizer()
57 if(mTimerId != 0 && ThreadLocalStorage::Created())
59 Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
60 platformAbstraction.CancelTimer(mTimerId);
64 void LongPressGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
66 unsigned int pointCount(event.GetPointCount());
67 Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
68 GestureRecognizerPtr ptr(this); // To keep us from being destroyed during the life-time of this method
71 // CLEAR: Wait till one point touches the screen before starting timer.
74 const Integration::Point& point = event.points[0];
76 if(point.GetState() == PointState::DOWN)
78 mTouchPositions.clear();
79 mTouchPositions[point.GetDeviceId()] = point.GetScreenPosition();
81 mTouchTime = event.time;
85 platformAbstraction.CancelTimer(mTimerId);
87 mTimerId = platformAbstraction.StartTimer(mMinimumHoldingTime, MakeCallback(this, &LongPressGestureRecognizer::TimerCallback));
89 // A long press gesture may be possible, tell Core about this and change state to TOUCHED.
91 EmitGesture(GestureState::POSSIBLE);
97 // TOUCHED: Monitor movement and addition/removal of points.
100 if(pointCount > mMaximumTouchesRequired)
102 // A long press did not occur, tell Core that it was cancelled and change state to FAILED.
103 EmitGesture(GestureState::CANCELLED);
104 mTouchPositions.clear();
105 platformAbstraction.CancelTimer(mTimerId);
113 for(Integration::PointContainerConstIterator iter = event.points.begin(), endIter = event.points.end();
114 iter != endIter && !endLoop;
117 switch(iter->GetState())
120 case PointState::DOWN:
122 mTouchPositions[iter->GetDeviceId()] = iter->GetScreenPosition();
128 case PointState::INTERRUPTED:
130 // System has interrupted us, long press is not possible, inform Core
131 EmitGesture(GestureState::CANCELLED);
132 mTouchPositions.clear();
133 platformAbstraction.CancelTimer(mTimerId);
135 mState = (pointCount == 1) ? CLEAR : FAILED; // Change state to CLEAR if only one point, FAILED otherwise.
140 case PointState::MOTION:
142 const Vector2 touchPosition(mTouchPositions[iter->GetDeviceId()] - iter->GetScreenPosition());
143 float distanceSquared = touchPosition.LengthSquared();
145 if(distanceSquared > (MAXIMUM_MOTION_ALLOWED * MAXIMUM_MOTION_ALLOWED))
147 // We have moved more than the allowable motion for a long press gesture. Inform Core and change state to FAILED.
148 EmitGesture(GestureState::CANCELLED);
149 platformAbstraction.CancelTimer(mTimerId);
157 case PointState::STATIONARY:
158 case PointState::LEAVE:
167 // FAILED/FINISHED: Monitor the touches, waiting for all touches to be released.
171 // eventually the final touch point will be removed, marking the end of this gesture.
174 PointState::Type primaryPointState = event.points[0].GetState();
176 if((primaryPointState == PointState::UP) || (primaryPointState == PointState::INTERRUPTED))
178 if(mState == FINISHED)
180 // When the last touch point is lifted, we should inform the Core that the Long press has finished.
181 EmitGesture(GestureState::FINISHED);
183 mTouchPositions.clear();
184 mState = CLEAR; // Reset state to clear when last touch point is lifted.
192 void LongPressGestureRecognizer::Update(const GestureRequest& request)
194 const LongPressGestureRequest& longPress = static_cast<const LongPressGestureRequest&>(request);
196 mMinimumTouchesRequired = longPress.minTouches;
197 mMaximumTouchesRequired = longPress.maxTouches;
200 void LongPressGestureRecognizer::SetMinimumHoldingTime(uint32_t time)
202 mMinimumHoldingTime = time;
205 bool LongPressGestureRecognizer::TimerCallback()
207 EmitGesture(GestureState::STARTED);
216 void LongPressGestureRecognizer::EmitGesture(GestureState state)
218 unsigned int touchPoints(static_cast<unsigned int>(mTouchPositions.size()));
220 // We should tell Core about the POSSIBLE and CANCELLED states regardless of whether we have satisfied long press requirements.
221 if((state == GestureState::POSSIBLE) ||
222 (state == GestureState::CANCELLED) ||
223 (touchPoints >= mMinimumTouchesRequired))
225 LongPressGestureEvent longPress(state);
226 longPress.numberOfTouches = touchPoints;
228 for(std::map<int, Vector2>::iterator iter = mTouchPositions.begin(), endIter = mTouchPositions.end();
232 longPress.point += iter->second;
234 longPress.point /= static_cast<float>(touchPoints);
236 longPress.time = mTouchTime;
237 if(state != GestureState::POSSIBLE)
239 longPress.time += mMinimumHoldingTime;
241 longPress.sourceType = mSourceType;
242 longPress.sourceData = mSourceData;
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