Add InterceptWheelEvent 32/295232/2
authorjoogab.yun <joogab.yun@samsung.com>
Tue, 4 Jul 2023 05:08:46 +0000 (14:08 +0900)
committerjoogab.yun <joogab.yun@samsung.com>
Tue, 4 Jul 2023 05:38:29 +0000 (14:38 +0900)
The Wheel event calls the WheelEvent callback by going back from the last child actor to the parent via hitTest.
InterceptWheelEvent checks the wheel event in the parent first.
Returning false from InterceptWheelEvent allows child actors to receive WheelEvents.
If it returns true, the actor will receive a WheelEvent.

for example

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

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

If you connect InterceptWheelSignal to parentActor.

  Dali::DevelActor::InterceptWheelSignal(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 WheelEvent was intercepted.
So the child actor will not be able to receive wheel events.
Only the parentFunctor is called.

Change-Id: I8ec2c70ca148565e6e899cedb9a997b2f851b0b8

automated-tests/src/dali/utc-Dali-WheelEvent.cpp
dali/devel-api/actors/actor-devel.cpp
dali/devel-api/actors/actor-devel.h
dali/internal/event/actors/actor-impl.cpp
dali/internal/event/actors/actor-impl.h
dali/internal/event/events/wheel-event-processor.cpp

index bdd13a1..edf476a 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <dali-test-suite-utils.h>
+#include <dali/devel-api/actors/actor-devel.h>
 #include <dali/devel-api/events/wheel-event-devel.h>
 #include <dali/integration-api/events/wheel-event-integ.h>
 #include <dali/public-api/dali-core.h>
@@ -57,8 +58,9 @@ struct SignalData
 // Functor that sets the data when called
 struct WheelEventReceivedFunctor
 {
-  WheelEventReceivedFunctor(SignalData& data)
-  : signalData(data)
+  WheelEventReceivedFunctor(SignalData& data, bool returnValue = true)
+  : signalData(data),
+    returnValue(returnValue)
   {
   }
 
@@ -68,10 +70,11 @@ struct WheelEventReceivedFunctor
     signalData.receivedWheelEvent = wheelEvent;
     signalData.wheeledActor       = actor;
 
-    return true;
+    return returnValue;
   }
 
   SignalData& signalData;
+  bool        returnValue;
 };
 
 } // anonymous namespace
@@ -272,3 +275,72 @@ int UtcDaliWheelEventSignalling(void)
   data.Reset();
   END_TEST;
 }
+
+int UtcDaliWheelEventIntercept(void)
+{
+  TestApplication application; // Reset all test adapter return codes
+
+  Actor actor = Actor::New();
+  actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  application.GetScene().Add(actor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Connect to actor's wheel event signal
+  SignalData                data;
+  WheelEventReceivedFunctor functor(data);
+  actor.WheelEventSignal().Connect(&application, functor);
+
+  Vector2                 screenCoordinates(10.0f, 10.0f);
+  Integration::WheelEvent event(Integration::WheelEvent::MOUSE_WHEEL, 0, SHIFT_MODIFIER, screenCoordinates, 1, 1000u);
+
+  // Emit a wheel signal
+  application.ProcessEvent(event);
+  DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION);
+  DALI_TEST_CHECK(actor == data.wheeledActor);
+  DALI_TEST_EQUALS(WheelEvent::MOUSE_WHEEL, data.receivedWheelEvent.GetType(), TEST_LOCATION); // check type
+  DALI_TEST_EQUALS(0, data.receivedWheelEvent.GetDirection(), TEST_LOCATION);                  // check direction
+  DALI_TEST_EQUALS(SHIFT_MODIFIER, data.receivedWheelEvent.GetModifiers(), TEST_LOCATION);     // check modifier
+  DALI_TEST_EQUALS(screenCoordinates, data.receivedWheelEvent.GetPoint(), TEST_LOCATION);      // check modifier
+  DALI_TEST_EQUALS(1, data.receivedWheelEvent.GetDelta(), TEST_LOCATION);                      // check modifier
+  DALI_TEST_EQUALS(1000u, data.receivedWheelEvent.GetTime(), TEST_LOCATION);                   // check modifier
+  data.Reset();
+
+  // Intercept wheel events on the root actor.
+  Actor rootActor(application.GetScene().GetRootLayer());
+
+  // Connect to root actor's intercept wheel event signal
+  SignalData                rootData;
+  WheelEventReceivedFunctor rootFunctor(rootData); // Consumes signal
+  Dali::DevelActor::InterceptWheelSignal(rootActor).Connect(&application, rootFunctor);
+
+  Integration::WheelEvent newEvent(Integration::WheelEvent::MOUSE_WHEEL, 0, SHIFT_MODIFIER, screenCoordinates, 1, 1000u);
+  application.ProcessEvent(newEvent);
+
+  // It should be able to receive wheel events to root actor by registering only InterceptWheelEvent.
+  DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION);
+  DALI_TEST_CHECK(rootActor == rootData.wheeledActor);
+  DALI_TEST_EQUALS(WheelEvent::MOUSE_WHEEL, rootData.receivedWheelEvent.GetType(), TEST_LOCATION); // check type
+  DALI_TEST_EQUALS(0, rootData.receivedWheelEvent.GetDirection(), TEST_LOCATION);                  // check direction
+  DALI_TEST_EQUALS(SHIFT_MODIFIER, rootData.receivedWheelEvent.GetModifiers(), TEST_LOCATION);     // check modifier
+  DALI_TEST_EQUALS(screenCoordinates, rootData.receivedWheelEvent.GetPoint(), TEST_LOCATION);      // check modifier
+  DALI_TEST_EQUALS(1, rootData.receivedWheelEvent.GetDelta(), TEST_LOCATION);                      // check modifier
+  DALI_TEST_EQUALS(1000u, rootData.receivedWheelEvent.GetTime(), TEST_LOCATION);                   // check modifier
+
+  // Remove actor from the scene
+  application.GetScene().Remove(actor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Emit a move at the same point, we should not be signalled.
+  application.ProcessEvent(event);
+  DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION);
+  data.Reset();
+  END_TEST;
+}
index 8e51ae9..4809778 100644 (file)
@@ -69,6 +69,11 @@ Actor::TouchEventSignalType& InterceptTouchedSignal(Actor actor)
   return GetImplementation(actor).InterceptTouchedSignal();
 }
 
+Actor::WheelEventSignalType& InterceptWheelSignal(Actor actor)
+{
+  return GetImplementation(actor).InterceptWheelSignal();
+}
+
 void SetNeedGesturePropagation(Actor actor, bool propagation)
 {
   return GetImplementation(actor).SetNeedGesturePropagation(propagation);
index c4e8a55..5120f54 100644 (file)
@@ -346,6 +346,40 @@ DALI_CORE_API ChildOrderChangedSignalType& ChildOrderChangedSignal(Actor actor);
 DALI_CORE_API Actor::TouchEventSignalType& InterceptTouchedSignal(Actor actor);
 
 /**
+ * @brief This signal is emitted when intercepting the actor's wheel event.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ *   void MyCallbackName( Actor actor );
+ * @endcode
+ * actor The actor to intercept
+ *
+ * @note WheelEvent callbacks are called from the last child in the order of the parent's actor.
+ * The InterceptWheelEvent callback is to intercept the wheel event in the parent.
+ * So, if the parent interepts the wheel event, the child cannot receive the Wheel event.
+ *
+ * @note example
+ *   Actor parent = Actor::New();
+ *   Actor child = Actor::New();
+ *   parent.Add(child);
+ *   child.WheelEventSignal().Connect(&application, childFunctor);
+ *   parent.WheelEventSignal().Connect(&application, parentFunctor);
+ * The wheel event callbacks are called in the order childFunctor -> parentFunctor.
+ *
+ * If you connect InterceptWheelSignal to parentActor.
+ *   Dali::DevelActor::InterceptWheelSignal(parent).Connect(&application, interceptFunctor);
+ *
+ * When interceptFunctor returns false, the wheel event callbacks are called in the same order childFunctor -> parentFunctor.
+ * If interceptFunctor returns true, it means that the WheelEvent was intercepted.
+ * So the child actor will not be able to receive wheel events.
+ * Only the parentFunctor is called.
+ *
+ * @return The signal to connect to
+ * @pre The Actor has been initialized
+ */
+DALI_CORE_API Actor::WheelEventSignalType& InterceptWheelSignal(Actor actor);
+
+/**
  * @brief This is used when the parent actor wants to listen to gesture events.
  *
  * @note example The child is overlapped on the parent.
index 5d091c7..ac39d30 100644 (file)
@@ -1028,6 +1028,11 @@ bool Actor::EmitHoverEventSignal(const Dali::HoverEvent& event)
   return EmitConsumingSignal(*this, mHoveredSignal, event);
 }
 
+bool Actor::EmitInterceptWheelEventSignal(const Dali::WheelEvent& event)
+{
+  return EmitConsumingSignal(*this, mInterceptWheelSignal, event);
+}
+
 bool Actor::EmitWheelEventSignal(const Dali::WheelEvent& event)
 {
   return EmitConsumingSignal(*this, mWheelEventSignal, event);
@@ -1087,6 +1092,7 @@ Actor::Actor(DerivedType derivedType, const SceneGraph::Node& node)
   mInterceptTouchedSignal(),
   mTouchedSignal(),
   mHoveredSignal(),
+  mInterceptWheelSignal(),
   mWheelEventSignal(),
   mOnSceneSignal(),
   mOffSceneSignal(),
index 85d0d53..041e12c 100644 (file)
@@ -1304,6 +1304,15 @@ public:
   }
 
   /**
+   * Query whether the application or derived actor type requires intercept wheel events.
+   * @return True if intercept wheel events are required.
+   */
+  bool GetInterceptWheelRequired() const
+  {
+    return !mInterceptWheelSignal.Empty();
+  }
+
+  /**
    * Query whether the application or derived actor type requires wheel events.
    * @return True if wheel events are required.
    */
@@ -1401,6 +1410,13 @@ public:
   bool EmitHoverEventSignal(const Dali::HoverEvent& event);
 
   /**
+   * Used by the EventProcessor to emit intercept wheel event signals.
+   * @param[in] event The wheel event.
+   * @return True if the event was intercepted.
+   */
+  bool EmitInterceptWheelEventSignal(const Dali::WheelEvent& event);
+
+  /**
    * Used by the EventProcessor to emit wheel event signals.
    * @param[in] event The wheel event.
    * @return True if the event was consumed.
@@ -1462,6 +1478,14 @@ public:
   }
 
   /**
+   * @copydoc DevelActor::InterceptWheelSignal()
+   */
+  Dali::Actor::WheelEventSignalType& InterceptWheelSignal()
+  {
+    return mInterceptWheelSignal;
+  }
+
+  /**
    * @copydoc Dali::Actor::WheelEventSignal()
    */
   Dali::Actor::WheelEventSignalType& WheelEventSignal()
@@ -1923,6 +1947,7 @@ protected:
   Dali::Actor::TouchEventSignalType             mInterceptTouchedSignal;
   Dali::Actor::TouchEventSignalType             mTouchedSignal;
   Dali::Actor::HoverSignalType                  mHoveredSignal;
+  Dali::Actor::WheelEventSignalType             mInterceptWheelSignal;
   Dali::Actor::WheelEventSignalType             mWheelEventSignal;
   Dali::Actor::OnSceneSignalType                mOnSceneSignal;
   Dali::Actor::OffSceneSignalType               mOffSceneSignal;
index c54cd3e..0580b29 100644 (file)
@@ -41,6 +41,38 @@ DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_WHEEL_PROCESSOR");
 #endif
 
+Dali::Actor EmitInterceptWheelSignals(Dali::Actor actor, const Dali::WheelEvent& wheelEvent)
+{
+  Dali::Actor interceptedActor;
+
+  if(actor)
+  {
+    Dali::Actor parent = actor.GetParent();
+    if(parent)
+    {
+      // Recursively deliver events to the actor and its parents for intercept wheel event.
+      interceptedActor = EmitInterceptWheelSignals(parent, wheelEvent);
+    }
+
+    if(!interceptedActor)
+    {
+      bool   intercepted = false;
+      Actor& actorImpl(GetImplementation(actor));
+      if(actorImpl.GetInterceptWheelRequired())
+      {
+        DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_WHEEL_EVENT_SIGNAL");
+        intercepted = actorImpl.EmitInterceptWheelEventSignal(wheelEvent);
+        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.
  */
@@ -60,6 +92,7 @@ Dali::Actor EmitWheelSignals(Dali::Actor actor, const Dali::WheelEvent& event)
     if(actorImpl.GetWheelEventRequired())
     {
       // Emit the signal to the parent
+      DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_WHEEL_EVENT_SIGNAL");
       consumed = actorImpl.EmitWheelEventSignal(event);
     }
 
@@ -96,7 +129,7 @@ bool IsActorWheelableFunction(Dali::Actor actor, Dali::HitTestAlgorithm::Travers
   {
     case Dali::HitTestAlgorithm::CHECK_ACTOR:
     {
-      if(GetImplementation(actor).GetWheelEventRequired() && // Does the Application or derived actor type require a wheel event?
+      if((GetImplementation(actor).GetWheelEventRequired() || GetImplementation(actor).GetInterceptWheelRequired()) && // Does the Application or derived actor type require a wheel event?
          GetImplementation(actor).IsHittable())
       {
         hittable = true;
@@ -144,7 +177,18 @@ void WheelEventProcessor::ProcessWheelEvent(const Integration::WheelEvent& event
     DALI_LOG_INFO(gLogFilter, Debug::General, "  Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n", event.point.x, event.point.y, (hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL), (hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : ""), hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y);
 
     // Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
-    Dali::Actor consumedActor = EmitWheelSignals(hitTestResults.actor, wheelEventHandle);
+    Dali::Actor consumedActor;
+
+    // Emit the intercept wheel event signal
+    Dali::Actor interceptedActor = EmitInterceptWheelSignals(hitTestResults.actor, wheelEventHandle);
+    if(interceptedActor)
+    {
+      consumedActor = EmitWheelSignals(interceptedActor, wheelEventHandle);
+    }
+    else
+    {
+      consumedActor = EmitWheelSignals(hitTestResults.actor, wheelEventHandle);
+    }
 
     DALI_LOG_INFO(gLogFilter, Debug::Concise, "HitActor:      (%p) %s\n", hitTestResults.actor ? reinterpret_cast<void*>(&hitTestResults.actor.GetBaseObject()) : NULL, hitTestResults.actor ? hitTestResults.actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "");
     DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor: (%p) %s\n", consumedActor ? reinterpret_cast<void*>(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str() : "");