// INTERNAL INCLUDES
#include <dali/integration-api/system-overlay.h>
+#include <dali/public-api/actors/layer.h>
#include <dali/public-api/math/vector2.h>
#include <dali/public-api/math/vector4.h>
#include <dali/integration-api/debug.h>
#include <dali/internal/event/actors/actor-impl.h>
#include <dali/internal/event/actors/camera-actor-impl.h>
+#include <dali/internal/event/actors/image-actor-impl.h>
#include <dali/internal/event/actors/layer-impl.h>
#include <dali/internal/event/actors/layer-list.h>
-#include <dali/internal/event/actors/renderable-actor-impl.h>
#include <dali/internal/event/common/system-overlay-impl.h>
#include <dali/internal/event/common/stage-impl.h>
#include <dali/internal/event/common/projection.h>
x( 0 ),
y( 0 ),
distance( std::numeric_limits<float>::max() ),
- overlay( false )
+ depth( std::numeric_limits<int>::min() )
{
-
}
Actor *actor; ///< the actor hit. (if actor hit, then initialised)
float x; ///< x position of hit (only valid if actor valid)
float y; ///< y position of hit (only valid if actor valid)
float distance; ///< distance from ray origin to hit actor
- bool overlay; ///< true if the hit actor is an overlay
+ int depth; ///< depth index of this actor
};
return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE );
}
+ virtual bool DoesLayerConsumeHit( Layer* layer )
+ {
+ // 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.
+ // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
+ return false;
+ }
+
Dali::HitTestAlgorithm::HitTestFunction mFunc;
};
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 )
+ {
+ return layer->IsTouchConsumed();
+ }
};
/**
+ * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
+ */
+bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
+ const RenderTask& renderTask,
+ const Vector< RenderTaskList::Exclusive >& exclusives )
+
+{
+ if ( exclusives.Size() )
+ {
+ for ( Vector< RenderTaskList::Exclusive >::Iterator exclusiveIt = exclusives.Begin(); exclusives.End() != exclusiveIt; ++exclusiveIt )
+ {
+ if ( exclusiveIt->renderTaskPtr != &renderTask )
+ {
+ if ( exclusiveIt->actorPtr == &actor )
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
* Recursively hit test all the actors, without crossing into other layers.
* This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
* Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
* of touch vector). The closest Hit-Tested Actor is that which is returned.
* Exceptions to this rule are:
- * - If the Actor is an overlay then it is considered closer than all previous
- * overlays encountered in the hit test traversal.
* - When comparing against renderable parents, if Actor is the same distance
* or closer than it's renderable parent, then it takes priority.
*/
HitActor HitTestWithinLayer( Actor& actor,
+ const RenderTask& renderTask,
+ const Vector< RenderTaskList::Exclusive >& exclusives,
const Vector4& rayOrigin,
const Vector4& rayDir,
- bool worldOverlay,
float& nearClippingPlane,
float& farClippingPlane,
HitTestInterface& hitCheck,
bool& stencilOnLayer,
bool& stencilHit,
- bool parentIsStencil )
+ bool parentIsStencil,
+ bool layerIs3d )
{
- worldOverlay |= actor.IsOverlay();
-
HitActor hit;
+ if ( IsActorExclusiveToAnotherRenderTask( actor, renderTask, exclusives ) )
+ {
+ return hit;
+ }
+
// Children should inherit the stencil draw mode
bool isStencil = parentIsStencil;
hit.x = hitPointLocal.x;
hit.y = hitPointLocal.y;
hit.distance = distance;
- hit.overlay = worldOverlay;
+ hit.depth = actor.GetHierarchyDepth() * Dali::Layer::TREE_DEPTH_MULTIPLIER;
+
+ if ( actor.GetRendererCount() > 0 )
+ {
+ //Get renderer with maximum depth
+ int rendererMaxDepth(actor.GetRendererAt( 0 ).Get()->GetDepthIndex());
+ for( unsigned int i(1); i<actor.GetRendererCount(); ++i)
+ {
+ int depth = actor.GetRendererAt( i ).Get()->GetDepthIndex();
+ if( depth > rendererMaxDepth )
+ {
+ rendererMaxDepth = depth;
+ }
+ }
+ hit.depth += rendererMaxDepth;
+ }
}
}
}
if( actor.GetChildCount() > 0 )
{
childHit.distance = std::numeric_limits<float>::max();
- Dali::ActorContainer& children = actor.GetChildrenInternal();
+ childHit.depth = std::numeric_limits<int>::min();
+ ActorContainer& children = actor.GetChildrenInternal();
// Hit test ALL children and calculate their distance.
bool parentIsRenderable = actor.IsRenderable();
- for (Dali::ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
+ for( ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter )
{
// Descend tree only if...
- if ( !iter->IsLayer() && // Child is NOT a layer, hit testing current layer only or Child is not a layer and we've inherited the stencil draw mode
- ( isStencil || hitCheck.DescendActorHierarchy( &GetImplementation( *iter ) ) ) ) // We are a stencil OR we can descend into child hierarchy
+ if ( !(*iter)->IsLayer() && // Child is NOT a layer, hit testing current layer only or Child is not a layer and we've inherited the stencil draw mode
+ ( isStencil || hitCheck.DescendActorHierarchy( ( *iter ).Get() ) ) ) // We are a stencil OR we can descend into child hierarchy
{
- HitActor currentHit( HitTestWithinLayer( GetImplementation(*iter), rayOrigin, rayDir, worldOverlay, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, isStencil ) );
-
- // If Current child is an overlay, then it takes priority.
- // If it is not an overlay, and the previously hit sibling is also not an overlay, then closest takes priority.
- // (last overlay sibling has priority as is rendered on top)
- if ( currentHit.distance >= 0.f && (currentHit.overlay || (!childHit.overlay && currentHit.distance < childHit.distance) ) )
+ HitActor currentHit( HitTestWithinLayer( (*iter->Get()),
+ renderTask,
+ exclusives,
+ rayOrigin,
+ rayDir,
+ nearClippingPlane,
+ farClippingPlane,
+ hitCheck,
+ stencilOnLayer,
+ stencilHit,
+ isStencil,
+ layerIs3d) );
+
+ bool updateChildHit = false;
+ if ( currentHit.distance >= 0.0f )
{
- if ( !parentIsRenderable )
+ if( layerIs3d )
{
- // If our parent is not renderable, then child should be hit regardless of distance.
- childHit = currentHit;
+ updateChildHit = ( ( currentHit.depth > childHit.depth ) ||
+ ( ( currentHit.depth == childHit.depth ) && ( currentHit.distance < childHit.distance ) ) );
}
- else if ( currentHit.overlay || (!hit.overlay && currentHit.distance <= hit.distance) )
+ else
{
- // If our parent is renderable, then child should only be hit if it is an overlay, or if it is closer than a non-overlay.
- // (child overlay has priority as is rendered on top of it's parent)
- childHit = currentHit;
+ updateChildHit = currentHit.depth >= childHit.depth;
}
}
+
+ if ( updateChildHit )
+ {
+ if( !parentIsRenderable || currentHit.depth > hit.depth ||
+ ( layerIs3d && ( currentHit.depth == hit.depth && currentHit.distance < hit.distance )) )
+ {
+ childHit = currentHit;
+ }
+ }
}
}
}
/**
* Hit test a RenderTask
*/
-bool HitTestRenderTask( LayerList& layers,
+bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
+ Stage& stage,
+ LayerList& layers,
RenderTask& renderTask,
Vector2 screenCoordinates,
Results& results,
HitActor hit;
bool stencilOnLayer = false;
bool stencilHit = false;
- const Vector2& stageSize = Stage::GetCurrent()->GetSize();
+ bool layerConsumesHit = false;
+
+ const Vector2& stageSize = stage.GetSize();
for (int i=layers.GetLayerCount()-1; i>=0 && !(hit.actor); --i)
{
Layer* layer( layers.GetLayer(i) );
-
HitActor previousHit = hit;
stencilOnLayer = false;
stencilHit = false;
if ( sourceActorDepth == static_cast<unsigned int>(i) )
{
// Recursively hit test the source actor & children, without crossing into other layers.
- hit = HitTestWithinLayer( *sourceActor, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
+ hit = HitTestWithinLayer( *sourceActor,
+ renderTask,
+ exclusives,
+ results.rayOrigin,
+ results.rayDirection,
+ nearClippingPlane,
+ farClippingPlane,
+ hitCheck,
+ stencilOnLayer,
+ stencilHit,
+ false,
+ layer->GetBehavior() == Dali::Layer::LAYER_3D);
}
else if ( IsWithinSourceActors( *sourceActor, *layer ) )
{
// Recursively hit test all the actors, without crossing into other layers.
- hit = HitTestWithinLayer( *layer, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
+ hit = HitTestWithinLayer( *layer,
+ renderTask,
+ exclusives,
+ results.rayOrigin,
+ results.rayDirection,
+ nearClippingPlane,
+ farClippingPlane,
+ hitCheck,
+ stencilOnLayer,
+ stencilHit,
+ false,
+ layer->GetBehavior() == Dali::Layer::LAYER_3D);
}
- // If a stencil on this layer hasn't been hit, then discard hit results for this layer
- if ( stencilOnLayer && !stencilHit )
+
+ // If a stencil on this layer hasn't been hit, then discard hit results for this layer if our current hit actor is renderable
+ if ( stencilOnLayer && !stencilHit &&
+ hit.actor && hit.actor->IsRenderable() )
+ {
+ hit = previousHit;
+ }
+
+ // If this layer is set to consume the hit, then do not check any layers behind it
+ if ( hitCheck.DoesLayerConsumeHit( layer ) )
{
- hit = previousHit;
+ layerConsumesHit = true;
+ break;
}
}
}
results.actorCoordinates.y = hit.y;
return true; // Success
}
+ else if ( layerConsumesHit )
+ {
+ return true; // Also success if layer is consuming the hit
+ }
}
}
}
/**
* Iterate through RenderTaskList and perform hit test.
+ *
+ * @return true if we have a hit, false otherwise
*/
-
-void HitTestForEachRenderTask( LayerList& layers,
+bool HitTestForEachRenderTask( Stage& stage,
+ LayerList& layers,
RenderTaskList& taskList,
const Vector2& screenCoordinates,
Results& results,
RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
+ const Vector< RenderTaskList::Exclusive >& exclusives = taskList.GetExclusivesList();
+
// Check onscreen tasks before offscreen ones, hit test order should be reverse of draw order (see ProcessRenderTasks() where offscreen tasks are drawn first).
// on screen
}
}
- if ( HitTestRenderTask( layers, renderTask, screenCoordinates, results, hitCheck ) )
+ if ( HitTestRenderTask( exclusives, stage, layers, renderTask, screenCoordinates, results, hitCheck ) )
{
- // Exit when an actor is hit
- return; // don't bother checking off screen tasks
+ // 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
}
}
continue;
}
- if ( HitTestRenderTask( layers, renderTask, screenCoordinates, results, hitCheck ) )
+ if ( HitTestRenderTask( exclusives, stage, layers, renderTask, screenCoordinates, results, hitCheck ) )
{
- // Exit when an actor is hit
- break;
+ // Return true when an actor is hit (or a layer in our render-task consumes the hit)
+ return true;
}
}
}
+ return false;
}
} // unnamed namespace
-void HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
+bool HitTest( Stage& stage, 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();
Results hitTestResults;
HitTestFunctionWrapper hitTestFunctionWrapper( func );
- HitTestForEachRenderTask( layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper );
-
- results.actor = hitTestResults.actor;
- results.actorCoordinates = hitTestResults.actorCoordinates;
+ if ( HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
+ {
+ results.actor = hitTestResults.actor;
+ results.actorCoordinates = hitTestResults.actorCoordinates;
+ wasHit = true;
+ }
+ return wasHit;
}
-void HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
+bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
{
+ bool wasHit( false );
+
// Hit-test the system-overlay actors first
SystemOverlay* systemOverlay = stage.GetSystemOverlayInternal();
RenderTaskList& overlayTaskList = systemOverlay->GetOverlayRenderTasks();
LayerList& overlayLayerList = systemOverlay->GetLayerList();
- HitTestForEachRenderTask( overlayLayerList, overlayTaskList, screenCoordinates, results, hitTestInterface );
+ wasHit = HitTestForEachRenderTask( stage, overlayLayerList, overlayTaskList, screenCoordinates, results, hitTestInterface );
}
// Hit-test the regular on-stage actors
- if ( !results.actor )
+ if ( !wasHit )
{
RenderTaskList& taskList = stage.GetRenderTaskList();
LayerList& layerList = stage.GetLayerList();
- HitTestForEachRenderTask( layerList, taskList, screenCoordinates, results, hitTestInterface );
+ wasHit = HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, results, hitTestInterface );
}
+ return wasHit;
}
-void HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
+bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
{
ActorTouchableCheck actorTouchableCheck;
- HitTest( stage, screenCoordinates, results, actorTouchableCheck );
+ return HitTest( stage, screenCoordinates, results, actorTouchableCheck );
}
-void HitTest( Stage& stage, RenderTask& renderTask, const Vector2& screenCoordinates,
+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 );
- HitTestRenderTask( stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper );
- results.actor = hitTestResults.actor;
- results.actorCoordinates = hitTestResults.actorCoordinates;
+ if ( HitTestRenderTask( exclusives, stage, stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
+ {
+ results.actor = hitTestResults.actor;
+ results.actorCoordinates = hitTestResults.actorCoordinates;
+ wasHit = true;
+ }
+ return wasHit;
}
} // namespace HitTestAlgorithm