(HitTest) Ensure non-renderable actors are hittable even if outside the stencil 81/24581/2
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Wed, 16 Jul 2014 15:54:09 +0000 (16:54 +0100)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Wed, 16 Jul 2014 17:19:25 +0000 (18:19 +0100)
We do not expect to receive a hit outside the stencil area for a rendered actor. However, for
non-renderable actors, we cannot see these anyway and so these should still be hittable even if we
haven't hit the stencil area. This would allow controls like scroll-view to work with stencils
where we only want the stencil to affect the children of scroll-view.

Change-Id: Ic8e9d00a4125c94972ad2cfe298fae65313ad7dd
Signed-off-by: Adeel Kazmi <adeel.kazmi@samsung.com>
automated-tests/src/dali/utc-Dali-TouchProcessing.cpp
capi/dali/public-api/actors/actor.h
dali/internal/event/events/hit-test-algorithm-impl.cpp

index b0c3709..6f11247 100644 (file)
@@ -1289,3 +1289,215 @@ int UtcDaliTouchLeaveActorReadded(void)
 
   END_TEST;
 }
+
+int UtcDaliTouchStencil(void)
+{
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+
+  TextActor actor = TextActor::New();
+  actor.SetSize(100.0f, 100.0f);
+  actor.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  stage.Add(actor);
+
+  Actor stencil = Actor::New();
+  stencil.SetSize(50.0f, 50.0f);
+  stencil.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  stencil.SetDrawMode( DrawMode::STENCIL );
+  stage.Add(stencil);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Connect to actor's touched signal
+  SignalData data;
+  TouchEventFunctor functor( data );
+  actor.TouchedSignal().Connect( &application, functor );
+
+  // Emit an event within stencil area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  // Emit an event outside the stencil area but within the actor area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 60.0f, 60.0f ) ) );
+  DALI_TEST_EQUALS( false, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  END_TEST;
+}
+
+int UtcDaliTouchStencilInActorHierarchy(void)
+{
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+
+  TextActor parent = TextActor::New();
+  parent.SetSize(100.0f, 100.0f);
+  parent.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  stage.Add(parent);
+
+  TextActor child = TextActor::New();
+  child.SetSize(25.0f, 25.0f);
+  child.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  parent.Add(child);
+
+  Actor stencil = Actor::New();
+  stencil.SetSize(50.0f, 50.0f);
+  stencil.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  stencil.SetDrawMode( DrawMode::STENCIL );
+  stage.Add(stencil);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Connect to touch signals
+  SignalData parentData;
+  parent.TouchedSignal().Connect( &application, TouchEventFunctor(parentData) );
+  SignalData childData;
+  child.TouchedSignal().Connect( &application, TouchEventFunctor(childData) );
+
+  // Emit an event within stencil area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( false, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( true, childData.functorCalled, TEST_LOCATION );
+  parentData.Reset();
+  childData.Reset();
+
+  // Emit an event outside child area and within stencil area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Motion, Vector2( 40.0f, 40.0f ) ) );
+  DALI_TEST_EQUALS( true, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( false, childData.functorCalled, TEST_LOCATION );
+  parentData.Reset();
+  childData.Reset();
+
+  // Emit an event outside stencil are but within parent area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Motion, Vector2( 60.0f, 60.0f ) ) );
+  DALI_TEST_EQUALS( false, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( false, childData.functorCalled, TEST_LOCATION );
+  parentData.Reset();
+  childData.Reset();
+
+  // Readd actor (so that stencil is the first child)
+  stage.Remove(parent);
+  application.SendNotification();
+  application.Render();
+  stage.Add(parent);
+  application.SendNotification();
+  application.Render();
+
+  // Redo hit in same area...
+
+  // Emit an event within stencil area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Motion, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( false, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( true, childData.functorCalled, TEST_LOCATION );
+  parentData.Reset();
+  childData.Reset();
+
+  // Emit an event outside child area and within stencil area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Motion, Vector2( 40.0f, 40.0f ) ) );
+  DALI_TEST_EQUALS( true, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( false, childData.functorCalled, TEST_LOCATION );
+  parentData.Reset();
+  childData.Reset();
+
+  // Emit an event outside stencil are but within parent area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Motion, Vector2( 60.0f, 60.0f ) ) );
+  DALI_TEST_EQUALS( false, parentData.functorCalled, TEST_LOCATION );
+  DALI_TEST_EQUALS( false, childData.functorCalled, TEST_LOCATION );
+  parentData.Reset();
+  childData.Reset();
+
+  END_TEST;
+}
+
+int UtcDaliTouchMultipleStencils(void)
+{
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+
+  TextActor actor = TextActor::New();
+  actor.SetSize(100.0f, 100.0f);
+  actor.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  stage.Add(actor);
+
+  Actor stencil = Actor::New();
+  stencil.SetSize(50.0f, 50.0f);
+  stencil.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  stencil.SetDrawMode( DrawMode::STENCIL );
+  stage.Add(stencil);
+
+  Actor stencil2 = Actor::New();
+  stencil2.SetSize(50.0f, 50.0f);
+  stencil2.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  stencil2.SetDrawMode( DrawMode::STENCIL );
+  stencil2.SetPosition(50.0f, 50.0f);
+  stage.Add(stencil2);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Connect to actor's touched signal
+  SignalData data;
+  TouchEventFunctor functor( data );
+  actor.TouchedSignal().Connect( &application, functor );
+
+  // Emit an event within stencil area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  // Emit an event inside the second stencil area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 60.0f, 60.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  // Emit an event outside both stencil areas but within the actor area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 10.0f, 60.0f ) ) );
+  DALI_TEST_EQUALS( false, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  END_TEST;
+}
+
+int UtcDaliTouchStencilNonRenderableActor(void)
+{
+  TestApplication application;
+  Stage stage = Stage::GetCurrent();
+
+  Actor actor = Actor::New();
+  actor.SetSize(100.0f, 100.0f);
+  actor.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  stage.Add(actor);
+
+  Actor stencil = Actor::New();
+  stencil.SetSize(50.0f, 50.0f);
+  stencil.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+  stencil.SetDrawMode( DrawMode::STENCIL );
+  stage.Add(stencil);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render();
+
+  // Connect to actor's touched signal
+  SignalData data;
+  TouchEventFunctor functor( data );
+  actor.TouchedSignal().Connect( &application, functor );
+
+  // Emit an event within stencil area
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 10.0f, 10.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  // Emit an event outside the stencil area but within the actor area, we should have a hit!
+  application.ProcessEvent( GenerateSingleTouch( TouchPoint::Down, Vector2( 60.0f, 60.0f ) ) );
+  DALI_TEST_EQUALS( true, data.functorCalled, TEST_LOCATION );
+  data.Reset();
+
+  END_TEST;
+}
index 8649edd..a03259e 100644 (file)
@@ -156,12 +156,13 @@ typedef ActorContainer::const_iterator ActorConstIter; ///< Const iterator for D
  *
  *     Hit Priority of above Actor tree (all overlays): 1 - Lowest. 6 - Highest.
  *     @endcode
- *   - Stencil Actors can be used to influence the result of hits within a layer.
+ *   - Stencil Actors can be used to influence the result of hits on renderable actors within a layer.
  *     If a Stencil Actor exists on a layer and that Actor is marked visible then a successful
- *     hit can only take place in the area that the stencil Actor marks as visible.
+ *     hit on a renderable actor can only take place in the area that the stencil Actor marks as visible.
  *     The hit can be in any Stencil Actor in that layer, but must be in the region of one of them.
  *     Stencil Actor inheritance behaves as with rendering in that any child of a Stencil Actor will
  *     also be considered a Stencil Actor.
+ *     Non-renderable actors can be hit regardless of whether a stencil actor is hit or not.
  *
  * <i>Touch Event Delivery:</i>
  *
index 22fe563..51eeeec 100644 (file)
@@ -390,10 +390,11 @@ bool HitTestRenderTask( LayerList& layers,
               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