From 6fc4471cc7772729baaa580aa8a99a6bd39f8f51 Mon Sep 17 00:00:00 2001 From: Adeel Kazmi Date: Fri, 27 Feb 2015 11:23:48 +0000 Subject: [PATCH] (Touch) Emit interrupted when actor is disconnected Change-Id: I4c25b100ad908bdfb35021dc759e8d31559ff005 --- .../src/dali/utc-Dali-TouchProcessing.cpp | 254 ++++++++++++++++++++- dali/internal/event/events/actor-observer.cpp | 17 +- dali/internal/event/events/actor-observer.h | 21 +- .../event/events/touch-event-processor.cpp | 26 ++- dali/internal/event/events/touch-event-processor.h | 7 + 5 files changed, 317 insertions(+), 8 deletions(-) diff --git a/automated-tests/src/dali/utc-Dali-TouchProcessing.cpp b/automated-tests/src/dali/utc-Dali-TouchProcessing.cpp index 19327d6..9c72554 100644 --- a/automated-tests/src/dali/utc-Dali-TouchProcessing.cpp +++ b/automated-tests/src/dali/utc-Dali-TouchProcessing.cpp @@ -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; +} diff --git a/dali/internal/event/events/actor-observer.cpp b/dali/internal/event/events/actor-observer.cpp index f345ca7..142783d 100644 --- a/dali/internal/event/events/actor-observer.cpp +++ b/dali/internal/event/events/actor-observer.cpp @@ -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; } diff --git a/dali/internal/event/events/actor-observer.h b/dali/internal/event/events/actor-observer.h index 3cc8b19..bb9e5aa 100644 --- a/dali/internal/event/events/actor-observer.h +++ b/dali/internal/event/events/actor-observer.h @@ -19,6 +19,7 @@ */ // INTERNAL INCLUDES +#include #include 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 diff --git a/dali/internal/event/events/touch-event-processor.cpp b/dali/internal/event/events/touch-event-processor.cpp index 8426d77..aa6299f 100644 --- a/dali/internal/event/events/touch-event-processor.cpp +++ b/dali/internal/event/events/touch-event-processor.cpp @@ -25,6 +25,7 @@ // INTERNAL INCLUDES #include #include +#include #include #include #include @@ -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 diff --git a/dali/internal/event/events/touch-event-processor.h b/dali/internal/event/events/touch-event-processor.h index a7d64b9..e0c38b7 100644 --- a/dali/internal/event/events/touch-event-processor.h +++ b/dali/internal/event/events/touch-event-processor.h @@ -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 -- 2.7.4