2 * Copyright (c) 2014 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/touch-event-integ.h>
27 #include <dali/integration-api/events/hover-event-integ.h>
28 #include <dali/public-api/common/dali-common.h>
38 const unsigned long DEFAULT_MINIMUM_MOTION_TIME( 1u );
39 const Vector2 DEFAULT_MINIMUM_MOTION_DISTANCE( 1.0f, 1.0f );
40 } // unnamed namespace
42 struct TouchEventCombiner::PointInfo
48 * @param[in] touchPoint The point to add.
49 * @param[in] pointTime The time of the point event.
51 PointInfo( const Point& touchPoint, unsigned long pointTime )
52 : point( touchPoint ),
59 Point point; ///< The point.
60 unsigned long time; ///< The time the point event took place.
63 TouchEventCombiner::TouchEventCombiner()
64 : mMinMotionTime( DEFAULT_MINIMUM_MOTION_TIME ),
65 mMinMotionDistance( DEFAULT_MINIMUM_MOTION_DISTANCE )
69 TouchEventCombiner::TouchEventCombiner( unsigned long minMotionTime, float minMotionXDistance, float minMotionYDistance )
70 : mMinMotionTime( minMotionTime ),
71 mMinMotionDistance( minMotionXDistance, minMotionYDistance )
73 DALI_ASSERT_ALWAYS( minMotionXDistance >= 0.0f && minMotionYDistance >= 0.0f && "Negative values not allowed\n" );
76 TouchEventCombiner::TouchEventCombiner( unsigned long minMotionTime, Vector2 minMotionDistance )
77 : mMinMotionTime( minMotionTime ),
78 mMinMotionDistance( minMotionDistance )
80 DALI_ASSERT_ALWAYS( minMotionDistance.x >= 0.0f && minMotionDistance.y >= 0.0f && "Negative values not allowed\n" );
83 TouchEventCombiner::~TouchEventCombiner()
87 TouchEventCombiner::EventDispatchType TouchEventCombiner::GetNextTouchEvent( const Point& point, unsigned long time, TouchEvent& touchEvent, HoverEvent& hoverEvent )
89 TouchEventCombiner::EventDispatchType dispatchEvent( TouchEventCombiner::DispatchNone );
90 const PointState::Type state = point.GetState();
91 const int deviceId = point.GetDeviceId();
95 case PointState::STARTED:
97 touchEvent.time = time;
98 bool addToContainer( true );
100 // Iterate through already stored touch points and add to TouchEvent
101 for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
103 if ( iter->point.GetDeviceId() != deviceId )
105 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::DispatchTouch; // 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::DispatchBoth; // 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 iter->point.SetState( PointState::STATIONARY );
177 touchEvent.AddPoint( iter->point );
181 if ( match != mPressedPoints.end() )
183 mPressedPoints.erase( match );
184 dispatchEvent = TouchEventCombiner::DispatchTouch; // We should only dispatch touch events if the point was actually pressed in this window
186 // Iterate through already stored touch points for HoverEvent and delete them
187 for ( PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter )
189 if ( iter->point.GetDeviceId() == deviceId )
191 iter = mHoveredPoints.erase( iter );
198 case PointState::MOTION:
200 bool fromNewDeviceId = false;
202 if ( !mPressedPoints.empty() )
204 touchEvent.time = time;
207 PointInfoContainer::iterator match = mPressedPoints.end();
208 const Vector2& pointScreenPosition = point.GetScreenPosition();
209 for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
211 if ( deviceId == iter->point.GetDeviceId() )
213 unsigned long timeDiff( time - iter->time );
215 if ( timeDiff < mMinMotionTime )
217 // Motion event sent too soon after previous event so ignore
222 const Vector2& currentScreenPosition = iter->point.GetScreenPosition();
223 if ( ( std::abs( pointScreenPosition.x - currentScreenPosition.x ) < mMinMotionDistance.x ) &&
224 ( std::abs( pointScreenPosition.y - currentScreenPosition.y ) < mMinMotionDistance.y ) )
226 // Not enough positional change from last event so ignore
233 // Add new touch point to the TouchEvent
234 touchEvent.AddPoint( point );
238 iter->point.SetState( PointState::STATIONARY );
239 touchEvent.AddPoint( iter->point );
243 if ( match != mPressedPoints.end() )
245 PointInfo matchedPoint( point, time );
246 std::swap( *match, matchedPoint );
248 dispatchEvent = TouchEventCombiner::DispatchTouch; // Dispatch touch event
252 fromNewDeviceId = true;
256 // Dispatch hover event if no previous down event received or the motion event comes from a new device ID
257 if(mPressedPoints.empty() || fromNewDeviceId)
259 hoverEvent.time = time;
261 // Iterate through already stored touch points and add to HoverEvent
263 PointInfoContainer::iterator match = mHoveredPoints.end();
264 const Vector2& pointScreenPosition = point.GetScreenPosition();
265 for ( PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter )
267 if ( iter->point.GetDeviceId() == deviceId )
269 unsigned long timeDiff( time - iter->time );
271 if ( timeDiff < mMinMotionTime )
273 // Motion event sent too soon after previous event so ignore
278 const Vector2& currentScreenPosition = iter->point.GetScreenPosition();
279 if ( ( std::abs( pointScreenPosition.x - currentScreenPosition.x ) < mMinMotionDistance.x ) &&
280 ( std::abs( pointScreenPosition.y - currentScreenPosition.y ) < mMinMotionDistance.y ) )
282 // Not enough positional change from last event so ignore
289 // Add new touch point to the HoverEvent
290 hoverEvent.AddPoint( point );
294 iter->point.SetState( PointState::STATIONARY );
295 hoverEvent.AddPoint( iter->point );
299 // Add new hover point to the list and to the HoverEvent
300 if ( !ignore ) // Only dispatch hover event when it should not be ignored
302 if( match == mHoveredPoints.end() )
304 Point hoverPoint(point);
305 hoverPoint.SetState( PointState::STARTED ); // The first hover event received
306 mHoveredPoints.push_back( PointInfo( hoverPoint, time ) );
307 hoverEvent.AddPoint( hoverPoint );
311 PointInfo matchedPoint( point, time );
312 std::swap( *match, matchedPoint );
315 if(dispatchEvent == TouchEventCombiner::DispatchTouch)
317 dispatchEvent = TouchEventCombiner::DispatchBoth;
321 dispatchEvent = TouchEventCombiner::DispatchHover;
328 case PointState::INTERRUPTED:
332 // We should still tell core about the interruption.
333 touchEvent.AddPoint( point );
334 hoverEvent.AddPoint( point );
335 dispatchEvent = TouchEventCombiner::DispatchBoth;
343 return dispatchEvent;
346 void TouchEventCombiner::SetMinimumMotionTimeThreshold( unsigned long minTime )
348 mMinMotionTime = minTime;
351 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( float minDistance )
353 DALI_ASSERT_ALWAYS( minDistance >= 0.0f && "Negative values not allowed\n" );
355 mMinMotionDistance.x = mMinMotionDistance.y = minDistance;
358 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( float minXDistance, float minYDistance )
360 DALI_ASSERT_ALWAYS( minXDistance >= 0.0f && minYDistance >= 0.0f && "Negative values not allowed\n" );
362 mMinMotionDistance.x = minXDistance;
363 mMinMotionDistance.y = minYDistance;
366 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( Vector2 minDistance )
368 DALI_ASSERT_ALWAYS( minDistance.x >= 0.0f && minDistance.y >= 0.0f && "Negative values not allowed\n" );
370 mMinMotionDistance = minDistance;
373 unsigned long TouchEventCombiner::GetMinimumMotionTimeThreshold() const
375 return mMinMotionTime;
378 Vector2 TouchEventCombiner::GetMinimumMotionDistanceThreshold() const
380 return mMinMotionDistance;
383 void TouchEventCombiner::Reset()
385 mPressedPoints.clear();
386 mHoveredPoints.clear();
389 } // namespace Integration