(HitTest) Check clipped actor if it is really touchable before using as hit-actor
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hit-test-algorithm-impl.cpp
index 59bb3d0..d47f35d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 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.
@@ -27,9 +27,8 @@
 #include <dali/internal/event/actors/camera-actor-impl.h>
 #include <dali/internal/event/actors/layer-impl.h>
 #include <dali/internal/event/actors/layer-list.h>
-#include <dali/internal/event/common/stage-impl.h>
 #include <dali/internal/event/common/projection.h>
-#include <dali/internal/event/images/frame-buffer-image-impl.h>
+#include <dali/internal/event/events/ray-test.h>
 #include <dali/internal/event/render-tasks/render-task-impl.h>
 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
 
@@ -48,7 +47,7 @@ namespace
 struct HitActor
 {
   HitActor()
-  : actor( NULL ),
+  : actor( nullptr ),
     distance( std::numeric_limits<float>::max() ),
     depth( std::numeric_limits<int>::min() )
   {
@@ -75,17 +74,17 @@ struct HitTestFunctionWrapper : public HitTestInterface
   {
   }
 
-  virtual bool IsActorHittable( Actor* actor )
+  bool IsActorHittable( Actor* actor ) override
   {
     return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::CHECK_ACTOR );
   }
 
-  virtual bool DescendActorHierarchy( Actor* actor )
+  bool DescendActorHierarchy( Actor* actor ) override
   {
     return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE );
   }
 
-  virtual bool DoesLayerConsumeHit( Layer* layer )
+  bool DoesLayerConsumeHit( Layer* layer ) override
   {
     // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
     // where the caller may want to check for something completely different.
@@ -102,19 +101,19 @@ struct HitTestFunctionWrapper : public HitTestInterface
  */
 struct ActorTouchableCheck : public HitTestInterface
 {
-  virtual bool IsActorHittable( Actor* actor )
+  bool IsActorHittable( Actor* actor ) override
   {
     return actor->GetTouchRequired() && // Does the Application or derived actor type require a touch event?
            actor->IsHittable();         // Is actor sensitive, visible and on the scene?
   }
 
-  virtual bool DescendActorHierarchy( Actor* actor )
+  bool DescendActorHierarchy( Actor* actor ) override
   {
     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
   }
 
-  virtual bool DoesLayerConsumeHit( Layer* layer )
+  bool DoesLayerConsumeHit( Layer* layer ) override
   {
     return layer->IsTouchConsumed();
   }
@@ -125,14 +124,14 @@ struct ActorTouchableCheck : public HitTestInterface
  */
 bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
                                           const RenderTask& renderTask,
-                                          const Vector< RenderTaskList::Exclusive >& exclusives )
+                                          const RenderTaskList::ExclusivesContainer& exclusives )
 
 {
-  if( exclusives.Size() )
+  if( exclusives.size() )
   {
-    for( Vector< RenderTaskList::Exclusive >::Iterator exclusiveIt = exclusives.Begin(); exclusives.End() != exclusiveIt; ++exclusiveIt )
+    for( const auto& exclusive : exclusives )
     {
-      if( ( exclusiveIt->renderTaskPtr != &renderTask ) && ( exclusiveIt->actorPtr == &actor ) )
+      if( ( exclusive.renderTaskPtr != &renderTask ) && ( exclusive.actor.GetActor() == &actor ) )
       {
         return true;
       }
@@ -152,7 +151,7 @@ bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
  */
 HitActor HitTestWithinLayer( Actor& actor,
                              const RenderTask& renderTask,
-                             const Vector< RenderTaskList::Exclusive >& exclusives,
+                             const RenderTaskList::ExclusivesContainer& exclusives,
                              const Vector4& rayOrigin,
                              const Vector4& rayDir,
                              float& nearClippingPlane,
@@ -161,7 +160,8 @@ HitActor HitTestWithinLayer( Actor& actor,
                              bool& overlayHit,
                              bool layerIs3d,
                              uint32_t clippingDepth,
-                             uint32_t clippingBitPlaneMask )
+                             uint32_t clippingBitPlaneMask,
+                             const RayTest& rayTest )
 {
   HitActor hit;
 
@@ -188,13 +188,13 @@ HitActor HitTestWithinLayer( Actor& actor,
 
     // Ensure the actor has a valid size.
     // If so, perform a quick ray sphere test to see if our ray is close to the actor.
-    if( size.x > 0.0f && size.y > 0.0f && actor.RaySphereTest( rayOrigin, rayDir ) )
+    if( size.x > 0.0f && size.y > 0.0f && rayTest.SphereTest( actor, rayOrigin, rayDir ) )
     {
       Vector2 hitPointLocal;
       float distance;
 
       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
-      if( actor.RayActorTest( rayOrigin, rayDir, hitPointLocal, distance ) )
+      if( rayTest.ActorTest( actor, rayOrigin, rayDir, hitPointLocal, distance ) )
       {
         if( distance >= nearClippingPlane && distance <= farClippingPlane )
         {
@@ -298,7 +298,16 @@ HitActor HitTestWithinLayer( Actor& actor,
                                                  overlayHit,
                                                  layerIs3d,
                                                  newClippingDepth,
-                                                 clippingBitPlaneMask ) );
+                                                 clippingBitPlaneMask,
+                                                 rayTest ) );
+
+        // Make sure the set hit actor is actually hittable. This is usually required when we have some
+        // clipping as we need to hit-test all actors as we descend the tree regardless of whether they
+        // are hittable or not.
+        if(currentHit.actor && !hitCheck.IsActorHittable(currentHit.actor))
+        {
+          continue;
+        }
 
         bool updateChildHit = false;
         if( currentHit.distance >= 0.0f )
@@ -402,13 +411,14 @@ void GetCameraClippingPlane( RenderTask& renderTask, float& nearClippingPlane, f
 /**
  * Hit test a RenderTask
  */
-bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
-                        Stage& stage,
+bool HitTestRenderTask( const RenderTaskList::ExclusivesContainer& exclusives,
+                        const Vector2& sceneSize,
                         LayerList& layers,
                         RenderTask& renderTask,
                         Vector2 screenCoordinates,
                         Results& results,
-                        HitTestInterface& hitCheck )
+                        HitTestInterface& hitCheck,
+                        const RayTest& rayTest )
 {
   if ( renderTask.IsHittable( screenCoordinates ) )
   {
@@ -433,7 +443,7 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
       Dali::Layer layer( sourceActor->GetLayer() );
       if( layer )
       {
-        const uint32_t sourceActorDepth( layer.GetDepth() );
+        const uint32_t sourceActorDepth( layer.GetProperty< bool >( Dali::Layer::Property::DEPTH ) );
 
         CameraActor* cameraActor = renderTask.GetCameraActor();
         bool pickingPossible = cameraActor->BuildPickingRay(
@@ -450,7 +460,6 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
         HitActor hit;
         bool overlayHit = false;
         bool layerConsumesHit = false;
-        const Vector2& stageSize = stage.GetSize();
 
         for( int32_t i = layers.GetLayerCount() - 1; i >= 0 && !( hit.actor ); --i )
         {
@@ -458,7 +467,7 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
           overlayHit = false;
 
           // Ensure layer is touchable (also checks whether ancestors are also touchable)
-          if( IsActuallyHittable( *layer, screenCoordinates, stageSize, hitCheck ) )
+          if( IsActuallyHittable( *layer, screenCoordinates, sceneSize, hitCheck ) )
           {
             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
             if( sourceActorDepth == static_cast<uint32_t>( i ) )
@@ -475,7 +484,8 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
                                         overlayHit,
                                         layer->GetBehavior() == Dali::Layer::LAYER_3D,
                                         0u,
-                                        0u );
+                                        0u,
+                                        rayTest );
             }
             else if( IsWithinSourceActors( *sourceActor, *layer ) )
             {
@@ -491,7 +501,8 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
                                         overlayHit,
                                         layer->GetBehavior() == Dali::Layer::LAYER_3D,
                                         0u,
-                                        0u );
+                                        0u,
+                                        rayTest );
             }
 
             // If this layer is set to consume the hit, then do not check any layers behind it
@@ -525,7 +536,7 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
 /**
  * Iterate through the RenderTaskList and perform hit testing.
  *
- * @param[in] stage The stage the tests will be performed in
+ * @param[in] sceneSize The scene size the tests will be performed in
  * @param[in] layers The list of layers to test
  * @param[in] taskList The list of render tasks
  * @param[out] results Ray information calculated by the camera
@@ -533,7 +544,7 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
  * @param[in] onScreen True to test on-screen, false to test off-screen
  * @return True if we have a hit, false otherwise
  */
-bool HitTestRenderTaskList( Stage& stage,
+bool HitTestRenderTaskList( const Vector2& sceneSize,
                             LayerList& layers,
                             RenderTaskList& taskList,
                             const Vector2& screenCoordinates,
@@ -543,19 +554,20 @@ bool HitTestRenderTaskList( Stage& stage,
 {
   RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
   RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
-  const Vector< RenderTaskList::Exclusive >& exclusives = taskList.GetExclusivesList();
+  const auto& exclusives = taskList.GetExclusivesList();
+  RayTest rayTest;
 
   for( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
   {
     RenderTask& renderTask = *iter->Get();
-    bool isOffscreenRenderTask = ( renderTask.GetTargetFrameBuffer() || renderTask.GetFrameBuffer() );
+    const bool isOffscreenRenderTask = renderTask.GetFrameBuffer();
     if( (onScreen && isOffscreenRenderTask) || (!onScreen && !isOffscreenRenderTask) )
     {
       // Skip to next task
       continue;
     }
 
-    if( HitTestRenderTask( exclusives, stage, layers, renderTask, screenCoordinates, results, hitCheck ) )
+    if( HitTestRenderTask( exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest ) )
     {
       // Return true when an actor is hit (or layer in our render-task consumes the hit)
       return true; // don't bother checking off screen tasks
@@ -568,7 +580,7 @@ bool HitTestRenderTaskList( Stage& stage,
 /**
  * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
  *
- * @param[in] stage The stage the tests will be performed in
+ * @param[in] sceneSize The scene size the tests will be performed in
  * @param[in] layers The list of layers to test
  * @param[in] taskList The list of render tasks
  * @param[out] results Ray information calculated by the camera
@@ -576,7 +588,7 @@ bool HitTestRenderTaskList( Stage& stage,
  * @param[in] onScreen True to test on-screen, false to test off-screen
  * @return True if we have a hit, false otherwise
  */
-bool HitTestForEachRenderTask( Stage& stage,
+bool HitTestForEachRenderTask( const Vector2& sceneSize,
                                LayerList& layers,
                                RenderTaskList& taskList,
                                const Vector2& screenCoordinates,
@@ -587,8 +599,8 @@ bool HitTestForEachRenderTask( Stage& stage,
 
   // Check on-screen tasks before off-screen ones.
   // Hit test order should be reverse of draw order (see ProcessRenderTasks() where off-screen tasks are drawn first).
-  if( HitTestRenderTaskList( stage, layers, taskList, screenCoordinates, results, hitCheck, true  ) ||
-      HitTestRenderTaskList( stage, layers, taskList, screenCoordinates, results, hitCheck, false ) )
+  if( HitTestRenderTaskList( sceneSize, layers, taskList, screenCoordinates, results, hitCheck, true  ) ||
+      HitTestRenderTaskList( sceneSize, layers, taskList, screenCoordinates, results, hitCheck, false ) )
   {
     // Found hit.
     result = true;
@@ -599,20 +611,15 @@ bool HitTestForEachRenderTask( Stage& stage,
 
 } // unnamed namespace
 
-HitTestInterface::~HitTestInterface()
-{
-}
+HitTestInterface::~HitTestInterface() = default;
 
-bool HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
+bool HitTest( const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
 {
   bool wasHit( false );
-  // Hit-test the regular on-stage actors
-  RenderTaskList& taskList = stage.GetRenderTaskList();
-  LayerList& layerList = stage.GetLayerList();
-
+  // Hit-test the regular on-scene actors
   Results hitTestResults;
   HitTestFunctionWrapper hitTestFunctionWrapper( func );
-  if( HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
+  if( HitTestForEachRenderTask( sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
   {
     results.actor = hitTestResults.actor;
     results.actorCoordinates = hitTestResults.actorCoordinates;
@@ -621,45 +628,24 @@ bool HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgor
   return wasHit;
 }
 
-bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
+bool HitTest( const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
 {
   bool wasHit( false );
 
-  // Hit-test the regular on-stage actors
+  // Hit-test the regular on-scene actors
   if( !wasHit )
   {
-    RenderTaskList& taskList = stage.GetRenderTaskList();
-    LayerList& layerList = stage.GetLayerList();
-
-    wasHit = HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, results, hitTestInterface );
+    wasHit = HitTestForEachRenderTask( sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface );
   }
   return wasHit;
 }
 
-bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
+bool HitTest( const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results )
 {
   ActorTouchableCheck actorTouchableCheck;
-  return HitTest( stage, screenCoordinates, results, actorTouchableCheck );
+  return HitTest( sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck );
 }
 
-bool HitTest( Stage& stage, RenderTask& renderTask, const Vector2& screenCoordinates,
-              Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
-{
-  bool wasHit( false );
-  Results hitTestResults;
-
-  const Vector< RenderTaskList::Exclusive >& exclusives = stage.GetRenderTaskList().GetExclusivesList();
-  HitTestFunctionWrapper hitTestFunctionWrapper( func );
-  if( HitTestRenderTask( exclusives, stage, stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
-  {
-    results.actor = hitTestResults.actor;
-    results.actorCoordinates = hitTestResults.actorCoordinates;
-    wasHit = true;
-  }
-  return wasHit;
-}
-
-
 } // namespace HitTestAlgorithm
 
 } // namespace Internal