Conversion to Apache 2.0 license
[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 : GestureProcessor( Gesture::Pan ),
217   mStage( stage ),
218   mGestureManager( gestureManager ),
219   mGestureDetectors(),
220   mCurrentPanEmitters(),
221   mCurrentRenderTask(),
222   mPossiblePanPosition(),
223   mMinTouchesRequired( 1 ),
224   mMaxTouchesRequired( 1 ),
225   mSceneObject( SceneGraph::PanGesture::New() ) // Create scene object to store pan information.
226 {
227   // Pass ownership to scene-graph
228   AddGestureMessage( mStage.GetUpdateManager(), mSceneObject );
229 }
230
231 PanGestureProcessor::~PanGestureProcessor()
232 {
233   if( Stage::IsInstalled() && ( mSceneObject != NULL ) )
234   {
235     RemoveGestureMessage( mStage.GetUpdateManager(), mSceneObject );
236     mSceneObject = NULL; // mSceneObject is about to be destroyed
237   }
238 }
239
240 void PanGestureProcessor::Process( const Integration::PanGestureEvent& panEvent )
241 {
242   switch( panEvent.state )
243   {
244     case Gesture::Possible:
245     {
246       mCurrentPanEmitters.clear();
247       ResetActor();
248
249       HitTestAlgorithm::Results hitTestResults;
250       if( HitTest( mStage, panEvent.currentPosition, hitTestResults ) )
251       {
252         SetActor( hitTestResults.actor );
253         mPossiblePanPosition = panEvent.currentPosition;
254       }
255
256       break;
257     }
258
259     case Gesture::Started:
260     {
261       if ( GetCurrentGesturedActor() )
262       {
263         // The pan gesture should only be sent to the gesture detector which first received it so that
264         // it can be told when the gesture ends as well.
265
266         HitTestAlgorithm::Results hitTestResults;
267         HitTest( mStage, mPossiblePanPosition, hitTestResults ); // Hit test original possible position...
268
269         if ( hitTestResults.actor && ( GetCurrentGesturedActor() == &GetImplementation( hitTestResults.actor ) ) )
270         {
271           // Record the current render-task for Screen->Actor coordinate conversions
272           mCurrentRenderTask = hitTestResults.renderTask;
273
274           PanEventFunctor functor( panEvent, *this );
275           GestureDetectorContainer gestureDetectors;
276           UpCastContainer<PanGestureDetector>( mGestureDetectors, gestureDetectors );
277           ProcessAndEmit( hitTestResults, gestureDetectors, functor );
278         }
279         else
280         {
281           ResetActor();
282           mCurrentPanEmitters.clear();
283         }
284       }
285       break;
286     }
287
288     case Gesture::Continuing:
289     case Gesture::Finished:
290     case Gesture::Cancelled:
291     {
292       // Only send subsequent pan gesture signals if we processed the pan gesture when it started.
293       // Check if actor is still touchable.
294
295       Actor* currentGesturedActor = GetCurrentGesturedActor();
296       if ( currentGesturedActor )
297       {
298         if ( currentGesturedActor->IsHittable() && !mCurrentPanEmitters.empty() && mCurrentRenderTask )
299         {
300           PanGestureDetectorContainer outsideTouchesRangeEmitters;
301
302           // Removes emitters that no longer have the actor attached
303           // Also remove emitters whose touches are outside the range of the current pan event and add them to outsideTouchesRangeEmitters
304           PanGestureDetectorContainer::iterator endIter = std::remove_if( mCurrentPanEmitters.begin(), mCurrentPanEmitters.end(),
305                                                                           IsNotAttachedAndOutsideTouchesRangeFunctor(currentGesturedActor, panEvent.numberOfTouches, outsideTouchesRangeEmitters) );
306           mCurrentPanEmitters.erase( endIter, mCurrentPanEmitters.end() );
307
308           Vector2 actorCoords;
309
310           if ( !outsideTouchesRangeEmitters.empty() || !mCurrentPanEmitters.empty() )
311           {
312             currentGesturedActor->ScreenToLocal( GetImplementation( mCurrentRenderTask ), actorCoords.x, actorCoords.y, panEvent.currentPosition.x, panEvent.currentPosition.y );
313
314             // EmitPanSignal checks whether we have a valid actor and whether the container we are passing in has emitters before it emits the pan.
315             EmitPanSignal(Dali::Actor(currentGesturedActor), outsideTouchesRangeEmitters, panEvent, actorCoords, Gesture::Finished, mCurrentRenderTask);
316             EmitPanSignal(Dali::Actor(currentGesturedActor), mCurrentPanEmitters, panEvent, actorCoords, panEvent.state, mCurrentRenderTask);
317           }
318
319           if ( mCurrentPanEmitters.empty() )
320           {
321             // If we have no emitters attached then clear pan actor as well.
322             ResetActor();
323           }
324
325           // Clear current gesture detectors if pan gesture has ended or been cancelled.
326           if ( ( panEvent.state == Gesture::Finished ) || ( panEvent.state == Gesture::Cancelled ) )
327           {
328             mCurrentPanEmitters.clear();
329             ResetActor();
330           }
331         }
332         else
333         {
334           mCurrentPanEmitters.clear();
335           ResetActor();
336         }
337       }
338       break;
339     }
340
341     case Gesture::Clear:
342       DALI_ASSERT_ALWAYS( false && "Incorrect state received from Integration layer: Clear\n" );
343       break;
344   }
345 }
346
347 void PanGestureProcessor::AddGestureDetector( PanGestureDetector* gestureDetector )
348 {
349   bool firstRegistration(mGestureDetectors.empty());
350
351   mGestureDetectors.push_back(gestureDetector);
352
353   // Set the pan scene object on the gesture detector
354   gestureDetector->SetSceneObject( mSceneObject );
355
356   if (firstRegistration)
357   {
358     mMinTouchesRequired = gestureDetector->GetMinimumTouchesRequired();
359     mMaxTouchesRequired = gestureDetector->GetMaximumTouchesRequired();
360
361     Integration::PanGestureRequest request;
362     request.minTouches = mMinTouchesRequired;
363     request.maxTouches = mMaxTouchesRequired;
364     mGestureManager.Register(request);
365   }
366   else
367   {
368     UpdateDetection();
369   }
370 }
371
372 void PanGestureProcessor::RemoveGestureDetector( PanGestureDetector* gestureDetector )
373 {
374   if (!mCurrentPanEmitters.empty())
375   {
376     // Check if the removed detector was one that is currently being panned and remove it from emitters.
377     PanGestureDetectorContainer::iterator endIter = std::remove( mCurrentPanEmitters.begin(), mCurrentPanEmitters.end(), gestureDetector );
378     mCurrentPanEmitters.erase( endIter, mCurrentPanEmitters.end() );
379
380     // If we no longer have any emitters, then we should clear mCurrentGesturedActor as well
381     if ( mCurrentPanEmitters.empty() )
382     {
383       ResetActor();
384     }
385   }
386
387   // Find the detector...
388   PanGestureDetectorContainer::iterator endIter = std::remove( mGestureDetectors.begin(), mGestureDetectors.end(), gestureDetector );
389   DALI_ASSERT_DEBUG( endIter != mGestureDetectors.end() );
390
391   // ...and remove it
392   mGestureDetectors.erase(endIter, mGestureDetectors.end());
393
394   if (mGestureDetectors.empty())
395   {
396     Integration::GestureRequest request(Gesture::Pan);
397     mGestureManager.Unregister(request);
398   }
399   else
400   {
401     UpdateDetection();
402   }
403 }
404
405 void PanGestureProcessor::GestureDetectorUpdated( PanGestureDetector* gestureDetector )
406 {
407   DALI_ASSERT_DEBUG(find(mGestureDetectors.begin(), mGestureDetectors.end(), gestureDetector) != mGestureDetectors.end());
408
409   UpdateDetection();
410 }
411
412 void PanGestureProcessor::SetPanGestureProperties( const PanGesture& pan )
413 {
414   // If we are currently processing a pan gesture then just ignore
415   if ( mCurrentPanEmitters.empty() && mSceneObject )
416   {
417     // We update the scene object directly rather than sending a message.
418     // Sending a message could cause unnecessary delays, the scene object ensure thread safe behaviour.
419     mSceneObject->AddGesture( pan );
420   }
421 }
422
423 void PanGestureProcessor::EnableProfiling()
424 {
425   mSceneObject->EnableProfiling();
426 }
427
428 void PanGestureProcessor::SetPredictionMode(int mode)
429 {
430   if( (mode < 0)
431       || (mode >= SceneGraph::PanGesture::NUM_PREDICTION_MODES) )
432   {
433     mode = SceneGraph::PanGesture::DEFAULT_PREDICTION_MODE;
434   }
435   SceneGraph::PanGesture::PredictionMode modeEnum = static_cast<SceneGraph::PanGesture::PredictionMode>(mode);
436   mSceneObject->SetPredictionMode(modeEnum);
437 }
438
439 void PanGestureProcessor::SetPredictionAmount(float amount)
440 {
441   mSceneObject->SetPredictionAmount(amount);
442 }
443
444 void PanGestureProcessor::UpdateDetection()
445 {
446   DALI_ASSERT_DEBUG(!mGestureDetectors.empty());
447
448   unsigned int minimumRequired = UINT_MAX;
449   unsigned int maximumRequired = 0;
450
451   for ( PanGestureDetectorContainer::iterator iter = mGestureDetectors.begin(), endIter = mGestureDetectors.end(); iter != endIter; ++iter )
452   {
453     PanGestureDetector* detector(*iter);
454
455     unsigned int minimum = detector->GetMinimumTouchesRequired();
456     if (minimum < minimumRequired)
457     {
458       minimumRequired = minimum;
459     }
460
461     unsigned int maximum = detector->GetMaximumTouchesRequired();
462     if (maximum > maximumRequired)
463     {
464       maximumRequired = maximum;
465     }
466   }
467
468   if ( (minimumRequired != mMinTouchesRequired)||(maximumRequired != mMaxTouchesRequired) )
469   {
470     mMinTouchesRequired = minimumRequired;
471     mMaxTouchesRequired = maximumRequired;
472
473     Integration::PanGestureRequest request;
474     request.minTouches = mMinTouchesRequired;
475     request.maxTouches = mMaxTouchesRequired;
476     mGestureManager.Update(request);
477   }
478 }
479
480 void PanGestureProcessor::EmitPanSignal( Dali::Actor actorHandle,
481                                          PanGestureDetectorContainer& gestureDetectors,
482                                          const Integration::PanGestureEvent& panEvent,
483                                          Vector2 localCurrent,
484                                          Gesture::State state,
485                                          Dali::RenderTask renderTask )
486 {
487   if ( actorHandle && !gestureDetectors.empty() )
488   {
489     Actor& actor = GetImplementation(actorHandle);
490
491     PanGesture pan(state);
492     pan.time = panEvent.time;
493
494     pan.numberOfTouches = panEvent.numberOfTouches;
495     pan.screenPosition = panEvent.currentPosition;
496     pan.position = localCurrent;
497
498     RenderTask& renderTaskImpl( GetImplementation( renderTask ) );
499
500     Vector2 localPrevious;
501     actor.ScreenToLocal( renderTaskImpl, localPrevious.x, localPrevious.y, panEvent.previousPosition.x, panEvent.previousPosition.y );
502
503     pan.displacement = localCurrent - localPrevious;
504     Vector2 previousPos( panEvent.previousPosition );
505     if ( state == Gesture::Started )
506     {
507       previousPos = mPossiblePanPosition;
508     }
509
510     pan.screenDisplacement = panEvent.currentPosition - previousPos;
511
512     pan.velocity.x = pan.displacement.x / panEvent.timeDelta;
513     pan.velocity.y = pan.displacement.y / panEvent.timeDelta;
514
515     pan.screenVelocity.x = pan.screenDisplacement.x / panEvent.timeDelta;
516     pan.screenVelocity.y = pan.screenDisplacement.y / panEvent.timeDelta;
517
518     // When the gesture ends, we may incorrectly get a ZERO velocity (as we have lifted our finger without any movement)
519     // so we should use the last recorded velocity instead in this scenario.
520     if ( ( state == Gesture::Finished ) && ( pan.screenVelocity == Vector2::ZERO ) &&
521          ( panEvent.timeDelta < MAXIMUM_TIME_WITH_VALID_LAST_VELOCITY ) )
522     {
523       pan.velocity = mLastVelocity;
524       pan.screenVelocity = mLastScreenVelocity;
525     }
526     else
527     {
528       // Store the current velocity for future iterations.
529       mLastVelocity = pan.velocity;
530       mLastScreenVelocity = pan.screenVelocity;
531     }
532
533     if ( mSceneObject )
534     {
535       // We update the scene object directly rather than sending a message.
536       // Sending a message could cause unnecessary delays, the scene object ensure thread safe behaviour.
537       mSceneObject->AddGesture( pan );
538     }
539
540     for ( PanGestureDetectorContainer::iterator iter = gestureDetectors.begin(), endIter = gestureDetectors.end(); iter != endIter; ++iter )
541     {
542       (*iter)->EmitPanGestureSignal(actorHandle, pan);
543     }
544   }
545 }
546
547 void PanGestureProcessor::OnGesturedActorStageDisconnection()
548 {
549   mCurrentPanEmitters.clear();
550 }
551
552 } // namespace Internal
553
554 } // namespace Dali