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/integration-api/events/touch-event-combiner.h>
23 #include <cmath> // abs<float>
26 #include <dali/integration-api/events/hover-event-integ.h>
27 #include <dali/integration-api/events/touch-event-integ.h>
28 #include <dali/public-api/common/dali-common.h>
36 const unsigned long DEFAULT_MINIMUM_MOTION_TIME(1u);
37 const Vector2 DEFAULT_MINIMUM_MOTION_DISTANCE(1.0f, 1.0f);
38 } // unnamed namespace
40 struct TouchEventCombiner::PointInfo
46 * @param[in] touchPoint The point to add.
47 * @param[in] pointTime The time of the point event.
49 PointInfo(const Point& touchPoint, uint32_t pointTime)
57 Point point; ///< The point.
58 uint32_t time; ///< The time the point event took place.
61 TouchEventCombiner::TouchEventCombiner()
62 : mMinMotionTime(DEFAULT_MINIMUM_MOTION_TIME),
63 mMinMotionDistance(DEFAULT_MINIMUM_MOTION_DISTANCE)
67 TouchEventCombiner::TouchEventCombiner(uint32_t minMotionTime, float minMotionXDistance, float minMotionYDistance)
68 : mMinMotionTime(minMotionTime),
69 mMinMotionDistance(minMotionXDistance, minMotionYDistance)
71 DALI_ASSERT_ALWAYS(minMotionXDistance >= 0.0f && minMotionYDistance >= 0.0f && "Negative values not allowed\n");
74 TouchEventCombiner::TouchEventCombiner(uint32_t minMotionTime, Vector2 minMotionDistance)
75 : mMinMotionTime(minMotionTime),
76 mMinMotionDistance(minMotionDistance)
78 DALI_ASSERT_ALWAYS(minMotionDistance.x >= 0.0f && minMotionDistance.y >= 0.0f && "Negative values not allowed\n");
81 TouchEventCombiner::~TouchEventCombiner() = default;
83 TouchEventCombiner::EventDispatchType TouchEventCombiner::GetNextTouchEvent(const Point& point, uint32_t time, TouchEvent& touchEvent, HoverEvent& hoverEvent, bool isMultiTouchEvent)
85 TouchEventCombiner::EventDispatchType dispatchEvent(TouchEventCombiner::DISPATCH_NONE);
86 const PointState::Type state = point.GetState();
87 const int deviceId = point.GetDeviceId();
88 const Device::Class::Type deviceType = point.GetDeviceClass();
92 case PointState::STARTED:
94 touchEvent.time = time;
95 bool addToContainer(true);
97 // Iterate through already stored touch points and add to TouchEvent
98 for(PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter)
100 if(iter->point.GetDeviceId() != deviceId)
102 if(!isMultiTouchEvent)
104 iter->point.SetState(PointState::STATIONARY);
109 // System has sent us two down points for the same point ID, update our stored data to latest.
110 // We do not want to emit another down event for this Point Device ID.
112 addToContainer = false;
116 touchEvent.AddPoint(iter->point);
119 // Add new touch point to the list and to the TouchEvent
122 mPressedPoints.push_back(PointInfo(point, time));
123 touchEvent.AddPoint(point);
124 dispatchEvent = TouchEventCombiner::DISPATCH_TOUCH; // Only dispatch touch event if just added to container
126 // Check whether hover event was dispatched previously
127 if(!mHoveredPoints.empty())
129 hoverEvent.time = time;
131 PointInfoContainer::iterator match(mHoveredPoints.end());
132 for(PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter)
134 if(deviceId == iter->point.GetDeviceId())
137 // Add new point to the HoverEvent
138 iter->point.SetState(PointState::FINISHED);
139 hoverEvent.AddPoint(iter->point);
143 iter->point.SetState(PointState::STATIONARY);
144 hoverEvent.AddPoint(iter->point);
148 if(match != mHoveredPoints.end())
150 mHoveredPoints.erase(match);
151 dispatchEvent = TouchEventCombiner::DISPATCH_BOTH; // We should only dispatch hover events if the point was actually hovered in this window
159 case PointState::FINISHED:
161 touchEvent.time = time;
163 // Find pressed touch point in local list (while also adding the stored points to the touchEvent)
164 PointInfoContainer::iterator match(mPressedPoints.end());
165 for(PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter)
167 if(deviceId == iter->point.GetDeviceId())
171 // Add new point to the TouchEvent
172 touchEvent.AddPoint(point);
176 if(!isMultiTouchEvent)
178 iter->point.SetState(PointState::STATIONARY);
180 touchEvent.AddPoint(iter->point);
184 if(match != mPressedPoints.end())
186 mPressedPoints.erase(match);
187 dispatchEvent = TouchEventCombiner::DISPATCH_TOUCH; // We should only dispatch touch events if the point was actually pressed in this window
189 // Iterate through already stored touch points for HoverEvent and delete them
190 for(PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter)
192 if(iter->point.GetDeviceId() == deviceId)
194 iter = mHoveredPoints.erase(iter);
198 if(deviceType == Device::Class::Type::MOUSE)
200 hoverEvent.time = time;
201 Point hoverPoint(point);
202 hoverPoint.SetState(PointState::STARTED); // The first hover event received
203 mHoveredPoints.push_back(PointInfo(hoverPoint, time));
204 hoverEvent.AddPoint(hoverPoint);
205 dispatchEvent = TouchEventCombiner::DISPATCH_BOTH;
211 case PointState::MOTION:
213 bool fromNewDeviceId = false;
215 if(!mPressedPoints.empty())
217 touchEvent.time = time;
220 PointInfoContainer::iterator match = mPressedPoints.end();
221 const Vector2& pointScreenPosition = point.GetScreenPosition();
222 for(PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter)
224 if(deviceId == iter->point.GetDeviceId())
226 uint32_t timeDiff(time - iter->time);
228 if(timeDiff < mMinMotionTime)
230 // Motion event sent too soon after previous event so ignore
235 const Vector2& currentScreenPosition = iter->point.GetScreenPosition();
236 if((std::abs(pointScreenPosition.x - currentScreenPosition.x) < mMinMotionDistance.x) &&
237 (std::abs(pointScreenPosition.y - currentScreenPosition.y) < mMinMotionDistance.y))
239 // Not enough positional change from last event so ignore
246 // Add new touch point to the TouchEvent
247 touchEvent.AddPoint(point);
251 if(!isMultiTouchEvent)
253 iter->point.SetState(PointState::STATIONARY);
255 touchEvent.AddPoint(iter->point);
259 if(match != mPressedPoints.end())
261 PointInfo matchedPoint(point, time);
262 std::swap(*match, matchedPoint);
264 dispatchEvent = TouchEventCombiner::DISPATCH_TOUCH; // Dispatch touch event
268 fromNewDeviceId = true;
272 // Dispatch hover event if no previous down event received or the motion event comes from a new device ID
273 // On touch down the MOTION -> STARTED event received at the same time. In this case, unnecessary hoverEvent may be triggered by the MOTION event.
274 // So, in case of touch device, it is ignored.
275 if((mPressedPoints.empty() || fromNewDeviceId) && deviceType != Device::Class::Type::TOUCH)
277 hoverEvent.time = time;
279 // Iterate through already stored touch points and add to HoverEvent
281 PointInfoContainer::iterator match = mHoveredPoints.end();
282 const Vector2& pointScreenPosition = point.GetScreenPosition();
283 for(PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter)
285 if(iter->point.GetDeviceId() == deviceId)
287 uint32_t timeDiff(time - iter->time);
289 if(timeDiff < mMinMotionTime)
291 // Motion event sent too soon after previous event so ignore
296 const Vector2& currentScreenPosition = iter->point.GetScreenPosition();
297 if((std::abs(pointScreenPosition.x - currentScreenPosition.x) < mMinMotionDistance.x) &&
298 (std::abs(pointScreenPosition.y - currentScreenPosition.y) < mMinMotionDistance.y))
300 // Not enough positional change from last event so ignore
307 // Add new touch point to the HoverEvent
308 hoverEvent.AddPoint(point);
312 if(!isMultiTouchEvent)
314 iter->point.SetState(PointState::STATIONARY);
316 hoverEvent.AddPoint(iter->point);
320 // Add new hover point to the list and to the HoverEvent
321 if(!ignore) // Only dispatch hover event when it should not be ignored
323 if(match == mHoveredPoints.end())
325 Point hoverPoint(point);
326 hoverPoint.SetState(PointState::STARTED); // The first hover event received
327 mHoveredPoints.push_back(PointInfo(hoverPoint, time));
328 hoverEvent.AddPoint(hoverPoint);
332 PointInfo matchedPoint(point, time);
333 std::swap(*match, matchedPoint);
336 if(dispatchEvent == TouchEventCombiner::DISPATCH_TOUCH)
338 dispatchEvent = TouchEventCombiner::DISPATCH_BOTH;
342 dispatchEvent = TouchEventCombiner::DISPATCH_HOVER;
349 case PointState::INTERRUPTED:
351 // We should still tell core about the interruption.
352 if(!mPressedPoints.empty())
354 touchEvent.AddPoint(point);
355 dispatchEvent = TouchEventCombiner::DISPATCH_TOUCH;
357 if((!mHoveredPoints.empty()))
359 hoverEvent.AddPoint(point);
360 dispatchEvent = dispatchEvent == TouchEventCombiner::DISPATCH_TOUCH ? TouchEventCombiner::DISPATCH_BOTH : TouchEventCombiner::DISPATCH_HOVER;
370 return dispatchEvent;
373 void TouchEventCombiner::SetMinimumMotionTimeThreshold(uint32_t minTime)
375 mMinMotionTime = minTime;
378 void TouchEventCombiner::SetMinimumMotionDistanceThreshold(float minDistance)
380 DALI_ASSERT_ALWAYS(minDistance >= 0.0f && "Negative values not allowed\n");
382 mMinMotionDistance.x = mMinMotionDistance.y = minDistance;
385 void TouchEventCombiner::SetMinimumMotionDistanceThreshold(float minXDistance, float minYDistance)
387 DALI_ASSERT_ALWAYS(minXDistance >= 0.0f && minYDistance >= 0.0f && "Negative values not allowed\n");
389 mMinMotionDistance.x = minXDistance;
390 mMinMotionDistance.y = minYDistance;
393 void TouchEventCombiner::SetMinimumMotionDistanceThreshold(Vector2 minDistance)
395 DALI_ASSERT_ALWAYS(minDistance.x >= 0.0f && minDistance.y >= 0.0f && "Negative values not allowed\n");
397 mMinMotionDistance = minDistance;
400 unsigned long TouchEventCombiner::GetMinimumMotionTimeThreshold() const
402 return mMinMotionTime;
405 Vector2 TouchEventCombiner::GetMinimumMotionDistanceThreshold() const
407 return mMinMotionDistance;
410 void TouchEventCombiner::Reset()
412 mPressedPoints.clear();
413 mHoveredPoints.clear();
416 } // namespace Integration