Add property which allows touch-down actor to continuously receive touch 94/237794/7
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 3 Jul 2020 17:03:35 +0000 (18:03 +0100)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Wed, 22 Jul 2020 22:32:15 +0000 (23:32 +0100)
Change-Id: Ie1c1cb38a0a8c3b3dc16ef711c65893d4b841df9

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

index f9451d2..676eba1 100644 (file)
@@ -7864,3 +7864,37 @@ int utcDaliActorPartialUpdateActorsWithSizeHint(void)
   END_TEST;
 }
 
+int UtcDaliActorCaptureAllTouchAfterStartPropertyP(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  DALI_TEST_EQUALS(actor.GetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START).Get<bool>(), false, TEST_LOCATION);
+  actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, true);
+  DALI_TEST_EQUALS(actor.GetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START).Get<bool>(), true, TEST_LOCATION);
+  END_TEST;
+}
+
+int UtcDaliActorCaptureAllTouchAfterStartPropertyN(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+
+  // Make sure setting invalid types does not cause a crash
+  try
+  {
+    actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, 1.0f);
+    actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Vector2::ONE);
+    actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Vector3::ONE);
+    actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Vector4::ONE);
+    actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Property::Map());
+    actor.SetProperty(DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, Property::Array());
+    tet_result(TET_PASS);
+  }
+  catch(...)
+  {
+     tet_result(TET_FAIL);
+  }
+  END_TEST;
+}
index 4e96d66..95db95e 100644 (file)
@@ -22,6 +22,7 @@
 #include <dali/integration-api/events/touch-event-integ.h>
 #include <dali/integration-api/render-task-list-integ.h>
 #include <dali-test-suite-utils.h>
+#include <dali/devel-api/actors/actor-devel.h>
 
 using namespace Dali;
 
@@ -2034,3 +2035,56 @@ int UtcDaliTouchDataGetMouseButtonNagative(void)
   END_TEST;
 }
 
+int UtcDaliTouchDataCapturePropertySet(void)
+{
+  TestApplication application;
+
+  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 touched signal
+  SignalData data;
+  TouchDataFunctor functor( data );
+  actor.TouchSignal().Connect( &application, functor );
+
+  // Emit a down signal
+  application.ProcessEvent( GenerateSingleTouch( PointState::STARTED, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  // Now motion outside of actor, we should not receive the event
+  application.ProcessEvent( GenerateSingleTouch( PointState::MOTION, Vector2( 110.0f, 110.0f ) ) );
+  DALI_TEST_EQUALS( false, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  // Up event, should receive an interrupted
+  application.ProcessEvent( GenerateSingleTouch( PointState::FINISHED, Vector2( 110.0f, 110.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( data.touchData.GetPoint(0).state, PointState::INTERRUPTED, TEST_LOCATION );
+
+  // Now set the capture property
+  actor.SetProperty( DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START, true);
+
+  // Emit a down signal
+  application.ProcessEvent( GenerateSingleTouch( PointState::STARTED, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  // Now motion outside of actor, we now SHOULD receive the event
+  application.ProcessEvent( GenerateSingleTouch( PointState::MOTION, Vector2( 110.0f, 110.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  // Up event, we should receive it again, but as ended rather than interrupted
+  application.ProcessEvent( GenerateSingleTouch( PointState::FINISHED, Vector2( 110.0f, 110.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( data.touchData.GetPoint(0).state, PointState::FINISHED, TEST_LOCATION );
+
+  END_TEST;
+}
index 9eccc32..9a82366 100644 (file)
@@ -106,14 +106,21 @@ enum Type
    * @note Raise, Lower, RaiseToTop, LowerToBottom, RaiseAbove and LowerBelow will override the
    * sibling order. The values set by this Property will likely change.
    */
-  SIBLING_ORDER = KEYBOARD_FOCUSABLE + 1,
+  SIBLING_ORDER,
 
   /**
    * @brief Sets the update size hint of the actor.
    * @details Name "updateSizeHint", type Property::VECTOR2.
    * @note Overrides the size used for the actor damaged area calculation. Affected by the actor model view matrix.
    */
-  UPDATE_SIZE_HINT = SIBLING_ORDER + 1,
+  UPDATE_SIZE_HINT,
+
+  /**
+    * @brief If this actor receives a touch-start event, then all following touch events are sent to this actor until a touch-end.
+    * @details Name "captureAllTouchAfterStart", type Property::BOOLEAN
+    * @note Default is false, i.e. actor under touch event will receive the touch even if touch started on this actor
+    */
+  CAPTURE_ALL_TOUCH_AFTER_START
 };
 
 } // namespace Property
index 65e9ee2..58577dc 100644 (file)
@@ -220,6 +220,7 @@ DALI_PROPERTY( "connectedToScene",          BOOLEAN,  false, false, false, Dali:
 DALI_PROPERTY( "keyboardFocusable",         BOOLEAN,  true,  false, false, Dali::Actor::Property::KEYBOARD_FOCUSABLE )
 DALI_PROPERTY( "siblingOrder",              INTEGER,  true,  false, false, Dali::DevelActor::Property::SIBLING_ORDER )
 DALI_PROPERTY( "updateSizeHint",            VECTOR2,  true,  false, false, Dali::DevelActor::Property::UPDATE_SIZE_HINT )
+DALI_PROPERTY( "captureAllTouchAfterStart", VECTOR2,  true,  false, false, Dali::DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START )
 DALI_PROPERTY_TABLE_END( DEFAULT_ACTOR_PROPERTY_START_INDEX, ActorDefaultProperties )
 
 // Signals
@@ -2807,6 +2808,16 @@ void Actor::SetDefaultProperty( Property::Index index, const Property::Value& pr
       break;
     }
 
+    case Dali::DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START:
+    {
+      bool boolValue = false;
+      if ( property.Get( boolValue ) )
+      {
+        mCaptureAllTouchAfterStart = boolValue;
+      }
+      break;
+    }
+
     default:
     {
       // this can happen in the case of a non-animatable default property so just do nothing
@@ -3939,6 +3950,12 @@ bool Actor::GetCachedPropertyValue( Property::Index index, Property::Value& valu
       break;
     }
 
+    case Dali::DevelActor::Property::CAPTURE_ALL_TOUCH_AFTER_START:
+    {
+      value = mCaptureAllTouchAfterStart;
+      break;
+    }
+
     default:
     {
       // Must be a scene-graph only property
old mode 100755 (executable)
new mode 100644 (file)
index 4e57518..0adfa39
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_ACTOR_H
 
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1374,6 +1374,15 @@ public:
    */
   bool IsHittable() const;
 
+  /**
+   * Query whether the actor captures all touch after it starts even if touch leaves its boundary.
+   * @return true, if it captures all touch after start
+   */
+  bool CapturesAllTouchAfterStart() const
+  {
+    return mCaptureAllTouchAfterStart;
+  }
+
   // Gestures
 
   /**
@@ -2017,6 +2026,7 @@ protected:
   bool mPositionUsesAnchorPoint                    : 1; ///< Cached: Whether the position uses the anchor point or not.
   bool mVisible                                    : 1; ///< Cached: Whether the actor is visible or not.
   bool mInheritLayoutDirection                     : 1; ///< Whether the actor inherits the layout direction from parent.
+  bool mCaptureAllTouchAfterStart                  : 1; ///< Whether the actor should capture all touch after touch starts even if the motion moves outside of the actor area.
   LayoutDirection::Type mLayoutDirection           : 2; ///< Layout direction, Left to Right or Right to Left.
   DrawMode::Type mDrawMode                         : 3; ///< Cached: How the actor and its children should be drawn
   ColorMode mColorMode                             : 3; ///< Cached: Determines whether mWorldColor is inherited
index 97c15a2..cc08da8 100644 (file)
@@ -147,12 +147,55 @@ Dali::Actor EmitTouchSignals( Actor* actor, RenderTask& renderTask, const TouchE
   return consumingActor;
 }
 
+/**
+ * @brief Parses the primary touch point by performing a hit-test if necessary
+ *
+ * @param[out] hitTestResults The hit test results are put into this variable
+ * @param[in/out] capturingTouchActorObserver The observer for the capturing touch actor member
+ * @param[in] lastRenderTask The last render task member
+ * @param[in] currentPoint The current point information
+ * @param[in] scene The scene that this touch is related to
+ */
+void ParsePrimaryTouchPoint(
+    HitTestAlgorithm::Results& hitTestResults,
+    ActorObserver& capturingTouchActorObserver,
+    const RenderTaskPtr& lastRenderTask,
+    const Integration::Point& currentPoint,
+    const Internal::Scene& scene )
+{
+  Actor* capturingTouchActor = capturingTouchActorObserver.GetActor();
+
+  // We only set the capturing touch actor when the first touch-started actor captures all touch so if it's set, just use it
+  if( capturingTouchActor && lastRenderTask )
+  {
+    hitTestResults.actor = Dali::Actor( capturingTouchActor );
+    hitTestResults.renderTask = lastRenderTask;
+    const Vector2& screenPosition = currentPoint.GetScreenPosition();
+    capturingTouchActor->ScreenToLocal( *lastRenderTask, hitTestResults.actorCoordinates.x, hitTestResults.actorCoordinates.y, screenPosition.x, screenPosition.y );
+  }
+  else
+  {
+    HitTestAlgorithm::HitTest( scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults );
+
+    if( currentPoint.GetState() == PointState::STARTED && hitTestResults.actor )
+    {
+      // If we've just started touch, then check whether the actor has requested to capture all touch events
+      Actor* hitActor = &GetImplementation( hitTestResults.actor );
+      if( hitActor->CapturesAllTouchAfterStart() )
+      {
+        capturingTouchActorObserver.SetActor( hitActor );
+      }
+    }
+  }
+}
+
 } // unnamed namespace
 
 TouchEventProcessor::TouchEventProcessor( Scene& scene )
 : mScene( scene ),
   mLastPrimaryHitActor( MakeCallback( this, &TouchEventProcessor::OnObservedActorDisconnected ) ),
   mLastConsumedActor(),
+  mCapturingTouchActor(),
   mTouchDownConsumedActor(),
   mLastRenderTask()
 {
@@ -212,9 +255,10 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even
       AllocAndEmitTouchSignals( event.time, touchDownConsumedActorHandle, currentPoint );
     }
 
-    mLastPrimaryHitActor.SetActor( NULL );
-    mLastConsumedActor.SetActor( NULL );
-    mTouchDownConsumedActor.SetActor( NULL );
+    mLastPrimaryHitActor.SetActor( nullptr );
+    mLastConsumedActor.SetActor( nullptr );
+    mCapturingTouchActor.SetActor( nullptr );
+    mTouchDownConsumedActor.SetActor( nullptr );
     mLastRenderTask.Reset();
 
     currentPoint.SetHitActor( Dali::Actor() );
@@ -239,13 +283,25 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even
   DALI_LOG_INFO( gLogFilter, Debug::General, "Point(s): %d\n", event.GetPointCount() );
 
   RenderTaskPtr currentRenderTask;
+  bool firstPointParsed = false;
 
-  for ( Integration::PointContainerConstIterator iter = event.points.begin(), beginIter = event.points.begin(), endIter = event.points.end(); iter != endIter; ++iter )
+  for ( auto&& currentPoint : event.points )
   {
     HitTestAlgorithm::Results hitTestResults;
-    HitTestAlgorithm::HitTest( mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), iter->GetScreenPosition(), hitTestResults );
+    if( !firstPointParsed )
+    {
+      firstPointParsed = true;
+      ParsePrimaryTouchPoint( hitTestResults, mCapturingTouchActor, mLastRenderTask, currentPoint, mScene );
+
+      // Only set the currentRenderTask for the primary hit actor.
+      currentRenderTask = hitTestResults.renderTask;
+    }
+    else
+    {
+      HitTestAlgorithm::HitTest( mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults );
+    }
 
-    Integration::Point newPoint( *iter );
+    Integration::Point newPoint( currentPoint );
     newPoint.SetHitActor( hitTestResults.actor );
     newPoint.SetLocalPosition( hitTestResults.actorCoordinates );
 
@@ -253,16 +309,11 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even
     touchData->AddPoint( newPoint );
 
     DALI_LOG_INFO( gLogFilter, Debug::General, "  State(%s), Screen(%.0f, %.0f), HitActor(%p, %s), Local(%.2f, %.2f)\n",
-                   TOUCH_POINT_STATE[iter->GetState()], iter->GetScreenPosition().x, iter->GetScreenPosition().y,
+                   TOUCH_POINT_STATE[currentPoint.GetState()], currentPoint.GetScreenPosition().x, currentPoint.GetScreenPosition().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 );
 
-    // Only set the currentRenderTask for the primary hit actor.
-    if ( iter == beginIter && hitTestResults.renderTask )
-    {
-      currentRenderTask = hitTestResults.renderTask;
-    }
   }
 
   // 3) Recursively deliver events to the actor and its parents, until the event is consumed or the stage is reached.
@@ -354,8 +405,9 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even
 
   if ( primaryPointState == PointState::UP )
   {
-    mLastPrimaryHitActor.SetActor( NULL );
-    mLastConsumedActor.SetActor( NULL );
+    mLastPrimaryHitActor.SetActor( nullptr );
+    mLastConsumedActor.SetActor( nullptr );
+    mCapturingTouchActor.SetActor( nullptr );
     mLastRenderTask.Reset();
   }
   else
@@ -379,8 +431,9 @@ void TouchEventProcessor::ProcessTouchEvent( const Integration::TouchEvent& even
     }
     else
     {
-      mLastPrimaryHitActor.SetActor( NULL );
-      mLastConsumedActor.SetActor( NULL );
+      mLastPrimaryHitActor.SetActor( nullptr );
+      mLastConsumedActor.SetActor( nullptr );
+      mCapturingTouchActor.SetActor( nullptr );
       mLastRenderTask.Reset();
     }
   }
index 61edcca..9306e6f 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_TOUCH_EVENT_PROCESSOR_H
 
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -91,6 +91,7 @@ private:
 
   ActorObserver mLastPrimaryHitActor; ///< Stores the last primary point hit actor
   ActorObserver mLastConsumedActor; ///< Stores the last consumed actor
+  ActorObserver mCapturingTouchActor; ///< Stored the actor that captures touch
   ActorObserver mTouchDownConsumedActor; ///< Stores the touch-down consumed actor
   RenderTaskPtr mLastRenderTask; ///< The RenderTask used for the last hit actor
 };