License conversion from Flora to Apache 2.0
[platform/core/uifw/dali-core.git] / dali / internal / event / events / pan-gesture-processor.cpp
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
19 #include <dali/internal/event/events/pan-gesture-processor.h>
20
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/actors/actor.h>
26 #include <dali/public-api/common/dali-common.h>
27 #include <dali/public-api/events/pan-gesture.h>
28 #include <dali/public-api/math/vector2.h>
29 #include <dali/integration-api/events/pan-gesture-event.h>
30 #include <dali/integration-api/gesture-manager.h>
31 #include <dali/integration-api/debug.h>
32 #include <dali/internal/event/common/stage-impl.h>
33 #include <dali/internal/event/render-tasks/render-task-impl.h>
34 #include <dali/internal/update/gestures/scene-graph-pan-gesture.h>
35
36 namespace Dali
37 {
38
39 namespace Internal
40 {
41
42 namespace // unnamed namespace
43 {
44
45 const unsigned long MAXIMUM_TIME_WITH_VALID_LAST_VELOCITY( 50u );
46
47 /**
48  * Functor which checks whether the specified actor is attached to the gesture detector.
49  * If the actor is attached, it also checks whether the number of touches of the current pan event
50  * are within the range of that expected by the detector.
51  * It returns true if it is no longer attached or the touches are out of range.
52  * This can be used in remove_if functions.
53  */
54 struct IsNotAttachedAndOutsideTouchesRangeFunctor
55 {
56   /**
57    * Constructor
58    * @param[in]  actor                 The actor to check whether it is attached.
59    * @param[in]  touches               The number of touches in the current pan event.
60    * @param[in]  outsideRangeEmitters  Reference to container where emitters outside of the touches range should be added.
61    */
62   IsNotAttachedAndOutsideTouchesRangeFunctor(Actor* actor, unsigned int touches, PanGestureDetectorContainer& outsideRangeEmitters)
63   : actorToCheck(actor),
64     numberOfTouches(touches),
65     outsideTouchesRangeEmitters(outsideRangeEmitters)
66   {
67   }
68
69   /**
70    * Returns true if not attached, false if it is still attached.
71    * Additionally, checks if the number of touches has changed and stops sending the pan to a particular
72    * detector if it exceeds the range of that detector.
73    * @param[in]  detector  The detector to check.
74    * @return true, if not attached, false otherwise.
75    */
76   bool operator()(PanGestureDetector* detector) const
77   {
78     bool remove(!detector->IsAttached(*actorToCheck));
79
80     if (!remove)
81     {
82       // Ensure number of touch points is within the range of our emitter. If it isn't then remove
83       // this emitter and add it to the outsideTouchesRangeEmitters container
84       if ( (numberOfTouches < detector->GetMinimumTouchesRequired()) ||
85            (numberOfTouches > detector->GetMaximumTouchesRequired()) )
86       {
87         remove = true;
88         outsideTouchesRangeEmitters.push_back(detector);
89       }
90     }
91
92     return remove;
93   }
94
95   Actor* actorToCheck; ///< The actor to check whether it is attached or not.
96   unsigned int numberOfTouches; ///< The number of touches in the pan event.
97   PanGestureDetectorContainer& outsideTouchesRangeEmitters; ///< Emitters that are outside of the range of current pan.
98 };
99
100 } // unnamed namespace
101
102 struct PanGestureProcessor::PanEventFunctor : public GestureProcessor::Functor
103 {
104   /**
105    * Constructor
106    * @param[in]  panEvent   The current gesture event.
107    * @param[in]  processor  Reference to the processor.
108    */
109   PanEventFunctor( const Integration::PanGestureEvent& panEvent, PanGestureProcessor& processor )
110   : panEvent( panEvent ),
111     processor( processor )
112   {
113   }
114
115   /**
116    * Check if the detector meets the current gesture event parameters.
117    */
118   virtual bool operator() ( GestureDetector* detector, Actor* actor )
119   {
120     bool retVal( false );
121
122     PanGestureDetector* panDetector( static_cast< PanGestureDetector* >( detector ) );
123
124     if ( ( panEvent.numberOfTouches >= panDetector->GetMinimumTouchesRequired() ) &&
125          ( panEvent.numberOfTouches <= panDetector->GetMaximumTouchesRequired() ) )
126     {
127       // Check if the detector requires directional panning.
128       if ( panDetector->RequiresDirectionalPan() && processor.mCurrentRenderTask )
129       {
130         // It does, calculate the angle of the pan in local actor coordinates and ensures it fits
131         // the detector's criteria.
132         RenderTask& renderTaskImpl( GetImplementation( processor.mCurrentRenderTask ) );
133
134         Vector2 startPosition, currentPosition;
135         actor->ScreenToLocal( renderTaskImpl, startPosition.x,   startPosition.y,   processor.mPossiblePanPosition.x, processor.mPossiblePanPosition.y );
136         actor->ScreenToLocal( renderTaskImpl, currentPosition.x, currentPosition.y, panEvent.currentPosition.x,       panEvent.currentPosition.y );
137         Vector2 displacement( currentPosition - startPosition );
138
139         Radian angle( atan( displacement.y / displacement.x ) );
140
141         /////////////////////////////
142         //            |            //
143         //            |            //
144         //   Q3 (-,-) | Q4 (+,-)   //
145         //            |            //
146         //    ----------------- +x //
147         //            |            //
148         //   Q2 (-,+) | Q1 (+,+)   //
149         //            |            //
150         //            |            //
151         //           +y            //
152         /////////////////////////////
153         // Quadrant 1: As is
154         // Quadrant 2: 180 degrees + angle
155         // Quadrant 3: angle - 180 degrees
156         // Quadrant 4: As is
157         /////////////////////////////
158
159         if ( displacement.x < 0.0f )
160         {
161           if ( displacement.y >= 0.0f )
162           {
163             // Quadrant 2
164             angle += Math::PI;
165           }
166           else
167           {
168             // Quadrant 3
169             angle -= Math::PI;
170           }
171         }
172
173         if ( panDetector->CheckAngleAllowed( angle ) )
174         {
175           retVal = true;
176         }
177       }
178       else
179       {
180         // Directional panning not required so we can use this actor and gesture detector.
181         retVal = true;
182       }
183     }
184
185     return retVal;
186   }
187
188   /**
189    * Gestured actor and gesture detectors that meet the gesture's parameters found, emit and save required information.
190    */
191   virtual void operator() ( Dali::Actor actor, const GestureDetectorContainer& gestureDetectors, Vector2 actorCoordinates )
192   {
193     PanGestureDetectorContainer derivedContainer;
194     DownCastContainer<PanGestureDetector>( gestureDetectors, derivedContainer );
195
196     processor.mCurrentPanEmitters.clear();
197     processor.ResetActor();
198
199     Actor* actorImpl( &GetImplementation( actor ) );
200     actorImpl->ScreenToLocal( GetImplementation(processor.mCurrentRenderTask), actorCoordinates.x, actorCoordinates.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
201
202     processor.EmitPanSignal( actor, derivedContainer, panEvent, actorCoordinates, panEvent.state, processor.mCurrentRenderTask );
203
204     if ( actorImpl->OnStage() )
205     {
206       processor.mCurrentPanEmitters = derivedContainer;
207       processor.SetActor( actor );
208     }
209   }
210
211   const Integration::PanGestureEvent& panEvent;
212   PanGestureProcessor& processor;
213 };
214
215 PanGestureProcessor::PanGestureProcessor( Stage& stage, Integration::GestureManager& gestureManager )
216 : mStage( stage ),
217   mGestureManager( gestureManager ),
218   mGestureDetectors(),
219   mCurrentPanEmitters(),
220   mCurrentRenderTask(),
221   mPossiblePanPosition(),
222   mMinTouchesRequired( 1 ),
223   mMaxTouchesRequired( 1 ),
224   mSceneObject( SceneGraph::PanGesture::New() ) // Create scene object to store pan information.
225 {
226   // Pass ownership to scene-graph
227   AddGestureMessage( mStage.GetUpdateManager(), mSceneObject );
228 }
229
230 PanGestureProcessor::~PanGestureProcessor()
231 {
232   if( Stage::IsInstalled() && ( mSceneObject != NULL ) )
233   {
234     RemoveGestureMessage( mStage.GetUpdateManager(), mSceneObject );
235     mSceneObject = NULL; // mSceneObject is about to be destroyed
236   }
237 }
238
239 void PanGestureProcessor::Process( const Integration::PanGestureEvent& panEvent )
240 {
241   switch( panEvent.state )
242   {
243     case Gesture::Possible:
244     {
245       mCurrentPanEmitters.clear();
246       ResetActor();
247
248       HitTestAlgorithm::Results hitTestResults;
249       if( HitTest( mStage, panEvent.currentPosition, hitTestResults ) )
250       {
251         SetActor( hitTestResults.actor );
252         mPossiblePanPosition = panEvent.currentPosition;
253       }
254
255       break;
256     }
257
258     case Gesture::Started:
259     {
260       if ( GetCurrentGesturedActor() )
261       {
262         // The pan gesture should only be sent to the gesture detector which first received it so that
263         // it can be told when the gesture ends as well.
264
265         HitTestAlgorithm::Results hitTestResults;
266         HitTestAlgorithm::HitTest( mStage, mPossiblePanPosition, hitTestResults ); // Hit test original possible position...
267
268         if ( hitTestResults.actor && ( GetCurrentGesturedActor() == &GetImplementation( hitTestResults.actor ) ) )
269         {
270           // Record the current render-task for Screen->Actor coordinate conversions
271           mCurrentRenderTask = hitTestResults.renderTask;
272
273           PanEventFunctor functor( panEvent, *this );
274           GestureDetectorContainer gestureDetectors;
275           UpCastContainer<PanGestureDetector>( mGestureDetectors, gestureDetectors );
276           ProcessAndEmit( hitTestResults, gestureDetectors, functor );
277         }
278         else
279         {
280           ResetActor();
281           mCurrentPanEmitters.clear();
282         }
283       }
284       break;
285     }
286
287     case Gesture::Continuing:
288     case Gesture::Finished:
289     case Gesture::Cancelled:
290     {
291       // Only send subsequent pan gesture signals if we processed the pan gesture when it started.
292       // Check if actor is still touchable.
293
294       Actor* currentGesturedActor = GetCurrentGesturedActor();
295       if ( currentGesturedActor )
296       {
297         if ( currentGesturedActor->IsHittable() && !mCurrentPanEmitters.empty() && mCurrentRenderTask )
298         {
299           PanGestureDetectorContainer outsideTouchesRangeEmitters;
300
301           // Removes emitters that no longer have the actor attached
302           // Also remove emitters whose touches are outside the range of the current pan event and add them to outsideTouchesRangeEmitters
303           PanGestureDetectorContainer::iterator endIter = std::remove_if( mCurrentPanEmitters.begin(), mCurrentPanEmitters.end(),
304                                                                           IsNotAttachedAndOutsideTouchesRangeFunctor(currentGesturedActor, panEvent.numberOfTouches, outsideTouchesRangeEmitters) );
305           mCurrentPanEmitters.erase( endIter, mCurrentPanEmitters.end() );
306
307           Vector2 actorCoords;
308
309           if ( !outsideTouchesRangeEmitters.empty() || !mCurrentPanEmitters.empty() )
310           {
311             currentGesturedActor->ScreenToLocal( GetImplementation( mCurrentRenderTask ), actorCoords.x, actorCoords.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
312
313             // EmitPanSignal checks whether we have a valid actor and whether the container we are passing in has emitters before it emits the pan.
314             EmitPanSignal(Dali::Actor(currentGesturedActor), outsideTouchesRangeEmitters, panEvent, actorCoords, Gesture::Finished, mCurrentRenderTask);
315             EmitPanSignal(Dali::Actor(currentGesturedActor), mCurrentPanEmitters, panEvent, actorCoords, panEvent.state, mCurrentRenderTask);
316           }
317
318           if ( mCurrentPanEmitters.empty() )
319           {
320             // If we have no emitters attached then clear pan actor as well.
321             ResetActor();
322           }
323
324           // Clear current gesture detectors if pan gesture has ended or been cancelled.
325           if ( ( panEvent.state == Gesture::Finished ) || ( panEvent.state == Gesture::Cancelled ) )
326           {
327             mCurrentPanEmitters.clear();
328             ResetActor();
329           }
330         }
331         else
332         {
333           mCurrentPanEmitters.clear();
334           ResetActor();
335         }
336       }
337       break;
338     }
339
340     case Gesture::Clear:
341       DALI_ASSERT_ALWAYS( false && "Incorrect state received from Integration layer: Clear\n" );
342       break;
343   }
344 }
345
346 void PanGestureProcessor::AddGestureDetector( PanGestureDetector* gestureDetector )
347 {
348   bool firstRegistration(mGestureDetectors.empty());
349
350   mGestureDetectors.push_back(gestureDetector);
351
352   // Set the pan scene object on the gesture detector
353   gestureDetector->SetSceneObject( mSceneObject );
354
355   if (firstRegistration)
356   {
357     mMinTouchesRequired = gestureDetector->GetMinimumTouchesRequired();
358     mMaxTouchesRequired = gestureDetector->GetMaximumTouchesRequired();
359
360     Integration::PanGestureRequest request;
361     request.minTouches = mMinTouchesRequired;
362     request.maxTouches = mMaxTouchesRequired;
363     mGestureManager.Register(request);
364   }
365   else
366   {
367     UpdateDetection();
368   }
369 }
370
371 void PanGestureProcessor::RemoveGestureDetector( PanGestureDetector* gestureDetector )
372 {
373   if (!mCurrentPanEmitters.empty())
374   {
375     // Check if the removed detector was one that is currently being panned and remove it from emitters.
376     PanGestureDetectorContainer::iterator endIter = std::remove( mCurrentPanEmitters.begin(), mCurrentPanEmitters.end(), gestureDetector );
377     mCurrentPanEmitters.erase( endIter, mCurrentPanEmitters.end() );
378
379     // If we no longer have any emitters, then we should clear mCurrentGesturedActor as well
380     if ( mCurrentPanEmitters.empty() )
381     {
382       ResetActor();
383     }
384   }
385
386   // Find the detector...
387   PanGestureDetectorContainer::iterator endIter = std::remove( mGestureDetectors.begin(), mGestureDetectors.end(), gestureDetector );
388   DALI_ASSERT_DEBUG( endIter != mGestureDetectors.end() );
389
390   // ...and remove it
391   mGestureDetectors.erase(endIter, mGestureDetectors.end());
392
393   if (mGestureDetectors.empty())
394   {
395     Integration::GestureRequest request(Gesture::Pan);
396     mGestureManager.Unregister(request);
397   }
398   else
399   {
400     UpdateDetection();
401   }
402 }
403
404 void PanGestureProcessor::GestureDetectorUpdated( PanGestureDetector* gestureDetector )
405 {
406   DALI_ASSERT_DEBUG(find(mGestureDetectors.begin(), mGestureDetectors.end(), gestureDetector) != mGestureDetectors.end());
407
408   UpdateDetection();
409 }
410
411 void PanGestureProcessor::SetPanGestureProperties( const PanGesture& pan )
412 {
413   // If we are currently processing a pan gesture then just ignore
414   if ( mCurrentPanEmitters.empty() && mSceneObject )
415   {
416     // We update the scene object directly rather than sending a message.
417     // Sending a message could cause unnecessary delays, the scene object ensure thread safe behaviour.
418     mSceneObject->AddGesture( pan );
419   }
420 }
421
422 void PanGestureProcessor::EnableProfiling()
423 {
424   mSceneObject->EnableProfiling();
425 }
426
427 void PanGestureProcessor::SetPredictionMode(int mode)
428 {
429   if( (mode < 0)
430       || (mode >= SceneGraph::PanGesture::NUM_PREDICTION_MODES) )
431   {
432     mode = SceneGraph::PanGesture::DEFAULT_PREDICTION_MODE;
433   }
434   SceneGraph::PanGesture::PredictionMode modeEnum = static_cast<SceneGraph::PanGesture::PredictionMode>(mode);
435   mSceneObject->SetPredictionMode(modeEnum);
436 }
437
438 void PanGestureProcessor::UpdateDetection()
439 {
440   DALI_ASSERT_DEBUG(!mGestureDetectors.empty());
441
442   unsigned int minimumRequired = UINT_MAX;
443   unsigned int maximumRequired = 0;
444
445   for ( PanGestureDetectorContainer::iterator iter = mGestureDetectors.begin(), endIter = mGestureDetectors.end(); iter != endIter; ++iter )
446   {
447     PanGestureDetector* detector(*iter);
448
449     unsigned int minimum = detector->GetMinimumTouchesRequired();
450     if (minimum < minimumRequired)
451     {
452       minimumRequired = minimum;
453     }
454
455     unsigned int maximum = detector->GetMaximumTouchesRequired();
456     if (maximum > maximumRequired)
457     {
458       maximumRequired = maximum;
459     }
460   }
461
462   if ( (minimumRequired != mMinTouchesRequired)||(maximumRequired != mMaxTouchesRequired) )
463   {
464     mMinTouchesRequired = minimumRequired;
465     mMaxTouchesRequired = maximumRequired;
466
467     Integration::PanGestureRequest request;
468     request.minTouches = mMinTouchesRequired;
469     request.maxTouches = mMaxTouchesRequired;
470     mGestureManager.Update(request);
471   }
472 }
473
474 void PanGestureProcessor::EmitPanSignal( Dali::Actor actorHandle,
475                                          PanGestureDetectorContainer& gestureDetectors,
476                                          const Integration::PanGestureEvent& panEvent,
477                                          Vector2 localCurrent,
478                                          Gesture::State state,
479                                          Dali::RenderTask renderTask )
480 {
481   if ( actorHandle && !gestureDetectors.empty() )
482   {
483     Actor& actor = GetImplementation(actorHandle);
484
485     PanGesture pan(state);
486     pan.time = panEvent.time;
487
488     pan.numberOfTouches = panEvent.numberOfTouches;
489     pan.screenPosition = panEvent.currentPosition;
490     pan.position = localCurrent;
491
492     RenderTask& renderTaskImpl( GetImplementation( renderTask ) );
493
494     Vector2 localPrevious;
495     actor.ScreenToLocal( renderTaskImpl, localPrevious.x, localPrevious.y, panEvent.previousPosition.x, panEvent.previousPosition.y );
496
497     pan.displacement = localCurrent - localPrevious;
498     Vector2 previousPos( panEvent.previousPosition );
499     if ( state == Gesture::Started )
500     {
501       previousPos = mPossiblePanPosition;
502     }
503
504     pan.screenDisplacement = panEvent.currentPosition - previousPos;
505
506     pan.velocity.x = pan.displacement.x / panEvent.timeDelta;
507     pan.velocity.y = pan.displacement.y / panEvent.timeDelta;
508
509     pan.screenVelocity.x = pan.screenDisplacement.x / panEvent.timeDelta;
510     pan.screenVelocity.y = pan.screenDisplacement.y / panEvent.timeDelta;
511
512     // When the gesture ends, we may incorrectly get a ZERO velocity (as we have lifted our finger without any movement)
513     // so we should use the last recorded velocity instead in this scenario.
514     if ( ( state == Gesture::Finished ) && ( pan.screenVelocity == Vector2::ZERO ) &&
515          ( panEvent.timeDelta < MAXIMUM_TIME_WITH_VALID_LAST_VELOCITY ) )
516     {
517       pan.velocity = mLastVelocity;
518       pan.screenVelocity = mLastScreenVelocity;
519     }
520     else
521     {
522       // Store the current velocity for future iterations.
523       mLastVelocity = pan.velocity;
524       mLastScreenVelocity = pan.screenVelocity;
525     }
526
527     if ( mSceneObject )
528     {
529       // We update the scene object directly rather than sending a message.
530       // Sending a message could cause unnecessary delays, the scene object ensure thread safe behaviour.
531       mSceneObject->AddGesture( pan );
532     }
533
534     for ( PanGestureDetectorContainer::iterator iter = gestureDetectors.begin(), endIter = gestureDetectors.end(); iter != endIter; ++iter )
535     {
536       (*iter)->EmitPanGestureSignal(actorHandle, pan);
537     }
538   }
539 }
540
541 void PanGestureProcessor::OnGesturedActorStageDisconnection()
542 {
543   mCurrentPanEmitters.clear();
544 }
545
546 } // namespace Internal
547
548 } // namespace Dali