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 TouchPoint& touchPoint, unsigned long pointTime )
52 : point( touchPoint ),
59 TouchPoint 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 TouchPoint& point, unsigned long time, TouchEvent& touchEvent, HoverEvent& hoverEvent )
89 TouchEventCombiner::EventDispatchType dispatchEvent( TouchEventCombiner::DispatchNone );
91 switch ( point.state )
93 case TouchPoint::Started:
95 touchEvent.time = time;
96 bool addToContainer( true );
98 // Iterate through already stored touch points and add to TouchEvent
99 for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
101 if ( iter->point.deviceId != point.deviceId )
103 iter->point.state = TouchPoint::Stationary;
107 // System has sent us two down points for the same point ID, update our stored data to latest.
108 // We do not want to emit another down event for this Point Device ID.
110 addToContainer = false;
114 touchEvent.AddPoint( iter->point );
117 // Add new touch point to the list and to the TouchEvent
120 mPressedPoints.push_back( PointInfo( point, time ) );
121 touchEvent.AddPoint( point );
122 dispatchEvent = TouchEventCombiner::DispatchTouch; // Only dispatch touch event if just added to container
124 // Check whether hover event was dispatched previously
125 if ( !mHoveredPoints.empty() )
127 hoverEvent.time = time;
129 PointInfoContainer::iterator match( mHoveredPoints.end() );
130 for ( PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter )
132 if ( point.deviceId == iter->point.deviceId )
135 // Add new point to the HoverEvent
136 iter->point.state = TouchPoint::Finished;
137 hoverEvent.AddPoint( iter->point );
141 iter->point.state = TouchPoint::Stationary;
142 hoverEvent.AddPoint( iter->point );
146 if ( match != mHoveredPoints.end() )
148 mHoveredPoints.erase( match );
149 dispatchEvent = TouchEventCombiner::DispatchBoth; // We should only dispatch hover events if the point was actually hovered in this window
157 case TouchPoint::Finished:
159 touchEvent.time = time;
161 // Find pressed touch point in local list (while also adding the stored points to the touchEvent)
162 PointInfoContainer::iterator match( mPressedPoints.end() );
163 for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
165 if ( point.deviceId == iter->point.deviceId )
169 // Add new point to the TouchEvent
170 touchEvent.AddPoint( point );
174 iter->point.state = TouchPoint::Stationary;
175 touchEvent.AddPoint( iter->point );
179 if ( match != mPressedPoints.end() )
181 mPressedPoints.erase( match );
182 dispatchEvent = TouchEventCombiner::DispatchTouch; // We should only dispatch touch events if the point was actually pressed in this window
184 // Iterate through already stored touch points for HoverEvent and delete them
185 for ( PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter )
187 if ( iter->point.deviceId == point.deviceId )
189 iter = mHoveredPoints.erase( iter );
196 case TouchPoint::Motion:
198 bool fromNewDeviceId = false;
200 if ( !mPressedPoints.empty() )
202 touchEvent.time = time;
205 PointInfoContainer::iterator match = mPressedPoints.end();
206 for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
208 if ( point.deviceId == iter->point.deviceId )
210 unsigned long timeDiff( time - iter->time );
212 if ( timeDiff < mMinMotionTime )
214 // Motion event sent too soon after previous event so ignore
219 if ( ( std::abs( point.screen.x - iter->point.screen.x ) < mMinMotionDistance.x ) &&
220 ( std::abs( point.screen.y - iter->point.screen.y ) < mMinMotionDistance.y ) )
222 // Not enough positional change from last event so ignore
229 // Add new touch point to the TouchEvent
230 touchEvent.AddPoint( point );
234 iter->point.state = TouchPoint::Stationary;
235 touchEvent.AddPoint( iter->point );
239 if ( match != mPressedPoints.end() )
241 PointInfo matchedPoint( point, time );
242 std::swap( *match, matchedPoint );
244 dispatchEvent = TouchEventCombiner::DispatchTouch; // Dispatch touch event
248 fromNewDeviceId = true;
252 // Dispatch hover event if no previous down event received or the motion event comes from a new device ID
253 if(mPressedPoints.empty() || fromNewDeviceId)
255 hoverEvent.time = time;
257 // Iterate through already stored touch points and add to HoverEvent
259 PointInfoContainer::iterator match = mHoveredPoints.end();
260 for ( PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter )
262 if ( iter->point.deviceId == point.deviceId )
264 unsigned long timeDiff( time - iter->time );
266 if ( timeDiff < mMinMotionTime )
268 // Motion event sent too soon after previous event so ignore
273 if ( ( std::abs( point.screen.x - iter->point.screen.x ) < mMinMotionDistance.x ) &&
274 ( std::abs( point.screen.y - iter->point.screen.y ) < mMinMotionDistance.y ) )
276 // Not enough positional change from last event so ignore
283 // Add new touch point to the HoverEvent
284 hoverEvent.AddPoint( point );
288 iter->point.state = TouchPoint::Stationary;
289 hoverEvent.AddPoint( iter->point );
293 // Add new hover point to the list and to the HoverEvent
294 if ( !ignore ) // Only dispatch hover event when it should not be ignored
296 if( match == mHoveredPoints.end() )
298 TouchPoint hoverPoint(point);
299 hoverPoint.state = TouchPoint::Started; // The first hover event received
300 mHoveredPoints.push_back( PointInfo( hoverPoint, time ) );
301 hoverEvent.AddPoint( hoverPoint );
305 PointInfo matchedPoint( point, time );
306 std::swap( *match, matchedPoint );
309 if(dispatchEvent == TouchEventCombiner::DispatchTouch)
311 dispatchEvent = TouchEventCombiner::DispatchBoth;
315 dispatchEvent = TouchEventCombiner::DispatchHover;
322 case TouchPoint::Interrupted:
326 // We should still tell core about the interruption.
327 touchEvent.AddPoint( point );
328 hoverEvent.AddPoint( point );
329 dispatchEvent = TouchEventCombiner::DispatchBoth;
337 return dispatchEvent;
340 void TouchEventCombiner::SetMinimumMotionTimeThreshold( unsigned long minTime )
342 mMinMotionTime = minTime;
345 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( float minDistance )
347 DALI_ASSERT_ALWAYS( minDistance >= 0.0f && "Negative values not allowed\n" );
349 mMinMotionDistance.x = mMinMotionDistance.y = minDistance;
352 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( float minXDistance, float minYDistance )
354 DALI_ASSERT_ALWAYS( minXDistance >= 0.0f && minYDistance >= 0.0f && "Negative values not allowed\n" );
356 mMinMotionDistance.x = minXDistance;
357 mMinMotionDistance.y = minYDistance;
360 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( Vector2 minDistance )
362 DALI_ASSERT_ALWAYS( minDistance.x >= 0.0f && minDistance.y >= 0.0f && "Negative values not allowed\n" );
364 mMinMotionDistance = minDistance;
367 unsigned long TouchEventCombiner::GetMinimumMotionTimeThreshold() const
369 return mMinMotionTime;
372 Vector2 TouchEventCombiner::GetMinimumMotionDistanceThreshold() const
374 return mMinMotionDistance;
377 void TouchEventCombiner::Reset()
379 mPressedPoints.clear();
380 mHoveredPoints.clear();
383 } // namespace Integration