-//
-// Copyright (c) 2014 Samsung Electronics Co., Ltd.
-//
-// Licensed under the Flora License, Version 1.0 (the License);
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://floralicense.org/license/
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an AS IS BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
+/*
+ * Copyright (c) 2014 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
// CLASS HEADER
#include <dali/internal/event/events/hit-test-algorithm-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>
+#include <dali/internal/event/images/frame-buffer-image-impl.h>
#include <dali/internal/event/render-tasks/render-task-impl.h>
#include <dali/internal/event/render-tasks/render-task-list-impl.h>
};
/**
- * The function to be used in the hit-test algorithm to check whether the actor is touchable.
- * It is used by the touch event and gesture processor.
+ * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
*/
-bool IsActorTouchableFunction(Dali::Actor actor, Dali::HitTestAlgorithm::TraverseType type)
+struct HitTestFunctionWrapper : public HitTestInterface
{
- bool hittable = false;
+ /**
+ * Constructor
+ *
+ * @param[in] func HitTestFunction to call with an Actor handle.
+ */
+ HitTestFunctionWrapper( Dali::HitTestAlgorithm::HitTestFunction func )
+ : mFunc( func )
+ {
+ }
- switch (type)
+ virtual bool IsActorHittable( Actor* actor )
{
- case Dali::HitTestAlgorithm::CHECK_ACTOR:
- {
- if( GetImplementation(actor).GetTouchRequired() && // Does the Application or derived actor type require a touch event?
- GetImplementation(actor).IsHittable() ) // Is actor sensitive, visible and on the scene?
- {
- hittable = true;
- }
- break;
- }
- case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE:
- {
- if( 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.
- {
- hittable = true;
- }
- break;
- }
- default:
- {
- break;
- }
+ return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::CHECK_ACTOR );
}
- return hittable;
-}
+ virtual bool DescendActorHierarchy( Actor* 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;
+};
+
+/**
+ * Used in the hit-test algorithm to check whether the actor is touchable.
+ * It is used by the touch event processor.
+ */
+struct ActorTouchableCheck : public HitTestInterface
+{
+ virtual bool IsActorHittable( Actor* actor )
+ {
+ 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 )
+ {
+ 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();
+ }
+};
/**
* Recursively hit test all the actors, without crossing into other layers.
bool worldOverlay,
float& nearClippingPlane,
float& farClippingPlane,
- Dali::HitTestAlgorithm::HitTestFunction func,
+ HitTestInterface& hitCheck,
bool& stencilOnLayer,
bool& stencilHit,
bool parentIsStencil )
}
// If we are a stencil or hittable...
- if ( isStencil || func(Dali::Actor(&actor), Dali::HitTestAlgorithm::CHECK_ACTOR) ) // Is actor hittable
+ if ( isStencil || hitCheck.IsActorHittable( &actor ) )
{
Vector3 size( actor.GetCurrentSize() );
- if ( size.x > 0.0f && size.y > 0.0f && // Ensure the actor has a valid size.
+ if ( size.x > 0.0f && size.y > 0.0f && // Ensure the actor has a valid size.
actor.RaySphereTest( rayOrigin, rayDir ) ) // Perform quicker ray sphere test to see if our ray is close to the actor.
{
Vector4 hitPointLocal;
}
}
- // If there is a stencil on this layer and we've also registered a hit, then don't both searching any children
- if ( stencilHit && hit.actor )
+ // If we are a stencil (or a child of a stencil) and we have already ascertained that the stencil has been hit then there is no need to hit-test the children of this stencil-actor
+ if ( isStencil && stencilHit )
{
return hit;
}
if( actor.GetChildCount() > 0 )
{
childHit.distance = std::numeric_limits<float>::max();
- Dali::ActorContainer& children = actor.GetChildrenInternal();
+ 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 || func(*iter, Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE ) ) ) // Child is visible and sensitive, otherwise none of its children should be hittable.
+ ( isStencil || hitCheck.DescendActorHierarchy( &GetImplementation( *iter ) ) ) ) // We are a stencil OR we can descend into child hierarchy
{
- HitActor currentHit( HitTestWithinLayer( GetImplementation(*iter), rayOrigin, rayDir, worldOverlay, nearClippingPlane, farClippingPlane, func, stencilOnLayer, stencilHit, isStencil ) );
+ 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.
/**
* Returns true if the layer and all of the layer's parents are visible and sensitive.
*/
-inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, Dali::HitTestAlgorithm::HitTestFunction func )
+inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck )
{
bool hittable( true );
if(hittable)
{
Actor* actor( &layer );
+
+ // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
while ( actor && hittable )
{
- if ( !(func(Dali::Actor(actor), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE)) ) // Layer (or its Parent) is NOT visible and sensitive, so our layer is not either.
+ if ( ! hitCheck.DescendActorHierarchy( actor ) )
{
hittable = false;
break;
RenderTask& renderTask,
Vector2 screenCoordinates,
Results& results,
- Dali::HitTestAlgorithm::HitTestFunction func )
+ HitTestInterface& hitCheck )
{
if ( renderTask.IsHittable( screenCoordinates ) )
{
HitActor hit;
bool stencilOnLayer = false;
bool stencilHit = false;
+ bool layerConsumesHit = false;
const Vector2& stageSize = Stage::GetCurrent()->GetSize();
for (int i=layers.GetLayerCount()-1; i>=0 && !(hit.actor); --i)
stencilHit = false;
// Ensure layer is touchable (also checks whether ancestors are also touchable)
- if ( IsActuallyHittable ( *layer, screenCoordinates, stageSize, func ) )
+ if ( IsActuallyHittable ( *layer, screenCoordinates, stageSize, hitCheck ) )
{
// Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
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, func, stencilOnLayer, stencilHit, false );
+ hit = HitTestWithinLayer( *sourceActor, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
}
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, func, stencilOnLayer, stencilHit, false );
+ hit = HitTestWithinLayer( *layer, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
}
- // 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;
+ hit = previousHit;
+ }
+
+ // If this layer is set to consume the hit, then do not check any layers behind it
+ if ( hitCheck.DoesLayerConsumeHit( layer ) )
+ {
+ 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( LayerList& layers,
RenderTaskList& taskList,
const Vector2& screenCoordinates,
Results& results,
- Dali::HitTestAlgorithm::HitTestFunction func )
+ HitTestInterface& hitCheck )
{
RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
}
}
- if ( HitTestRenderTask( layers, renderTask, screenCoordinates, results, func ) )
+ if ( HitTestRenderTask( 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, func ) )
+ if ( HitTestRenderTask( 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;
- HitTestForEachRenderTask( layerList, taskList, screenCoordinates, hitTestResults, func );
-
- results.actor = hitTestResults.actor;
- results.actorCoordinates = hitTestResults.actorCoordinates;
+ HitTestFunctionWrapper hitTestFunctionWrapper( func );
+ if ( HitTestForEachRenderTask( 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 )
+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, IsActorTouchableFunction );
+ wasHit = HitTestForEachRenderTask( 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, IsActorTouchableFunction );
+ wasHit = HitTestForEachRenderTask( layerList, taskList, screenCoordinates, results, hitTestInterface );
}
+ return wasHit;
}
-void HitTest( Stage& stage, RenderTask& renderTask, const Vector2& screenCoordinates,
+bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
+{
+ ActorTouchableCheck actorTouchableCheck;
+ return HitTest( stage, 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;
- HitTestRenderTask( stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, func );
- results.actor = hitTestResults.actor;
- results.actorCoordinates = hitTestResults.actorCoordinates;
+
+ HitTestFunctionWrapper hitTestFunctionWrapper( func );
+ if ( HitTestRenderTask( stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
+ {
+ results.actor = hitTestResults.actor;
+ results.actorCoordinates = hitTestResults.actorCoordinates;
+ wasHit = true;
+ }
+ return wasHit;
}
} // namespace HitTestAlgorithm