Dali 3D User Interface Engine
touch-event-combiner.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17 
18 // CLASS HEADER
20 
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23 #include <cmath> // abs<float>
24 
25 // INTERNAL INCLUDES
29 
30 namespace Dali
31 {
32 
33 namespace Integration
34 {
35 
36 namespace
37 {
38 const unsigned long DEFAULT_MINIMUM_MOTION_TIME( 1u );
39 const Vector2 DEFAULT_MINIMUM_MOTION_DISTANCE( 1.0f, 1.0f );
40 } // unnamed namespace
41 
43 {
44  // Construction
45 
51  PointInfo( const TouchPoint& touchPoint, unsigned long pointTime )
52  : point( touchPoint ),
53  time( pointTime )
54  {
55  }
56 
57  // Data
58 
60  unsigned long time;
61 };
62 
63 TouchEventCombiner::TouchEventCombiner()
64 : mMinMotionTime( DEFAULT_MINIMUM_MOTION_TIME ),
65  mMinMotionDistance( DEFAULT_MINIMUM_MOTION_DISTANCE )
66 {
67 }
68 
69 TouchEventCombiner::TouchEventCombiner( unsigned long minMotionTime, float minMotionXDistance, float minMotionYDistance )
70 : mMinMotionTime( minMotionTime ),
71  mMinMotionDistance( minMotionXDistance, minMotionYDistance )
72 {
73  DALI_ASSERT_ALWAYS( minMotionXDistance >= 0.0f && minMotionYDistance >= 0.0f && "Negative values not allowed\n" );
74 }
75 
76 TouchEventCombiner::TouchEventCombiner( unsigned long minMotionTime, Vector2 minMotionDistance )
77 : mMinMotionTime( minMotionTime ),
78  mMinMotionDistance( minMotionDistance )
79 {
80  DALI_ASSERT_ALWAYS( minMotionDistance.x >= 0.0f && minMotionDistance.y >= 0.0f && "Negative values not allowed\n" );
81 }
82 
83 TouchEventCombiner::~TouchEventCombiner()
84 {
85 }
86 
87 TouchEventCombiner::EventDispatchType TouchEventCombiner::GetNextTouchEvent( const TouchPoint& point, unsigned long time, TouchEvent& touchEvent, HoverEvent& hoverEvent )
88 {
89  TouchEventCombiner::EventDispatchType dispatchEvent( TouchEventCombiner::DispatchNone );
90 
91  switch ( point.state )
92  {
94  {
95  touchEvent.time = time;
96  bool addToContainer( true );
97 
98  // Iterate through already stored touch points and add to TouchEvent
99  for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
100  {
101  if ( iter->point.deviceId != point.deviceId )
102  {
103  iter->point.state = TouchPoint::Stationary;
104  }
105  else
106  {
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.
109 
110  addToContainer = false;
111  iter->point = point;
112  iter->time = time;
113  }
114  touchEvent.AddPoint( iter->point );
115  }
116 
117  // Add new touch point to the list and to the TouchEvent
118  if (addToContainer)
119  {
120  mPressedPoints.push_back( PointInfo( point, time ) );
121  touchEvent.AddPoint( point );
122  dispatchEvent = TouchEventCombiner::DispatchTouch; // Only dispatch touch event if just added to container
123 
124  // Check whether hover event was dispatched previously
125  if ( !mHoveredPoints.empty() )
126  {
127  hoverEvent.time = time;
128 
129  PointInfoContainer::iterator match( mHoveredPoints.end() );
130  for ( PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter )
131  {
132  if ( point.deviceId == iter->point.deviceId )
133  {
134  match = iter;
135  // Add new point to the HoverEvent
136  iter->point.state = TouchPoint::Finished;
137  hoverEvent.AddPoint( iter->point );
138  }
139  else
140  {
141  iter->point.state = TouchPoint::Stationary;
142  hoverEvent.AddPoint( iter->point );
143  }
144  }
145 
146  if ( match != mHoveredPoints.end() )
147  {
148  mHoveredPoints.erase( match );
149  dispatchEvent = TouchEventCombiner::DispatchBoth; // We should only dispatch hover events if the point was actually hovered in this window
150  }
151  }
152  }
153 
154  break;
155  }
156 
158  {
159  touchEvent.time = time;
160 
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 )
164  {
165  if ( point.deviceId == iter->point.deviceId )
166  {
167  match = iter;
168 
169  // Add new point to the TouchEvent
170  touchEvent.AddPoint( point );
171  }
172  else
173  {
174  iter->point.state = TouchPoint::Stationary;
175  touchEvent.AddPoint( iter->point );
176  }
177  }
178 
179  if ( match != mPressedPoints.end() )
180  {
181  mPressedPoints.erase( match );
182  dispatchEvent = TouchEventCombiner::DispatchTouch; // We should only dispatch touch events if the point was actually pressed in this window
183 
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 )
186  {
187  if ( iter->point.deviceId == point.deviceId )
188  {
189  iter = mHoveredPoints.erase( iter );
190  }
191  }
192  }
193  break;
194  }
195 
196  case TouchPoint::Motion:
197  {
198  bool fromNewDeviceId = false;
199 
200  if ( !mPressedPoints.empty() )
201  {
202  touchEvent.time = time;
203 
204  bool ignore = false;
205  PointInfoContainer::iterator match = mPressedPoints.end();
206  for ( PointInfoContainer::iterator iter = mPressedPoints.begin(), endIter = mPressedPoints.end(); iter != endIter; ++iter )
207  {
208  if ( point.deviceId == iter->point.deviceId )
209  {
210  unsigned long timeDiff( time - iter->time );
211 
212  if ( timeDiff < mMinMotionTime )
213  {
214  // Motion event sent too soon after previous event so ignore
215  ignore = true;
216  break;
217  }
218 
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 ) )
221  {
222  // Not enough positional change from last event so ignore
223  ignore = true;
224  break;
225  }
226 
227  match = iter;
228 
229  // Add new touch point to the TouchEvent
230  touchEvent.AddPoint( point );
231  }
232  else
233  {
234  iter->point.state = TouchPoint::Stationary;
235  touchEvent.AddPoint( iter->point );
236  }
237  }
238 
239  if ( match != mPressedPoints.end() )
240  {
241  PointInfo matchedPoint( point, time );
242  std::swap( *match, matchedPoint );
243 
244  dispatchEvent = TouchEventCombiner::DispatchTouch; // Dispatch touch event
245  }
246  else if(!ignore)
247  {
248  fromNewDeviceId = true;
249  }
250  }
251 
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)
254  {
255  hoverEvent.time = time;
256 
257  // Iterate through already stored touch points and add to HoverEvent
258  bool ignore = false;
259  PointInfoContainer::iterator match = mHoveredPoints.end();
260  for ( PointInfoContainer::iterator iter = mHoveredPoints.begin(), endIter = mHoveredPoints.end(); iter != endIter; ++iter )
261  {
262  if ( iter->point.deviceId == point.deviceId )
263  {
264  unsigned long timeDiff( time - iter->time );
265 
266  if ( timeDiff < mMinMotionTime )
267  {
268  // Motion event sent too soon after previous event so ignore
269  ignore = true;
270  break;
271  }
272 
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 ) )
275  {
276  // Not enough positional change from last event so ignore
277  ignore = true;
278  break;
279  }
280 
281  match = iter;
282 
283  // Add new touch point to the HoverEvent
284  hoverEvent.AddPoint( point );
285  }
286  else
287  {
288  iter->point.state = TouchPoint::Stationary;
289  hoverEvent.AddPoint( iter->point );
290  }
291  }
292 
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
295  {
296  if( match == mHoveredPoints.end() )
297  {
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 );
302  }
303  else
304  {
305  PointInfo matchedPoint( point, time );
306  std::swap( *match, matchedPoint );
307  }
308 
309  if(dispatchEvent == TouchEventCombiner::DispatchTouch)
310  {
311  dispatchEvent = TouchEventCombiner::DispatchBoth;
312  }
313  else
314  {
315  dispatchEvent = TouchEventCombiner::DispatchHover;
316  }
317  }
318  }
319  break;
320  }
321 
323  {
324  Reset();
325 
326  // We should still tell core about the interruption.
327  touchEvent.AddPoint( point );
328  hoverEvent.AddPoint( point );
329  dispatchEvent = TouchEventCombiner::DispatchBoth;
330  break;
331  }
332 
333  default:
334  break;
335  }
336 
337  return dispatchEvent;
338 }
339 
340 void TouchEventCombiner::SetMinimumMotionTimeThreshold( unsigned long minTime )
341 {
342  mMinMotionTime = minTime;
343 }
344 
345 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( float minDistance )
346 {
347  DALI_ASSERT_ALWAYS( minDistance >= 0.0f && "Negative values not allowed\n" );
348 
349  mMinMotionDistance.x = mMinMotionDistance.y = minDistance;
350 }
351 
352 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( float minXDistance, float minYDistance )
353 {
354  DALI_ASSERT_ALWAYS( minXDistance >= 0.0f && minYDistance >= 0.0f && "Negative values not allowed\n" );
355 
356  mMinMotionDistance.x = minXDistance;
357  mMinMotionDistance.y = minYDistance;
358 }
359 
360 void TouchEventCombiner::SetMinimumMotionDistanceThreshold( Vector2 minDistance )
361 {
362  DALI_ASSERT_ALWAYS( minDistance.x >= 0.0f && minDistance.y >= 0.0f && "Negative values not allowed\n" );
363 
364  mMinMotionDistance = minDistance;
365 }
366 
367 unsigned long TouchEventCombiner::GetMinimumMotionTimeThreshold() const
368 {
369  return mMinMotionTime;
370 }
371 
372 Vector2 TouchEventCombiner::GetMinimumMotionDistanceThreshold() const
373 {
374  return mMinMotionDistance;
375 }
376 
377 void TouchEventCombiner::Reset()
378 {
379  mPressedPoints.clear();
380  mHoveredPoints.clear();
381 }
382 
383 } // namespace Integration
384 
385 } // namespace Dali
Dali Docs Home
Read more about Dali