Add intercept touch event 58/245258/18
authorJoogab Yun <joogab.yun@samsung.com>
Tue, 6 Oct 2020 06:38:33 +0000 (15:38 +0900)
committerjoogab yun <joogab.yun@samsung.com>
Wed, 14 Oct 2020 02:58:14 +0000 (02:58 +0000)
The Touch event calls the TouchEvent callback by going back from the last child actor to the parent via hitTest.
InterceptTouchEvent checks the touch event in the parent first.
Returning false from interceptTouchEvent allows child actors to receive TouchEvents.
If it returns true, the actor will receive a TouchEvent.

for example

   Actor parent = Actor::New();
   Actor actor = Actor::New();
   parent.Add(actor);
   actor.TouchedSignal().Connect(&application, functor);
   parent.TouchedSignal().Connect(&application, parentFunctor);

The callbacks are called in the order functor -> parentFunctor.

If you connect interceptTouchSignal to parentActor.

   Dali::DevelActor::InterceptTouchedSignal(parent).Connect(&application, interceptFunctor);

When interceptFunctor returns false, it is called in the same order functor -> parentFunctor.
If it returns true, it means that the TouchEvent was intercepted.
So the child actor will not be able to receive touch events.
Only the parentFunctor is called.

Change-Id: Ib6887adbcee59168a7caf7f36bcc400500c626e8

automated-tests/src/dali/utc-Dali-TouchProcessing.cpp [changed mode: 0644->0755]
dali/devel-api/actors/actor-devel.cpp [changed mode: 0644->0755]
dali/devel-api/actors/actor-devel.h [changed mode: 0644->0755]
dali/internal/event/actors/actor-impl.cpp [changed mode: 0644->0755]
dali/internal/event/actors/actor-impl.h [changed mode: 0644->0755]
dali/internal/event/events/touch-event-processor.cpp [changed mode: 0644->0755]

old mode 100644 (file)
new mode 100755 (executable)
index 2aaf3e2..3c01346
@@ -2068,3 +2068,56 @@ int UtcDaliTouchEventIntegNewTouchEvent(void)
 
   END_TEST;
 }
+
+
+int UtcDaliTouchEventIntercept(void)
+{
+  TestApplication application;
+
+  Actor parent = Actor::New();
+  parent.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+  parent.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  application.GetScene().Add(parent);
+
+  Actor actor = Actor::New();
+  actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, 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 parent's intercept touched signal
+  SignalData        interceptData;
+  TouchEventFunctor interceptFunctor(interceptData, true /* Do intercept */);
+  Dali::DevelActor::InterceptTouchedSignal(parent).Connect(&application, interceptFunctor);
+
+  // Emit a down signal
+  application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f)));
+  // The actor touched signal is not called because the touch is intercepted in the parent.
+  DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, interceptData.functorCalled, TEST_LOCATION);
+  DALI_TEST_EQUALS(PointState::DOWN, interceptData.receivedTouch.points[0].state, TEST_LOCATION);
+  DALI_TEST_CHECK(actor == interceptData.receivedTouch.points[0].hitActor);
+  DALI_TEST_CHECK(parent == interceptData.touchedActor);
+  DALI_TEST_EQUALS(true, parentData.functorCalled, TEST_LOCATION);
+  DALI_TEST_EQUALS(PointState::DOWN, parentData.receivedTouch.points[0].state, TEST_LOCATION);
+  DALI_TEST_CHECK(actor == parentData.receivedTouch.points[0].hitActor);
+  DALI_TEST_CHECK(parent == parentData.touchedActor);
+  data.Reset();
+  parentData.Reset();
+
+  END_TEST;
+}
+
old mode 100644 (file)
new mode 100755 (executable)
index 68a5205..fd08434
@@ -48,6 +48,11 @@ ChildOrderChangedSignalType& ChildOrderChangedSignal(Actor actor)
   return GetImplementation(actor).ChildOrderChangedSignal();
 }
 
+Actor::TouchEventSignalType& InterceptTouchedSignal(Actor actor)
+{
+  return GetImplementation(actor).InterceptTouchedSignal();
+}
+
 } // namespace DevelActor
 
 } // namespace Dali
old mode 100644 (file)
new mode 100755 (executable)
index 43b44f7..cda374f
@@ -215,6 +215,40 @@ using ChildOrderChangedSignalType = Signal<void(Actor)>; ///< Used when the acto
  */
 DALI_CORE_API ChildOrderChangedSignalType& ChildOrderChangedSignal(Actor actor);
 
+/**
+ * @brief This signal is emitted when intercepting the actor's touch event.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ *   void MyCallbackName( Actor actor );
+ * @endcode
+ * actor The actor to intercept
+ *
+ * @note TouchEvent callbacks are called from the last child in the order of the parent's actor.
+ * The InterceptTouchEvent callback is to intercept the touch event in the parent.
+ * So, if the parent interepts the touch event, the child cannot receive the touch event.
+ *
+ * @note example
+ *   Actor parent = Actor::New();
+ *   Actor child = Actor::New();
+ *   parent.Add(child);
+ *   child.TouchedSignal().Connect(&application, childFunctor);
+ *   parent.TouchedSignal().Connect(&application, parentFunctor);
+ * The touch event callbacks are called in the order childFunctor -> parentFunctor.
+ *
+ * If you connect interceptTouchSignal to parentActor.
+ *   Dali::DevelActor::InterceptTouchedSignal(parent).Connect(&application, interceptFunctor);
+ *
+ * When interceptFunctor returns false, the touch event callbacks are called in the same order childFunctor -> parentFunctor.
+ * If interceptFunctor returns true, it means that the TouchEvent was intercepted.
+ * So the child actor will not be able to receive touch events.
+ * Only the parentFunctor is called.
+ *
+ * @return The signal to connect to
+ * @pre The Actor has been initialized
+ */
+DALI_CORE_API Actor::TouchEventSignalType& InterceptTouchedSignal(Actor actor);
+
 } // namespace DevelActor
 
 } // namespace Dali
old mode 100644 (file)
new mode 100755 (executable)
index 321b111..0e0342d
@@ -1319,6 +1319,11 @@ bool Actor::IsGestureRequired( GestureType::Value type ) const
   return mGestureData && mGestureData->IsGestureRequired( type );
 }
 
+bool Actor::EmitInterceptTouchEventSignal( const Dali::TouchEvent& touch )
+{
+  return EmitConsumingSignal( *this, mInterceptTouchedSignal, touch );
+}
+
 bool Actor::EmitTouchEventSignal( const Dali::TouchEvent& touch )
 {
   return EmitConsumingSignal( *this, mTouchedSignal, touch );
@@ -1418,6 +1423,7 @@ Actor::Actor( DerivedType derivedType, const SceneGraph::Node& node )
   mAnchorPoint( nullptr ),
   mRelayoutData( nullptr ),
   mGestureData( nullptr ),
+  mInterceptTouchedSignal(),
   mTouchedSignal(),
   mHoveredSignal(),
   mWheelEventSignal(),
old mode 100644 (file)
new mode 100755 (executable)
index 5a11cd0..a982904
@@ -1326,6 +1326,16 @@ public:
     return mKeyboardFocusable;
   }
 
+
+  /**
+   * Query whether the application or derived actor type requires intercept touch events.
+   * @return True if intercept touch events are required.
+   */
+  bool GetInterceptTouchRequired() const
+  {
+    return !mInterceptTouchedSignal.Empty();
+  }
+
   /**
    * Query whether the application or derived actor type requires touch events.
    * @return True if touch events are required.
@@ -1393,6 +1403,13 @@ public:
 
   // Signals
 
+  /**
+   * Used by the EventProcessor to emit intercept touch event signals.
+   * @param[in] touch The touch data.
+   * @return True if the event was intercepted.
+   */
+  bool EmitInterceptTouchEventSignal( const Dali::TouchEvent& touch );
+
   /**
    * Used by the EventProcessor to emit touch event signals.
    * @param[in] touch The touch data.
@@ -1439,6 +1456,14 @@ public:
    */
   void EmitChildRemovedSignal( Actor& child );
 
+  /**
+   * @copydoc DevelActor::InterceptTouchedSignal()
+   */
+  Dali::Actor::TouchEventSignalType& InterceptTouchedSignal()
+  {
+    return mInterceptTouchedSignal;
+  }
+
   /**
    * @copydoc Dali::Actor::TouchedSignal()
    */
@@ -1982,6 +2007,7 @@ protected:
   ActorGestureData* mGestureData;   ///< Optional Gesture data. Only created when actor requires gestures
 
   // Signals
+  Dali::Actor::TouchEventSignalType         mInterceptTouchedSignal;
   Dali::Actor::TouchEventSignalType        mTouchedSignal;
   Dali::Actor::HoverSignalType             mHoveredSignal;
   Dali::Actor::WheelEventSignalType        mWheelEventSignal;
old mode 100644 (file)
new mode 100755 (executable)
index 864ce8c..146387c
@@ -60,6 +60,36 @@ const char * TOUCH_POINT_STATE[ 6 ] =
 
 #endif // defined(DEBUG_ENABLED)
 
+Dali::Actor EmitInterceptTouchSignals( Dali::Actor actor, const Dali::TouchEvent& touchEvent )
+{
+  Dali::Actor interceptedActor;
+
+  if( actor )
+  {
+     Dali::Actor parent = actor.GetParent();
+     if( parent )
+     {
+       // Recursively deliver events to the actor and its parents for intercept touch event.
+       interceptedActor = EmitInterceptTouchSignals( parent, touchEvent );
+     }
+
+     if( !interceptedActor )
+     {
+       bool intercepted = false;
+       Actor& actorImpl( GetImplementation(actor) );
+       if( actorImpl.GetInterceptTouchRequired() )
+       {
+          intercepted = actorImpl.EmitInterceptTouchEventSignal( touchEvent );
+          if( intercepted )
+          {
+            interceptedActor = Dali::Actor( &actorImpl );
+          }
+       }
+     }
+  }
+
+  return interceptedActor;
+}
 
 /**
  *  Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
@@ -315,7 +345,16 @@ bool TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even
   Dali::Actor consumedActor;
   if ( currentRenderTask )
   {
-    consumedActor = EmitTouchSignals( touchEventImpl->GetPoint( 0 ).GetHitActor(), touchEventHandle );
+    // Emit the intercept touch signal
+    Dali::Actor interceptedActor = EmitInterceptTouchSignals( touchEventImpl->GetPoint( 0 ).GetHitActor(), touchEventHandle );
+    if( interceptedActor )
+    {
+      consumedActor = EmitTouchSignals( interceptedActor, touchEventHandle );
+    }
+    else
+    {
+      consumedActor = EmitTouchSignals( touchEventImpl->GetPoint( 0 ).GetHitActor(), touchEventHandle );
+    }
     consumed = consumedActor ? true : false;
   }