(Touch) Emit interrupted when actor is disconnected 20/36020/7
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 27 Feb 2015 11:23:48 +0000 (11:23 +0000)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Mon, 2 Mar 2015 09:23:04 +0000 (01:23 -0800)
Change-Id: I4c25b100ad908bdfb35021dc759e8d31559ff005

automated-tests/src/dali/utc-Dali-TouchProcessing.cpp
dali/internal/event/events/actor-observer.cpp
dali/internal/event/events/actor-observer.h
dali/internal/event/events/touch-event-processor.cpp
dali/internal/event/events/touch-event-processor.h

index 19327d6..9c72554 100644 (file)
@@ -485,6 +485,8 @@ int UtcDaliTouchInterruptedParentConsumer(void)
 
   // Remove actor from Stage
   Stage::GetCurrent().Remove( actor );
+  data.Reset();
+  rootData.Reset();
 
   // Render and notify
   application.SendNotification();
@@ -712,9 +714,6 @@ int UtcDaliTouchActorBecomesInsensitiveParentConsumer(void)
   data.Reset();
   rootData.Reset();
 
-  // Remove actor from Stage
-  Stage::GetCurrent().Remove( actor );
-
   // Render and notify
   application.SendNotification();
   application.Render();
@@ -724,7 +723,8 @@ int UtcDaliTouchActorBecomesInsensitiveParentConsumer(void)
 
   // Emit a motion signal, signalled with an interrupted (should get interrupted even if within root actor)
   application.ProcessEvent( GenerateSingleTouch( TouchPoint::Motion, Vector2 ( 200.0f, 200.0f )) );
-  DALI_TEST_EQUALS( false, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, data.touchEvent.points[0].state, TEST_LOCATION );
   DALI_TEST_EQUALS( true, rootData.functorCalled, TEST_LOCATION );
   DALI_TEST_EQUALS( TouchPoint::Interrupted, rootData.touchEvent.points[0].state, TEST_LOCATION );
   END_TEST;
@@ -1145,6 +1145,7 @@ int UtcDaliTouchActorUnStaged(void)
 
   // Remove actor from stage
   Stage::GetCurrent().Remove( actor );
+  data.Reset();
 
   // Render and notify
   application.SendNotification();
@@ -1501,3 +1502,248 @@ int UtcDaliTouchStencilNonRenderableActor(void)
 
   END_TEST;
 }
+
+int UtcDaliTouchActorUnstaged(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  actor.SetSize(100.0f, 100.0f);
+  actor.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  Stage::GetCurrent().Add(actor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Connect to actor's touched signal
+  SignalData data;
+  TouchEventFunctor functor( data );
+  actor.TouchedSignal().Connect( &application, functor );
+
+  // Emit a down signal
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Down, data.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_CHECK( actor == data.touchEvent.points[0].hitActor );
+  data.Reset();
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Unparent the actor
+  actor.Unparent();
+
+  // Should receive an interrupted event
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, data.touchEvent.points[0].state, TEST_LOCATION );
+  END_TEST;
+}
+
+int UtcDaliTouchParentUnstaged(void)
+{
+  TestApplication application;
+
+  Actor parent = Actor::New();
+  parent.SetSize(100.0f, 100.0f);
+  parent.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  Stage::GetCurrent().Add(parent);
+
+  Actor actor = Actor::New();
+  actor.SetSize(100.0f, 100.0f);
+  actor.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  parent.Add(actor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Connect to actor's touched signal
+  SignalData data;
+  TouchEventFunctor functor( data );
+  actor.TouchedSignal().Connect( &application, functor );
+
+  // Emit a down signal
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Down, data.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_CHECK( actor == data.touchEvent.points[0].hitActor );
+  data.Reset();
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Unparent the parent of the touchable actor
+  parent.Unparent();
+
+  // Should receive an interrupted event
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, data.touchEvent.points[0].state, TEST_LOCATION );
+  END_TEST;
+}
+
+int UtcDaliTouchActorUnstagedDifferentConsumer(void)
+{
+  TestApplication application;
+
+  Actor parent = Actor::New();
+  parent.SetSize(100.0f, 100.0f);
+  parent.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  Stage::GetCurrent().Add(parent);
+
+  Actor actor = Actor::New();
+  actor.SetSize(100.0f, 100.0f);
+  actor.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  parent.Add(actor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Connect to actor's touched signal
+  SignalData data;
+  TouchEventFunctor functor( data, false /* Do not consume */ );
+  actor.TouchedSignal().Connect( &application, functor );
+
+  // Connect to parent's touched signal
+  SignalData parentData;
+  TouchEventFunctor parentFunctor( parentData );
+  parent.TouchedSignal().Connect( &application, parentFunctor );
+
+  // Emit a down signal
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Down, data.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_CHECK( actor == data.touchEvent.points[0].hitActor );
+  DALI_TEST_CHECK( actor == data.touchedActor );
+  DALI_TEST_EQUALS( true, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Down, parentData.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_CHECK( actor == parentData.touchEvent.points[0].hitActor );
+  DALI_TEST_CHECK( parent == parentData.touchedActor );
+  data.Reset();
+  parentData.Reset();
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Unparent the actor
+  actor.Unparent();
+
+  // Should receive an interrupted event for both actor & parent
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, data.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_EQUALS( true, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, parentData.touchEvent.points[0].state, TEST_LOCATION );
+  data.Reset();
+  parentData.Reset();
+
+  // Readd actor to parent
+  parent.Add(actor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Emit a motion signal
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Motion, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( true, parentData.functorCalled, TEST_LOCATION );
+  data.Reset();
+  parentData.Reset();
+
+  // Parent is now consumer, connect again to the touched signal of the actor so that it becomes the consumer
+  SignalData secondData;
+  TouchEventFunctor secondFunctor( secondData /* Consume */ );
+  actor.TouchedSignal().Connect( &application, secondFunctor );
+
+  // Unparent the actor
+  actor.Unparent();
+
+  // Should receive an interrupted event for both actor functors & the parent as well as it was last consumer
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, data.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_EQUALS( true, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, parentData.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_EQUALS( true, secondData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, secondData.touchEvent.points[0].state, TEST_LOCATION );
+  data.Reset();
+  parentData.Reset();
+  secondData.Reset();
+
+  END_TEST;
+}
+
+int UtcDaliTouchInterruptedDifferentConsumer(void)
+{
+  TestApplication application;
+  Actor rootActor( Stage::GetCurrent().GetRootLayer() );
+
+  Actor parent = Actor::New();
+  parent.SetSize(100.0f, 100.0f);
+  parent.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  Stage::GetCurrent().Add(parent);
+
+  Actor actor = Actor::New();
+  actor.SetSize(100.0f, 100.0f);
+  actor.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  parent.Add(actor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Connect to actor's touched signal
+  SignalData data;
+  TouchEventFunctor functor( data, false /* Do not consume */ );
+  actor.TouchedSignal().Connect( &application, functor );
+
+  // Connect to parent's touched signal
+  SignalData parentData;
+  TouchEventFunctor parentFunctor( parentData, false /* Do not consume */ );
+  parent.TouchedSignal().Connect( &application, parentFunctor );
+
+  // Connect to root's touched signal and consume
+  SignalData rootData;
+  TouchEventFunctor rootFunctor( rootData );
+  rootActor.TouchedSignal().Connect( &application, rootFunctor );
+
+  // Emit a down signal
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Down, data.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_CHECK( actor == data.touchEvent.points[0].hitActor );
+  DALI_TEST_CHECK( actor == data.touchedActor );
+  DALI_TEST_EQUALS( true, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Down, parentData.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_CHECK( actor == parentData.touchEvent.points[0].hitActor );
+  DALI_TEST_CHECK( parent == parentData.touchedActor );
+  DALI_TEST_EQUALS( true, rootData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Down, rootData.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_CHECK( actor == rootData.touchEvent.points[0].hitActor );
+  DALI_TEST_CHECK( rootActor == rootData.touchedActor );
+  data.Reset();
+  parentData.Reset();
+  rootData.Reset();
+
+  // Root is now consumer, connect to the touched signal of the parent so that it becomes the consumer
+  SignalData secondData;
+  TouchEventFunctor secondFunctor( secondData /* Consume */ );
+  parent.TouchedSignal().Connect( &application, secondFunctor );
+
+  // Emit an interrupted signal, all three should STILL be called
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Interrupted, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, data.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_EQUALS( true, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, parentData.touchEvent.points[0].state, TEST_LOCATION );
+  DALI_TEST_EQUALS( true, rootData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( TouchPoint::Interrupted, rootData.touchEvent.points[0].state, TEST_LOCATION );
+  data.Reset();
+  parentData.Reset();
+  rootData.Reset();
+
+  END_TEST;
+}
index f345ca7..142783d 100644 (file)
@@ -35,15 +35,25 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_ACT
 
 ActorObserver::ActorObserver()
 : mActor ( NULL ),
-  mActorDisconnected(false)
+  mActorDisconnected( false ),
+  mRemoveCallback( NULL )
 {
   DALI_LOG_TRACE_METHOD( gLogFilter );
 }
 
+ActorObserver::ActorObserver( CallbackBase* callback )
+: mActor ( NULL ),
+  mActorDisconnected( false ),
+  mRemoveCallback( callback )
+{
+}
+
 ActorObserver::~ActorObserver()
 {
   DALI_LOG_TRACE_METHOD( gLogFilter );
   SetActor( NULL );
+
+  delete mRemoveCallback;
 }
 
 Actor* ActorObserver::GetActor()
@@ -89,6 +99,11 @@ void ActorObserver::SceneObjectRemoved( Object& object )
 
   if ( mActor == &object )
   {
+    if ( mRemoveCallback )
+    {
+      CallbackBase::Execute( *mRemoveCallback, mActor );
+    }
+
     // do not call object.RemoveObserver here, object is currently iterating through observers
     mActorDisconnected = true;
   }
index 3cc8b19..bb9e5aa 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 // INTERNAL INCLUDES
+#include <dali/public-api/signals/callback.h>
 #include <dali/internal/event/common/object-impl.h>
 
 namespace Dali
@@ -44,6 +45,21 @@ public:
   ActorObserver();
 
   /**
+   * Constructor with a callback which is called when the observed actor is removed from the scene.
+   *
+   * The callback should have the following signature:
+   * @code
+   * void MyCallback( Actor* actor );
+   * @endcode
+   * Where actor is a pointer to the object that has been removed from the scene.
+   *
+   * @param[in]  callback  The callback to connect to.
+   *
+   * @note Ownership of callback is passed onto this class.
+   */
+  ActorObserver( CallbackBase* callback );
+
+  /**
    * Non virtual destructor
    */
   ~ActorObserver();
@@ -99,8 +115,9 @@ private:
   virtual void ObjectDestroyed(Object& object);
 
 private:
-  Actor* mActor;              ///< Raw pointer to an Actor.
-  bool  mActorDisconnected;   ///< Indicates whether the actor has been disconnected from the scene
+  Actor* mActor;                 ///< Raw pointer to an Actor.
+  bool  mActorDisconnected;      ///< Indicates whether the actor has been disconnected from the scene
+  CallbackBase* mRemoveCallback; ///< Callback to call when the observed actor is removed from the scene
 };
 
 } // namespace Internal
index 8426d77..aa6299f 100644 (file)
@@ -25,6 +25,7 @@
 // INTERNAL INCLUDES
 #include <dali/public-api/actors/renderable-actor.h>
 #include <dali/public-api/math/vector2.h>
+#include <dali/public-api/signals/callback.h>
 #include <dali/integration-api/debug.h>
 #include <dali/integration-api/events/touch-event-integ.h>
 #include <dali/internal/event/actors/actor-impl.h>
@@ -126,7 +127,7 @@ Dali::Actor EmitTouchSignals( Actor* actor, RenderTask& renderTask, const TouchE
 
 TouchEventProcessor::TouchEventProcessor( Stage& stage )
 : mStage( stage ),
-  mLastPrimaryHitActor(),
+  mLastPrimaryHitActor( MakeCallback( this, &TouchEventProcessor::OnObservedActorDisconnected ) ),
   mLastConsumedActor(),
   mTouchDownConsumedActor(),
   mLastRenderTask()
@@ -401,6 +402,29 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even
   }
 }
 
+void TouchEventProcessor::OnObservedActorDisconnected( Actor* actor )
+{
+  if ( actor == mLastPrimaryHitActor.GetActor() )
+  {
+    Dali::Actor handle( actor );
+    TouchEvent touchEvent( 0 );
+    touchEvent.points.push_back( TouchPoint( 0, TouchPoint::Interrupted, 0.0f, 0.0f ) );
+    touchEvent.points[0].hitActor = handle;
+
+    Dali::Actor eventConsumer = EmitTouchSignals( handle, touchEvent );
+
+    if ( mLastConsumedActor.GetActor() != eventConsumer )
+    {
+      EmitTouchSignals( Dali::Actor( mLastConsumedActor.GetActor() ), touchEvent );
+    }
+
+    // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers
+
+    mLastConsumedActor.SetActor( NULL );
+    mLastRenderTask.Reset();
+  }
+}
+
 } // namespace Internal
 
 } // namespace Dali
index a7d64b9..e0c38b7 100644 (file)
@@ -78,6 +78,13 @@ private:
   // Undefined
   TouchEventProcessor& operator=(const TouchEventProcessor& rhs);
 
+  /**
+   * Called by some actor-observers when the observed actor is disconnected.
+   *
+   * @param[in]  actor  The actor that has been disconnected.
+   */
+  void OnObservedActorDisconnected( Actor* actor );
+
 private:
 
   Stage& mStage; ///< Used to deliver touch events