From 587be28c9be07d7e707b24e5cfad42dcf4a873e8 Mon Sep 17 00:00:00 2001 From: "joogab.yun" Date: Wed, 6 Sep 2023 16:44:52 +0900 Subject: [PATCH] Touch and Hover event propagrated by geometry way. This is similar to how Android works. 1. Events are propagated based on geometry. 2. TouchEvent - If there is an actor who consumes, only the actor who consumes will receive the event that comes after. 3. Gesture will be a way for apps to receive and recognize touch events in the future. Change-Id: Iac58129b729446d3dffa54ad0aacc8d5e8135f19 --- automated-tests/src/dali/CMakeLists.txt | 3 + automated-tests/src/dali/utc-Dali-Actor.cpp | 2196 ++++++++++++++++-- .../src/dali/utc-Dali-GeoHitTestAlgorithm.cpp | 892 ++++++++ .../src/dali/utc-Dali-GeoHoverProcessing.cpp | 1387 ++++++++++++ .../src/dali/utc-Dali-GeoTouchProcessing.cpp | 2378 ++++++++++++++++++++ automated-tests/src/dali/utc-Dali-Scene.cpp | 102 + dali/devel-api/events/hit-test-algorithm.cpp | 4 +- dali/devel-api/events/hit-test-algorithm.h | 3 +- dali/integration-api/scene.cpp | 10 + dali/integration-api/scene.h | 14 + dali/internal/event/actors/actor-impl.cpp | 1 + dali/internal/event/actors/actor-impl.h | 18 + dali/internal/event/common/scene-impl.cpp | 11 + dali/internal/event/common/scene-impl.h | 11 + dali/internal/event/events/gesture-processor.cpp | 2 +- .../event/events/hit-test-algorithm-impl.cpp | 262 ++- .../event/events/hit-test-algorithm-impl.h | 27 +- .../event/events/hover-event-processor.cpp | 220 +- dali/internal/event/events/hover-event-processor.h | 12 +- .../event/events/touch-event-processor.cpp | 453 +++- dali/internal/event/events/touch-event-processor.h | 2 + 21 files changed, 7561 insertions(+), 447 deletions(-) create mode 100644 automated-tests/src/dali/utc-Dali-GeoHitTestAlgorithm.cpp create mode 100644 automated-tests/src/dali/utc-Dali-GeoHoverProcessing.cpp create mode 100644 automated-tests/src/dali/utc-Dali-GeoTouchProcessing.cpp diff --git a/automated-tests/src/dali/CMakeLists.txt b/automated-tests/src/dali/CMakeLists.txt index 64aa025..c115058 100644 --- a/automated-tests/src/dali/CMakeLists.txt +++ b/automated-tests/src/dali/CMakeLists.txt @@ -34,6 +34,9 @@ SET(TC_SOURCES utc-Dali-FrameCallbackInterface.cpp utc-Dali-FreeList.cpp utc-Dali-Geometry.cpp + utc-Dali-GeoHitTestAlgorithm.cpp + utc-Dali-GeoHoverProcessing.cpp + utc-Dali-GeoTouchProcessing.cpp utc-Dali-GestureDetector.cpp utc-Dali-Handle.cpp utc-Dali-Hash.cpp diff --git a/automated-tests/src/dali/utc-Dali-Actor.cpp b/automated-tests/src/dali/utc-Dali-Actor.cpp index 9a027fc..1cb1723 100644 --- a/automated-tests/src/dali/utc-Dali-Actor.cpp +++ b/automated-tests/src/dali/utc-Dali-Actor.cpp @@ -128,6 +128,20 @@ static void ResetTouchCallbacks() gTouchCallBackCalled3 = false; } +static void ResetTouchCallbacks(TestApplication& application) +{ + // reset touch + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::UP); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + application.ProcessEvent(touchEvent); + + ResetTouchCallbacks(); +} + static bool TestCallback3(Actor actor, const HoverEvent& event) { gHoverCallBackCalled = true; @@ -3148,6 +3162,37 @@ int UtcDaliActorTouchedSignal(void) END_TEST; } +int UtcDaliActorGeoTouchedSignal(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); + + // get the root layer + Actor actor = application.GetScene().GetRootLayer(); + DALI_TEST_CHECK(gTouchCallBackCalled == false); + + application.SendNotification(); + application.Render(); + + // connect to its touch signal + actor.TouchedSignal().Connect(TestTouchCallback); + + // simulate a touch event in the middle of the screen + Vector2 touchPoint(application.GetScene().GetSize() * 0.5); + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(touchPoint.x, touchPoint.y)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + application.ProcessEvent(touchEvent); + + DALI_TEST_CHECK(gTouchCallBackCalled == true); + END_TEST; +} + int UtcDaliActorHoveredSignal(void) { TestApplication application; @@ -3404,6 +3449,90 @@ int UtcDaliActorHitTest(void) END_TEST; } +int UtcDaliActorGeoHitTest(void) +{ + struct HitTestData + { + public: + HitTestData(const Vector3& scale, const Vector2& touchPoint, bool result) + : mScale(scale), + mTouchPoint(touchPoint), + mResult(result) + { + } + + Vector3 mScale; + Vector2 mTouchPoint; + bool mResult; + }; + + TestApplication application; + tet_infoline(" UtcDaliActorHitTest"); + + // Fill a vector with different hit tests. + struct HitTestData* hitTestData[] = { + // scale touch point result + new HitTestData(Vector3(100.f, 100.f, 1.f), Vector2(289.f, 400.f), true), // touch point close to the right edge (inside) + new HitTestData(Vector3(100.f, 100.f, 1.f), Vector2(291.f, 400.f), false), // touch point close to the right edge (outside) + new HitTestData(Vector3(110.f, 100.f, 1.f), Vector2(291.f, 400.f), true), // same point as above with a wider scale. Should be inside. + new HitTestData(Vector3(100.f, 100.f, 1.f), Vector2(200.f, 451.f), false), // touch point close to the down edge (outside) + new HitTestData(Vector3(100.f, 110.f, 1.f), Vector2(200.f, 451.f), true), // same point as above with a wider scale. Should be inside. + NULL, + }; + + // get the root layer + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + application.GetScene().Add(actor); + application.GetScene().SetGeometryHittestEnabled(true); + + ResetTouchCallbacks(application); + + unsigned int index = 0; + while(NULL != hitTestData[index]) + { + actor.SetProperty(Actor::Property::SIZE, Vector2(1.f, 1.f)); + actor.SetProperty(Actor::Property::SCALE, Vector3(hitTestData[index]->mScale.x, hitTestData[index]->mScale.y, hitTestData[index]->mScale.z)); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + + DALI_TEST_CHECK(!gTouchCallBackCalled); + + // connect to its touch signal + actor.TouchedSignal().Connect(TestTouchCallback); + + Dali::Integration::Point point; + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(hitTestData[index]->mTouchPoint.x, hitTestData[index]->mTouchPoint.y)); + Dali::Integration::TouchEvent event; + event.AddPoint(point); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + application.ProcessEvent(event); + + DALI_TEST_CHECK(gTouchCallBackCalled == hitTestData[index]->mResult); + + if(gTouchCallBackCalled != hitTestData[index]->mResult) + tet_printf("Test failed:\nScale %f %f %f\nTouchPoint %f, %f\nResult %d\n", + hitTestData[index]->mScale.x, + hitTestData[index]->mScale.y, + hitTestData[index]->mScale.z, + hitTestData[index]->mTouchPoint.x, + hitTestData[index]->mTouchPoint.y, + hitTestData[index]->mResult); + + ResetTouchCallbacks(application); + ++index; + } + END_TEST; +} + int UtcDaliActorSetDrawMode(void) { TestApplication application; @@ -5692,6 +5821,142 @@ int UtcDaliActorRaiseLower(void) END_TEST; } +int UtcDaliActorGeoTouchRaiseLower(void) +{ + tet_infoline("UtcDaliActor Raise and Lower test\n"); + + TestApplication application; + + Debug::Filter::SetGlobalLogLevel(Debug::Verbose); + Debug::Filter::EnableGlobalTrace(); + + Integration::Scene stage(application.GetScene()); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + stage.Add(actorA); + stage.Add(actorB); + stage.Add(actorC); + + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("Testing Raising of Actor\n"); + + int preActorOrder(0); + int postActorOrder(0); + + Property::Value value = actorB.GetProperty(Dali::DevelActor::Property::SIBLING_ORDER); + value.Get(preActorOrder); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorB.Raise(); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + + // Ensure sort order is calculated before next touch event + application.SendNotification(); + + value = actorB.GetProperty(Dali::DevelActor::Property::SIBLING_ORDER); + value.Get(postActorOrder); + + tet_printf("Raised ActorB from (%d) to (%d) \n", preActorOrder, postActorOrder); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("Testing Lowering of Actor\n"); + + value = actorB.GetProperty(Dali::DevelActor::Property::SIBLING_ORDER); + value.Get(preActorOrder); + + orderChangedSignal = false; + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorB.Lower(); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + + application.SendNotification(); // ensure sort order calculated before next touch event + + value = actorB.GetProperty(Dali::DevelActor::Property::SIBLING_ORDER); + value.Get(postActorOrder); + + tet_printf("Lowered ActorB from (%d) to (%d) \n", preActorOrder, postActorOrder); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(application); + + Debug::Filter::DisableGlobalTrace(); + Debug::Filter::SetGlobalLogLevel(Debug::NoLogging); + + END_TEST; +} + int UtcDaliActorRaiseToTopLowerToBottom(void) { tet_infoline("UtcDaliActorRaiseToTop and LowerToBottom test \n"); @@ -5923,9 +6188,9 @@ int UtcDaliActorRaiseToTopLowerToBottom(void) END_TEST; } -int UtcDaliActorRaiseAbove(void) +int UtcDaliActorGeoTouchRaiseToTopLowerToBottom(void) { - tet_infoline("UtcDaliActor RaiseToAbove test \n"); + tet_infoline("UtcDaliActorRaiseToTop and LowerToBottom test \n"); TestApplication application; @@ -5935,10 +6200,33 @@ int UtcDaliActorRaiseAbove(void) Actor actorB = Actor::New(); Actor actorC = Actor::New(); - actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); - actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); - - actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + // Set up renderers to add to Actors, float value 1, 2, 3 assigned to each + // enables checking of which actor the uniform is assigned too + Shader shaderA = CreateShader(); + shaderA.RegisterProperty("uRendererColor", 1.f); + + Shader shaderB = CreateShader(); + shaderB.RegisterProperty("uRendererColor", 2.f); + + Shader shaderC = CreateShader(); + shaderC.RegisterProperty("uRendererColor", 3.f); + + Geometry geometry = CreateQuadGeometry(); + + // Add renderers to Actors so ( uRendererColor, 1 ) is A, ( uRendererColor, 2 ) is B, and ( uRendererColor, 3 ) is C, + Renderer rendererA = Renderer::New(geometry, shaderA); + actorA.AddRenderer(rendererA); + + Renderer rendererB = Renderer::New(geometry, shaderB); + actorB.AddRenderer(rendererB); + + Renderer rendererC = Renderer::New(geometry, shaderC); + actorC.AddRenderer(rendererC); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); @@ -5957,11 +6245,36 @@ int UtcDaliActorRaiseAbove(void) stage.Add(actorB); stage.Add(actorC); - ResetTouchCallbacks(); + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + + // Set up gl abstraction trace so can query the set uniform order + TestGlAbstraction& glAbstraction = application.GetGlAbstraction(); + glAbstraction.EnableSetUniformCallTrace(true); + glAbstraction.ResetSetUniformCallStack(); + + TraceCallStack& glSetUniformStack = glAbstraction.GetSetUniformTrace(); application.SendNotification(); application.Render(); + tet_printf("Trace Output:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + int indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + int indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + int indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + bool CBA = (indexC > indexB) && (indexB > indexA); + + DALI_TEST_EQUALS(CBA, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); @@ -5972,11 +6285,6 @@ int UtcDaliActorRaiseAbove(void) actorB.TouchedSignal().Connect(TestTouchCallback2); actorC.TouchedSignal().Connect(TestTouchCallback3); - bool orderChangedSignal(false); - Actor orderChangedActor; - ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); - DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); - Dali::Integration::Point point; point.SetDeviceId(1); point.SetState(PointState::DOWN); @@ -5990,51 +6298,131 @@ int UtcDaliActorRaiseAbove(void) DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); - tet_printf("Raise actor B Above Actor C\n"); + tet_printf("RaiseToTop ActorA\n"); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorB.RaiseAbove(actorC); + actorA.RaiseToTop(); DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); - DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); + + application.SendNotification(); // ensure sorting order is calculated before next touch event + + application.ProcessEvent(touchEvent); + + glSetUniformStack.Reset(); - // Ensure sorting happens at end of Core::ProcessEvents() before next touch application.SendNotification(); + application.Render(); + + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + tet_infoline("Testing A above C and B at bottom\n"); + bool ACB = (indexA > indexC) && (indexC > indexB); + + DALI_TEST_EQUALS(ACB, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("RaiseToTop ActorB\n"); + + orderChangedSignal = false; + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorB.RaiseToTop(); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + + application.SendNotification(); // Ensure sort order is calculated before next touch event + application.ProcessEvent(touchEvent); + glSetUniformStack.Reset(); + + application.SendNotification(); + application.Render(); + + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + tet_infoline("Testing B above A and C at bottom\n"); + bool BAC = (indexB > indexA) && (indexA > indexC); + + DALI_TEST_EQUALS(BAC, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); - tet_printf("Raise actor A Above Actor B\n"); + tet_printf("LowerToBottom ActorA then ActorB leaving Actor C at Top\n"); orderChangedSignal = false; DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorA.RaiseAbove(actorB); + actorA.LowerToBottom(); DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); - // Ensure sorting happens at end of Core::ProcessEvents() before next touch application.SendNotification(); + application.Render(); - application.ProcessEvent(touchEvent); // process a touch event on ordered actors. + orderChangedSignal = false; - DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorB.LowerToBottom(); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + glSetUniformStack.Reset(); + + application.SendNotification(); + application.Render(); + + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + tet_infoline("Testing C above A and B at bottom\n"); + bool CAB = (indexC > indexA) && (indexA > indexB); + + DALI_TEST_EQUALS(CAB, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); - DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); END_TEST; } -int UtcDaliActorRaiseAbove2(void) +int UtcDaliActorRaiseAbove(void) { - tet_infoline("UtcDaliActor RaiseToAbove test using SIBLING_ORDER property\n"); + tet_infoline("UtcDaliActor RaiseToAbove test \n"); TestApplication application; @@ -6104,8 +6492,7 @@ int UtcDaliActorRaiseAbove2(void) tet_printf("Raise actor B Above Actor C\n"); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - int newOrder = actorC[DevelActor::Property::SIBLING_ORDER]; - actorB[DevelActor::Property::SIBLING_ORDER] = newOrder; + actorB.RaiseAbove(actorC); DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); @@ -6124,8 +6511,7 @@ int UtcDaliActorRaiseAbove2(void) orderChangedSignal = false; DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - newOrder = actorB[DevelActor::Property::SIBLING_ORDER]; - actorA[DevelActor::Property::SIBLING_ORDER] = newOrder; + actorA.RaiseAbove(actorB); DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); @@ -6143,41 +6529,18 @@ int UtcDaliActorRaiseAbove2(void) END_TEST; } -int UtcDaliActorLowerBelow(void) +int UtcDaliActorGeoTouchRaiseAbove(void) { - tet_infoline("UtcDaliActor LowerBelow test \n"); + tet_infoline("UtcDaliActor RaiseToAbove test \n"); TestApplication application; Integration::Scene stage(application.GetScene()); - // Set up renderers to add to Actors, float value 1, 2, 3 assigned to each - // enables checking of which actor the uniform is assigned too - Shader shaderA = CreateShader(); - shaderA.RegisterProperty("uRendererColor", 1.f); - - Shader shaderB = CreateShader(); - shaderB.RegisterProperty("uRendererColor", 2.f); - - Shader shaderC = CreateShader(); - shaderC.RegisterProperty("uRendererColor", 3.f); - Actor actorA = Actor::New(); Actor actorB = Actor::New(); Actor actorC = Actor::New(); - // Add renderers to Actors so ( uRendererColor, 1 ) is A, ( uRendererColor, 2 ) is B, and ( uRendererColor, 3 ) is C, - Geometry geometry = CreateQuadGeometry(); - - Renderer rendererA = Renderer::New(geometry, shaderA); - actorA.AddRenderer(rendererA); - - Renderer rendererB = Renderer::New(geometry, shaderB); - actorB.AddRenderer(rendererB); - - Renderer rendererC = Renderer::New(geometry, shaderC); - actorC.AddRenderer(rendererC); - actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); @@ -6196,46 +6559,16 @@ int UtcDaliActorLowerBelow(void) actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); - Actor container = Actor::New(); - container.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); - container.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS); - stage.Add(container); - - container.Add(actorA); - container.Add(actorB); - container.Add(actorC); - - ResetTouchCallbacks(); - - // Connect ChildOrderChangedSignal - bool orderChangedSignal(false); - Actor orderChangedActor; - ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); - DevelActor::ChildOrderChangedSignal(container).Connect(&application, f); - - // Set up gl abstraction trace so can query the set uniform order - TestGlAbstraction& glAbstraction = application.GetGlAbstraction(); - glAbstraction.EnableSetUniformCallTrace(true); - glAbstraction.ResetSetUniformCallStack(); - TraceCallStack& glSetUniformStack = glAbstraction.GetSetUniformTrace(); + stage.Add(actorA); + stage.Add(actorB); + stage.Add(actorC); - glAbstraction.ResetSetUniformCallStack(); + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); application.SendNotification(); application.Render(); - tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); - - // Test order of uniforms in stack - int indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); - int indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); - int indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); - - tet_infoline("Testing C above B and A at bottom\n"); - bool CBA = (indexC > indexB) && (indexB > indexA); - - DALI_TEST_EQUALS(CBA, true, TEST_LOCATION); - DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); @@ -6246,6 +6579,11 @@ int UtcDaliActorLowerBelow(void) actorB.TouchedSignal().Connect(TestTouchCallback2); actorC.TouchedSignal().Connect(TestTouchCallback3); + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + Dali::Integration::Point point; point.SetDeviceId(1); point.SetState(PointState::DOWN); @@ -6253,44 +6591,135 @@ int UtcDaliActorLowerBelow(void) Dali::Integration::TouchEvent touchEvent; touchEvent.AddPoint(point); - tet_infoline("UtcDaliActor Test Set up completed \n"); - application.ProcessEvent(touchEvent); DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); - tet_printf("Lower actor C below Actor B ( actor B and A on same level due to insertion order) so C is below both \n"); + tet_printf("Raise actor B Above Actor C\n"); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorC.LowerBelow(actorB); + actorB.RaiseAbove(actorC); DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); - DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); // Ensure sorting happens at end of Core::ProcessEvents() before next touch application.SendNotification(); - application.Render(); + application.ProcessEvent(touchEvent); - application.ProcessEvent(touchEvent); // touch event + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - glSetUniformStack.Reset(); + ResetTouchCallbacks(application); - application.SendNotification(); - application.Render(); + tet_printf("Raise actor A Above Actor B\n"); - tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + orderChangedSignal = false; - // Test order of uniforms in stack - indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); - indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); - indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.RaiseAbove(actorB); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); - tet_infoline("Testing render order is A, C, B"); - DALI_TEST_EQUALS(indexC > indexA, true, TEST_LOCATION); - DALI_TEST_EQUALS(indexB > indexC, true, TEST_LOCATION); + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + + application.ProcessEvent(touchEvent); // process a touch event on ordered actors. + + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(application); + + END_TEST; +} + + +int UtcDaliActorRaiseAbove2(void) +{ + tet_infoline("UtcDaliActor RaiseToAbove test using SIBLING_ORDER property\n"); + + TestApplication application; + + Integration::Scene stage(application.GetScene()); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + stage.Add(actorA); + stage.Add(actorB); + stage.Add(actorC); + + ResetTouchCallbacks(); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(); + + tet_printf("Raise actor B Above Actor C\n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + int newOrder = actorC[DevelActor::Property::SIBLING_ORDER]; + actorB[DevelActor::Property::SIBLING_ORDER] = newOrder; + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.ProcessEvent(touchEvent); DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); @@ -6298,74 +6727,145 @@ int UtcDaliActorLowerBelow(void) ResetTouchCallbacks(); - tet_printf("Lower actor C below Actor A leaving B on top\n"); + tet_printf("Raise actor A Above Actor B\n"); orderChangedSignal = false; DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorC.LowerBelow(actorA); + newOrder = actorB[DevelActor::Property::SIBLING_ORDER]; + actorA[DevelActor::Property::SIBLING_ORDER] = newOrder; DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); - DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); // Ensure sorting happens at end of Core::ProcessEvents() before next touch application.SendNotification(); + + application.ProcessEvent(touchEvent); // process a touch event on ordered actors. + + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(); + + END_TEST; +} + +int UtcDaliActorGeoTouchRaiseAbove2(void) +{ + tet_infoline("UtcDaliActor RaiseToAbove test using SIBLING_ORDER property\n"); + + TestApplication application; + + Integration::Scene stage(application.GetScene()); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + stage.Add(actorA); + stage.Add(actorB); + stage.Add(actorC); + + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); + + application.SendNotification(); application.Render(); + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + application.ProcessEvent(touchEvent); - glSetUniformStack.Reset(); + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); - application.Render(); - tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + ResetTouchCallbacks(application); - // Test order of uniforms in stack - indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); - indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); - indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + tet_printf("Raise actor B Above Actor C\n"); - DALI_TEST_EQUALS(indexA > indexC, true, TEST_LOCATION); - DALI_TEST_EQUALS(indexB > indexA, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + int newOrder = actorC[DevelActor::Property::SIBLING_ORDER]; + actorB[DevelActor::Property::SIBLING_ORDER] = newOrder; + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.ProcessEvent(touchEvent); DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); - tet_printf("Lower actor B below Actor C leaving A on top\n"); + tet_printf("Raise actor A Above Actor B\n"); orderChangedSignal = false; DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorB.LowerBelow(actorC); + newOrder = actorB[DevelActor::Property::SIBLING_ORDER]; + actorA[DevelActor::Property::SIBLING_ORDER] = newOrder; DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); - DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); // Ensure sorting happens at end of Core::ProcessEvents() before next touch application.SendNotification(); - application.Render(); - - application.ProcessEvent(touchEvent); - - glSetUniformStack.Reset(); - application.Render(); - tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + application.ProcessEvent(touchEvent); // process a touch event on ordered actors. - // Test order of uniforms in stack - indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); - indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); - indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - DALI_TEST_EQUALS(indexC > indexB, true, TEST_LOCATION); - DALI_TEST_EQUALS(indexA > indexC, true, TEST_LOCATION); + ResetTouchCallbacks(application); END_TEST; } -int UtcDaliActorLowerBelow2(void) +int UtcDaliActorLowerBelow(void) { - tet_infoline("UtcDaliActor LowerBelow test using SIBLING_ORDER property\n"); + tet_infoline("UtcDaliActor LowerBelow test \n"); TestApplication application; @@ -6486,7 +6986,7 @@ int UtcDaliActorLowerBelow2(void) tet_printf("Lower actor C below Actor B ( actor B and A on same level due to insertion order) so C is below both \n"); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorC[DevelActor::Property::SIBLING_ORDER] = 1; + actorC.LowerBelow(actorB); DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); @@ -6523,7 +7023,7 @@ int UtcDaliActorLowerBelow2(void) orderChangedSignal = false; DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorC[DevelActor::Property::SIBLING_ORDER] = 0; + actorC.LowerBelow(actorA); DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); @@ -6557,7 +7057,7 @@ int UtcDaliActorLowerBelow2(void) orderChangedSignal = false; DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorB[DevelActor::Property::SIBLING_ORDER] = 0; + actorB.LowerBelow(actorC); DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); @@ -6583,39 +7083,1064 @@ int UtcDaliActorLowerBelow2(void) END_TEST; } -int UtcDaliActorRaiseAboveDifferentParentsN(void) +int UtcDaliActorGeoTouchLowerBelow(void) { - tet_infoline("UtcDaliActor RaiseToAbove test with actor and target actor having different parents \n"); + tet_infoline("UtcDaliActor LowerBelow test \n"); TestApplication application; Integration::Scene stage(application.GetScene()); - Actor parentA = Actor::New(); - Actor parentB = Actor::New(); - parentA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); - parentA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); - parentB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); - parentB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + // Set up renderers to add to Actors, float value 1, 2, 3 assigned to each + // enables checking of which actor the uniform is assigned too + Shader shaderA = CreateShader(); + shaderA.RegisterProperty("uRendererColor", 1.f); - parentA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); - parentA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + Shader shaderB = CreateShader(); + shaderB.RegisterProperty("uRendererColor", 2.f); + + Shader shaderC = CreateShader(); + shaderC.RegisterProperty("uRendererColor", 3.f); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); + + // Add renderers to Actors so ( uRendererColor, 1 ) is A, ( uRendererColor, 2 ) is B, and ( uRendererColor, 3 ) is C, + Geometry geometry = CreateQuadGeometry(); + + Renderer rendererA = Renderer::New(geometry, shaderA); + actorA.AddRenderer(rendererA); + + Renderer rendererB = Renderer::New(geometry, shaderB); + actorB.AddRenderer(rendererB); + + Renderer rendererC = Renderer::New(geometry, shaderC); + actorC.AddRenderer(rendererC); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + Actor container = Actor::New(); + container.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + container.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS); + stage.Add(container); + + container.Add(actorA); + container.Add(actorB); + container.Add(actorC); + + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(container).Connect(&application, f); + + // Set up gl abstraction trace so can query the set uniform order + TestGlAbstraction& glAbstraction = application.GetGlAbstraction(); + glAbstraction.EnableSetUniformCallTrace(true); + glAbstraction.ResetSetUniformCallStack(); + TraceCallStack& glSetUniformStack = glAbstraction.GetSetUniformTrace(); + + glAbstraction.ResetSetUniformCallStack(); + + application.SendNotification(); + application.Render(); + + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + int indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + int indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + int indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + tet_infoline("Testing C above B and A at bottom\n"); + bool CBA = (indexC > indexB) && (indexB > indexA); + + DALI_TEST_EQUALS(CBA, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + tet_infoline("UtcDaliActor Test Set up completed \n"); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("Lower actor C below Actor B ( actor B and A on same level due to insertion order) so C is below both \n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorC.LowerBelow(actorB); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); // touch event + + glSetUniformStack.Reset(); + + application.SendNotification(); + application.Render(); + + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + tet_infoline("Testing render order is A, C, B"); + DALI_TEST_EQUALS(indexC > indexA, true, TEST_LOCATION); + DALI_TEST_EQUALS(indexB > indexC, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("Lower actor C below Actor A leaving B on top\n"); + + orderChangedSignal = false; + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorC.LowerBelow(actorA); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + glSetUniformStack.Reset(); + + application.Render(); + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + DALI_TEST_EQUALS(indexA > indexC, true, TEST_LOCATION); + DALI_TEST_EQUALS(indexB > indexA, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("Lower actor B below Actor C leaving A on top\n"); + + orderChangedSignal = false; + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorB.LowerBelow(actorC); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + glSetUniformStack.Reset(); + + application.Render(); + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + DALI_TEST_EQUALS(indexC > indexB, true, TEST_LOCATION); + DALI_TEST_EQUALS(indexA > indexC, true, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliActorLowerBelow2(void) +{ + tet_infoline("UtcDaliActor LowerBelow test using SIBLING_ORDER property\n"); + + TestApplication application; + + Integration::Scene stage(application.GetScene()); + + // Set up renderers to add to Actors, float value 1, 2, 3 assigned to each + // enables checking of which actor the uniform is assigned too + Shader shaderA = CreateShader(); + shaderA.RegisterProperty("uRendererColor", 1.f); + + Shader shaderB = CreateShader(); + shaderB.RegisterProperty("uRendererColor", 2.f); + + Shader shaderC = CreateShader(); + shaderC.RegisterProperty("uRendererColor", 3.f); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); + + // Add renderers to Actors so ( uRendererColor, 1 ) is A, ( uRendererColor, 2 ) is B, and ( uRendererColor, 3 ) is C, + Geometry geometry = CreateQuadGeometry(); + + Renderer rendererA = Renderer::New(geometry, shaderA); + actorA.AddRenderer(rendererA); + + Renderer rendererB = Renderer::New(geometry, shaderB); + actorB.AddRenderer(rendererB); + + Renderer rendererC = Renderer::New(geometry, shaderC); + actorC.AddRenderer(rendererC); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + Actor container = Actor::New(); + container.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + container.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS); + stage.Add(container); + + container.Add(actorA); + container.Add(actorB); + container.Add(actorC); + + ResetTouchCallbacks(); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(container).Connect(&application, f); + + // Set up gl abstraction trace so can query the set uniform order + TestGlAbstraction& glAbstraction = application.GetGlAbstraction(); + glAbstraction.EnableSetUniformCallTrace(true); + glAbstraction.ResetSetUniformCallStack(); + TraceCallStack& glSetUniformStack = glAbstraction.GetSetUniformTrace(); + + glAbstraction.ResetSetUniformCallStack(); + + application.SendNotification(); + application.Render(); + + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + int indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + int indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + int indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + tet_infoline("Testing C above B and A at bottom\n"); + bool CBA = (indexC > indexB) && (indexB > indexA); + + DALI_TEST_EQUALS(CBA, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + tet_infoline("UtcDaliActor Test Set up completed \n"); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(); + + tet_printf("Lower actor C below Actor B ( actor B and A on same level due to insertion order) so C is below both \n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorC[DevelActor::Property::SIBLING_ORDER] = 1; + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); // touch event + + glSetUniformStack.Reset(); + + application.SendNotification(); + application.Render(); + + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + tet_infoline("Testing render order is A, C, B"); + DALI_TEST_EQUALS(indexC > indexA, true, TEST_LOCATION); + DALI_TEST_EQUALS(indexB > indexC, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(); + + tet_printf("Lower actor C below Actor A leaving B on top\n"); + + orderChangedSignal = false; + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorC[DevelActor::Property::SIBLING_ORDER] = 0; + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + glSetUniformStack.Reset(); + + application.Render(); + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + DALI_TEST_EQUALS(indexA > indexC, true, TEST_LOCATION); + DALI_TEST_EQUALS(indexB > indexA, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(); + + tet_printf("Lower actor B below Actor C leaving A on top\n"); + + orderChangedSignal = false; + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorB[DevelActor::Property::SIBLING_ORDER] = 0; + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + glSetUniformStack.Reset(); + + application.Render(); + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + DALI_TEST_EQUALS(indexC > indexB, true, TEST_LOCATION); + DALI_TEST_EQUALS(indexA > indexC, true, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliActorGeoTouchLowerBelow2(void) +{ + tet_infoline("UtcDaliActor LowerBelow test using SIBLING_ORDER property\n"); + + TestApplication application; + + Integration::Scene stage(application.GetScene()); + + // Set up renderers to add to Actors, float value 1, 2, 3 assigned to each + // enables checking of which actor the uniform is assigned too + Shader shaderA = CreateShader(); + shaderA.RegisterProperty("uRendererColor", 1.f); + + Shader shaderB = CreateShader(); + shaderB.RegisterProperty("uRendererColor", 2.f); + + Shader shaderC = CreateShader(); + shaderC.RegisterProperty("uRendererColor", 3.f); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); + + // Add renderers to Actors so ( uRendererColor, 1 ) is A, ( uRendererColor, 2 ) is B, and ( uRendererColor, 3 ) is C, + Geometry geometry = CreateQuadGeometry(); + + Renderer rendererA = Renderer::New(geometry, shaderA); + actorA.AddRenderer(rendererA); + + Renderer rendererB = Renderer::New(geometry, shaderB); + actorB.AddRenderer(rendererB); + + Renderer rendererC = Renderer::New(geometry, shaderC); + actorC.AddRenderer(rendererC); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + Actor container = Actor::New(); + container.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + container.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS); + stage.Add(container); + + container.Add(actorA); + container.Add(actorB); + container.Add(actorC); + + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(container).Connect(&application, f); + + // Set up gl abstraction trace so can query the set uniform order + TestGlAbstraction& glAbstraction = application.GetGlAbstraction(); + glAbstraction.EnableSetUniformCallTrace(true); + glAbstraction.ResetSetUniformCallStack(); + TraceCallStack& glSetUniformStack = glAbstraction.GetSetUniformTrace(); + + glAbstraction.ResetSetUniformCallStack(); + + application.SendNotification(); + application.Render(); + + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + int indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + int indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + int indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + tet_infoline("Testing C above B and A at bottom\n"); + bool CBA = (indexC > indexB) && (indexB > indexA); + + DALI_TEST_EQUALS(CBA, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + tet_infoline("UtcDaliActor Test Set up completed \n"); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("Lower actor C below Actor B ( actor B and A on same level due to insertion order) so C is below both \n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorC[DevelActor::Property::SIBLING_ORDER] = 1; + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); // touch event + + glSetUniformStack.Reset(); + + application.SendNotification(); + application.Render(); + + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + tet_infoline("Testing render order is A, C, B"); + DALI_TEST_EQUALS(indexC > indexA, true, TEST_LOCATION); + DALI_TEST_EQUALS(indexB > indexC, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("Lower actor C below Actor A leaving B on top\n"); + + orderChangedSignal = false; + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorC[DevelActor::Property::SIBLING_ORDER] = 0; + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorC, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + glSetUniformStack.Reset(); + + application.Render(); + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + DALI_TEST_EQUALS(indexA > indexC, true, TEST_LOCATION); + DALI_TEST_EQUALS(indexB > indexA, true, TEST_LOCATION); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("Lower actor B below Actor C leaving A on top\n"); + + orderChangedSignal = false; + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorB[DevelActor::Property::SIBLING_ORDER] = 0; + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorB, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + glSetUniformStack.Reset(); + + application.Render(); + tet_printf("Trace:%s \n", glSetUniformStack.GetTraceString().c_str()); + + // Test order of uniforms in stack + indexC = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "3.000000"); + indexB = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "2.000000"); + indexA = glSetUniformStack.FindIndexFromMethodAndParams("uRendererColor", "1.000000"); + + DALI_TEST_EQUALS(indexC > indexB, true, TEST_LOCATION); + DALI_TEST_EQUALS(indexA > indexC, true, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliActorRaiseAboveDifferentParentsN(void) +{ + tet_infoline("UtcDaliActor RaiseToAbove test with actor and target actor having different parents \n"); + + TestApplication application; + + Integration::Scene stage(application.GetScene()); + + Actor parentA = Actor::New(); + Actor parentB = Actor::New(); + parentA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + parentA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + parentB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + parentB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + parentA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + parentA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + parentB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + parentB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + stage.Add(parentA); + stage.Add(parentB); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); + + parentA.Add(actorA); + parentA.Add(actorB); + + tet_printf("Actor C added to different parent from A and B \n"); + parentB.Add(actorC); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + ResetTouchCallbacks(); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(); + + tet_printf("Raise actor A Above Actor C which have different parents\n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.RaiseAbove(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + + application.ProcessEvent(touchEvent); // touch event + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(); + + END_TEST; +} + +int UtcDaliActorGeoTouchRaiseAboveDifferentParentsN(void) +{ + tet_infoline("UtcDaliActor RaiseToAbove test with actor and target actor having different parents \n"); + + TestApplication application; + + Integration::Scene stage(application.GetScene()); + + Actor parentA = Actor::New(); + Actor parentB = Actor::New(); + parentA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + parentA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + parentB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + parentB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + parentA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + parentA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + parentB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + parentB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + stage.Add(parentA); + stage.Add(parentB); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); + + parentA.Add(actorA); + parentA.Add(actorB); + + tet_printf("Actor C added to different parent from A and B \n"); + parentB.Add(actorC); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_printf("Raise actor A Above Actor C which have different parents\n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.RaiseAbove(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + + application.ProcessEvent(touchEvent); // touch event + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); - parentB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); - parentB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + ResetTouchCallbacks(application); - stage.Add(parentA); - stage.Add(parentB); + END_TEST; +} + +int UtcDaliActorRaiseLowerWhenUnparentedTargetN(void) +{ + tet_infoline("UtcDaliActor Test raiseAbove and lowerBelow api when target Actor has no parent \n"); + + TestApplication application; + + Integration::Scene stage(application.GetScene()); Actor actorA = Actor::New(); Actor actorB = Actor::New(); Actor actorC = Actor::New(); - parentA.Add(actorA); - parentA.Add(actorB); + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); - tet_printf("Actor C added to different parent from A and B \n"); - parentB.Add(actorC); + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + ResetTouchCallbacks(); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + tet_printf("Raise actor A Above Actor C which have no parents\n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.RaiseAbove(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + + application.ProcessEvent(touchEvent); + + tet_printf("Not parented so RaiseAbove should show no effect\n"); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(); + + orderChangedSignal = false; + + stage.Add(actorB); + tet_printf("Lower actor A below Actor C when only A is not on stage \n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.LowerBelow(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + tet_printf("Actor A not parented so LowerBelow should show no effect\n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(); + + orderChangedSignal = false; + + tet_printf("Adding Actor A to stage, will be on top\n"); + + stage.Add(actorA); + application.SendNotification(); + application.Render(); + + tet_printf("Raise actor B Above Actor C when only B has a parent\n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorB.RaiseAbove(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + + application.ProcessEvent(touchEvent); + + tet_printf("C not parented so RaiseAbove should show no effect\n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(); + + orderChangedSignal = false; + + tet_printf("Lower actor A below Actor C when only A has a parent\n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.LowerBelow(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + + application.ProcessEvent(touchEvent); + + tet_printf("C not parented so LowerBelow should show no effect\n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(); + + orderChangedSignal = false; + + stage.Add(actorC); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.RaiseAbove(actorC); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + tet_printf("Raise actor A Above Actor C, now both have same parent \n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliActorGeoTouchRaiseLowerWhenUnparentedTargetN(void) +{ + tet_infoline("UtcDaliActor Test raiseAbove and lowerBelow api when target Actor has no parent \n"); + + TestApplication application; + + Integration::Scene stage(application.GetScene()); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); @@ -6635,65 +8160,148 @@ int UtcDaliActorRaiseAboveDifferentParentsN(void) actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); - ResetTouchCallbacks(); + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + tet_printf("Raise actor A Above Actor C which have no parents\n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.RaiseAbove(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + + application.ProcessEvent(touchEvent); + + tet_printf("Not parented so RaiseAbove should show no effect\n"); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(application); + + orderChangedSignal = false; + + stage.Add(actorB); + tet_printf("Lower actor A below Actor C when only A is not on stage \n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.LowerBelow(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + tet_printf("Actor A not parented so LowerBelow should show no effect\n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + ResetTouchCallbacks(application); + + orderChangedSignal = false; + + tet_printf("Adding Actor A to stage, will be on top\n"); + + stage.Add(actorA); + application.SendNotification(); + application.Render(); + + tet_printf("Raise actor B Above Actor C when only B has a parent\n"); - // Connect ChildOrderChangedSignal - bool orderChangedSignal(false); - Actor orderChangedActor; - ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); - DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorB.RaiseAbove(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + // Ensure sorting happens at end of Core::ProcessEvents() before next touch application.SendNotification(); - application.Render(); - DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + application.ProcessEvent(touchEvent); + + tet_printf("C not parented so RaiseAbove should show no effect\n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - // connect to actor touch signals, will use touch callbacks to determine which actor is on top. - // Only top actor will get touched. - actorA.TouchedSignal().Connect(TestTouchCallback); - actorB.TouchedSignal().Connect(TestTouchCallback2); - actorC.TouchedSignal().Connect(TestTouchCallback3); + ResetTouchCallbacks(application); - Dali::Integration::Point point; - point.SetDeviceId(1); - point.SetState(PointState::DOWN); - point.SetScreenPosition(Vector2(10.f, 10.f)); - Dali::Integration::TouchEvent touchEvent; - touchEvent.AddPoint(point); + orderChangedSignal = false; + + tet_printf("Lower actor A below Actor C when only A has a parent\n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.LowerBelow(actorC); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + + // Ensure sorting happens at end of Core::ProcessEvents() before next touch + application.SendNotification(); application.ProcessEvent(touchEvent); - DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + tet_printf("C not parented so LowerBelow should show no effect\n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); - DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); - tet_printf("Raise actor A Above Actor C which have different parents\n"); + orderChangedSignal = false; + + stage.Add(actorC); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); actorA.RaiseAbove(actorC); - DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); // Ensure sorting happens at end of Core::ProcessEvents() before next touch application.SendNotification(); + application.Render(); - application.ProcessEvent(touchEvent); // touch event + application.ProcessEvent(touchEvent); - DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + tet_printf("Raise actor A Above Actor C, now both have same parent \n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); - DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); - - ResetTouchCallbacks(); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); END_TEST; } -int UtcDaliActorRaiseLowerWhenUnparentedTargetN(void) +int UtcDaliActorTestAllAPIwhenActorNotParented(void) { - tet_infoline("UtcDaliActor Test raiseAbove and lowerBelow api when target Actor has no parent \n"); + tet_infoline("UtcDaliActor Test all raise/lower api when actor has no parent \n"); TestApplication application; @@ -6729,13 +8337,6 @@ int UtcDaliActorRaiseLowerWhenUnparentedTargetN(void) ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); - application.SendNotification(); - application.Render(); - - DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); - DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); - DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - // connect to actor touch signals, will use touch callbacks to determine which actor is on top. // Only top actor will get touched. actorA.TouchedSignal().Connect(TestTouchCallback); @@ -6749,117 +8350,116 @@ int UtcDaliActorRaiseLowerWhenUnparentedTargetN(void) Dali::Integration::TouchEvent touchEvent; touchEvent.AddPoint(point); - tet_printf("Raise actor A Above Actor C which have no parents\n"); + stage.Add(actorA); + tet_printf("Raise actor B Above Actor C but B not parented\n"); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorA.RaiseAbove(actorC); + actorB.Raise(); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - // Ensure sorting happens at end of Core::ProcessEvents() before next touch application.SendNotification(); + application.Render(); application.ProcessEvent(touchEvent); tet_printf("Not parented so RaiseAbove should show no effect\n"); - DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + tet_printf("Raise actor B Above Actor C but B not parented\n"); ResetTouchCallbacks(); orderChangedSignal = false; - stage.Add(actorB); - tet_printf("Lower actor A below Actor C when only A is not on stage \n"); - DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorA.LowerBelow(actorC); + actorC.Lower(); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - // Ensure sorting happens at end of Core::ProcessEvents() before next touch + // Sort actor tree before next touch event application.SendNotification(); application.Render(); application.ProcessEvent(touchEvent); - tet_printf("Actor A not parented so LowerBelow should show no effect\n"); - DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); - DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); - DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + tet_printf("Not parented so RaiseAbove should show no effect\n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); ResetTouchCallbacks(); orderChangedSignal = false; - tet_printf("Adding Actor A to stage, will be on top\n"); - - stage.Add(actorA); - application.SendNotification(); - application.Render(); - - tet_printf("Raise actor B Above Actor C when only B has a parent\n"); + tet_printf("Lower actor C below B but C not parented\n"); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorB.RaiseAbove(actorC); + actorB.Lower(); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - // Ensure sorting happens at end of Core::ProcessEvents() before next touch + // Sort actor tree before next touch event application.SendNotification(); + application.Render(); application.ProcessEvent(touchEvent); - tet_printf("C not parented so RaiseAbove should show no effect\n"); + tet_printf("Not parented so Lower should show no effect\n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - ResetTouchCallbacks(); orderChangedSignal = false; - tet_printf("Lower actor A below Actor C when only A has a parent\n"); + tet_printf("Raise actor B to top\n"); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorA.LowerBelow(actorC); + actorB.RaiseToTop(); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - // Ensure sorting happens at end of Core::ProcessEvents() before next touch + // Sort actor tree before next touch event application.SendNotification(); + application.Render(); application.ProcessEvent(touchEvent); - tet_printf("C not parented so LowerBelow should show no effect\n"); + tet_printf("Not parented so RaiseToTop should show no effect\n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - ResetTouchCallbacks(); orderChangedSignal = false; - stage.Add(actorC); + tet_printf("Add ActorB to stage so only Actor C not parented\n"); + + stage.Add(actorB); + + tet_printf("Lower actor C to Bottom, B stays at top\n"); DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - actorA.RaiseAbove(actorC); - DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); - DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); + actorC.LowerToBottom(); + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); - // Ensure sorting happens at end of Core::ProcessEvents() before next touch application.SendNotification(); application.Render(); application.ProcessEvent(touchEvent); - tet_printf("Raise actor A Above Actor C, now both have same parent \n"); - DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); - DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + tet_printf("Not parented so LowerToBottom should show no effect\n"); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + ResetTouchCallbacks(); END_TEST; } -int UtcDaliActorTestAllAPIwhenActorNotParented(void) +int UtcDaliActorGeoTouchTestAllAPIwhenActorNotParented(void) { tet_infoline("UtcDaliActor Test all raise/lower api when actor has no parent \n"); @@ -6889,7 +8489,8 @@ int UtcDaliActorTestAllAPIwhenActorNotParented(void) actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); - ResetTouchCallbacks(); + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); // Connect ChildOrderChangedSignal bool orderChangedSignal(false); @@ -6929,7 +8530,7 @@ int UtcDaliActorTestAllAPIwhenActorNotParented(void) DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); tet_printf("Raise actor B Above Actor C but B not parented\n"); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); orderChangedSignal = false; @@ -6948,7 +8549,7 @@ int UtcDaliActorTestAllAPIwhenActorNotParented(void) DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); orderChangedSignal = false; @@ -6969,7 +8570,7 @@ int UtcDaliActorTestAllAPIwhenActorNotParented(void) DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); orderChangedSignal = false; @@ -6990,7 +8591,7 @@ int UtcDaliActorTestAllAPIwhenActorNotParented(void) DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); orderChangedSignal = false; @@ -7014,7 +8615,7 @@ int UtcDaliActorTestAllAPIwhenActorNotParented(void) DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled2, true, TEST_LOCATION); DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); - ResetTouchCallbacks(); + ResetTouchCallbacks(application); END_TEST; } @@ -7125,6 +8726,113 @@ int UtcDaliActorRaiseAboveActorAndTargetTheSameN(void) END_TEST; } +int UtcDaliActorGeoTouchRaiseAboveActorAndTargetTheSameN(void) +{ + tet_infoline("UtcDaliActor RaiseToAbove and test with actor provided as target resulting in a no operation \n"); + + TestApplication application; + + Integration::Scene stage(application.GetScene()); + + Actor actorA = Actor::New(); + Actor actorB = Actor::New(); + Actor actorC = Actor::New(); + + actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorC.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actorC.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + + actorA.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorA.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorB.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorB.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT"); + actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT"); + + stage.Add(actorA); + stage.Add(actorB); + stage.Add(actorC); + + // connect to actor touch signals, will use touch callbacks to determine which actor is on top. + // Only top actor will get touched. + actorA.TouchedSignal().Connect(TestTouchCallback); + actorB.TouchedSignal().Connect(TestTouchCallback2); + actorC.TouchedSignal().Connect(TestTouchCallback3); + + application.GetScene().SetGeometryHittestEnabled(true); + ResetTouchCallbacks(application); + + // Connect ChildOrderChangedSignal + bool orderChangedSignal(false); + Actor orderChangedActor; + ChildOrderChangedFunctor f(orderChangedSignal, orderChangedActor); + DevelActor::ChildOrderChangedSignal(stage.GetRootLayer()).Connect(&application, f); + + application.SendNotification(); + application.Render(); + + Dali::Integration::Point point; + point.SetDeviceId(1); + point.SetState(PointState::DOWN); + point.SetScreenPosition(Vector2(10.f, 10.f)); + Dali::Integration::TouchEvent touchEvent; + touchEvent.AddPoint(point); + + application.ProcessEvent(touchEvent); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(application); + + tet_infoline("Raise actor A Above Actor A which is the same actor!!\n"); + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.RaiseAbove(actorA); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); + + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + tet_infoline("No target is source Actor so RaiseAbove should show no effect\n"); + + DALI_TEST_EQUALS(gTouchCallBackCalled, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, true, TEST_LOCATION); + + ResetTouchCallbacks(application); + + orderChangedSignal = false; + + DALI_TEST_EQUALS(orderChangedSignal, false, TEST_LOCATION); + actorA.RaiseAbove(actorC); + DALI_TEST_EQUALS(orderChangedSignal, true, TEST_LOCATION); + DALI_TEST_EQUALS(orderChangedActor, actorA, TEST_LOCATION); + + application.SendNotification(); + application.Render(); + + application.ProcessEvent(touchEvent); + + tet_infoline("Raise actor A Above Actor C which will now be successful \n"); + DALI_TEST_EQUALS(gTouchCallBackCalled, true, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled2, false, TEST_LOCATION); + DALI_TEST_EQUALS(gTouchCallBackCalled3, false, TEST_LOCATION); + + END_TEST; +} + int UtcDaliActorGetScreenPosition(void) { tet_infoline("UtcDaliActorGetScreenPosition Get screen coordinates of Actor \n"); diff --git a/automated-tests/src/dali/utc-Dali-GeoHitTestAlgorithm.cpp b/automated-tests/src/dali/utc-Dali-GeoHitTestAlgorithm.cpp new file mode 100644 index 0000000..bc7d30b --- /dev/null +++ b/automated-tests/src/dali/utc-Dali-GeoHitTestAlgorithm.cpp @@ -0,0 +1,892 @@ +/* + * Copyright (c) 2023 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. + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +using namespace Dali; + +namespace +{ + +/** + * The functor to be used in the hit-test algorithm to check whether the actor is hittable. + */ +bool IsActorHittableFunction(Actor actor, Dali::HitTestAlgorithm::TraverseType type) +{ + bool hittable = false; + + switch(type) + { + case Dali::HitTestAlgorithm::CHECK_ACTOR: + { + // Check whether the actor is visible and not fully transparent. + if(actor.GetCurrentProperty(Actor::Property::VISIBLE) && actor.GetCurrentProperty(Actor::Property::WORLD_COLOR).a > 0.01f) // not FULLY_TRANSPARENT + { + // Check whether the actor has the specific name "HittableActor" + if(actor.GetProperty(Actor::Property::NAME) == "HittableActor") + { + hittable = true; + } + } + break; + } + case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE: + { + if(actor.GetCurrentProperty(Actor::Property::VISIBLE)) // Actor is visible, if not visible then none of its children are visible. + { + hittable = true; + } + break; + } + default: + { + break; + } + } + + return hittable; +}; + +bool DefaultIsActorTouchableFunction(Dali::Actor actor, Dali::HitTestAlgorithm::TraverseType type) +{ + bool hittable = false; + + switch(type) + { + case Dali::HitTestAlgorithm::CHECK_ACTOR: + { + if(actor.GetCurrentProperty(Actor::Property::VISIBLE) && + actor.GetProperty(Actor::Property::SENSITIVE) && + actor.GetCurrentProperty(Actor::Property::WORLD_COLOR).a > 0.01f) + { + hittable = true; + } + break; + } + case Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE: + { + if(actor.GetCurrentProperty(Actor::Property::VISIBLE) && // Actor is visible, if not visible then none of its children are visible. + actor.GetProperty(Actor::Property::SENSITIVE)) // Actor is sensitive, if insensitive none of its children should be hittable either. + { + hittable = true; + } + break; + } + default: + { + break; + } + } + + return hittable; +}; + +} // anonymous namespace + +// Positive test case for a method +int UtcDaliGeoHitTestAlgorithmWithFunctor(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm functor"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + actor.SetProperty(Actor::Property::NAME, "NonHittableActor"); + stage.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + Vector2 screenCoordinates(10.0f, 10.0f); + Vector2 localCoordinates; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + + // Perform a hit-test at the given screen coordinates + Dali::HitTestAlgorithm::Results results; + Dali::HitTestAlgorithm::HitTest(stage, screenCoordinates, results, IsActorHittableFunction, true); + DALI_TEST_CHECK(results.actor != actor); + + actor.SetProperty(Actor::Property::NAME, "HittableActor"); + + results.actor = Actor(); + results.actorCoordinates = Vector2::ZERO; + + // Perform a hit-test at the given screen coordinates + Dali::HitTestAlgorithm::HitTest(stage, screenCoordinates, results, IsActorHittableFunction, true); + DALI_TEST_CHECK(results.actor == actor); + DALI_TEST_EQUALS(localCoordinates, results.actorCoordinates, 0.1f, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoHitTestAlgorithmOrtho01(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm with parallel Ortho camera()"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + RenderTask defaultRenderTask = renderTaskList.GetTask(0u); + Dali::CameraActor cameraActor = defaultRenderTask.GetCameraActor(); + + Vector2 stageSize(stage.GetSize()); + cameraActor.SetOrthographicProjection(stageSize); + cameraActor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 1600.0f)); + + Vector2 actorSize(stageSize * 0.5f); + // Create two actors with half the size of the stage and set them to be partially overlapping + Actor blue = Actor::New(); + blue.SetProperty(Actor::Property::NAME, "Blue"); + blue.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + blue.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(1.0f / 3.0f, 1.0f / 3.0f, 0.5f)); + blue.SetProperty(Actor::Property::SIZE, actorSize); + blue.SetProperty(Actor::Property::POSITION_Z, 30.0f); + + Actor green = Actor::New(); + green.SetProperty(Actor::Property::NAME, "Green"); + green.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + green.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(2.0f / 3.0f, 2.0f / 3.0f, 0.5f)); + green.SetProperty(Actor::Property::SIZE, actorSize); + + // Add the actors to the view + stage.Add(blue); + stage.Add(green); + + // Render and notify + application.SendNotification(); + application.Render(0); + application.Render(10); + + HitTestAlgorithm::Results results; + HitTest(stage, stageSize / 2.0f, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == green); + DALI_TEST_EQUALS(results.actorCoordinates, actorSize * 1.0f / 6.0f, TEST_LOCATION); + + HitTest(stage, stageSize / 3.0f, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == blue); + DALI_TEST_EQUALS(results.actorCoordinates, actorSize * 0.5f, TEST_LOCATION); + + HitTest(stage, stageSize * 2.0f / 3.0f, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == green); + DALI_TEST_EQUALS(results.actorCoordinates, actorSize * 0.5f, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoHitTestAlgorithmOrtho02(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm with offset Ortho camera()"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + RenderTask defaultRenderTask = renderTaskList.GetTask(0u); + Dali::CameraActor cameraActor = defaultRenderTask.GetCameraActor(); + + Vector2 stageSize(stage.GetSize()); + cameraActor.SetOrthographicProjection(stageSize); + cameraActor.SetNearClippingPlane(800.0f); + cameraActor.SetFarClippingPlane(4895.0f); + + // Move camera not centered position. + cameraActor.SetProperty(Actor::Property::POSITION, Vector3(stageSize.x * 0.2f, stageSize.y * 0.2f, 1600.0f)); + + Vector2 actorSize(stageSize * 0.5f); + // Create two actors with half the size of the stage and set them to be partially overlapping + Actor blue = Actor::New(); + blue.SetProperty(Actor::Property::NAME, "Blue"); + blue.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + blue.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.2f, 0.2f, 0.5f)); + blue.SetProperty(Actor::Property::SIZE, actorSize); + blue.SetProperty(Actor::Property::POSITION_Z, 30.0f); + + Actor green = Actor::New(); + green.SetProperty(Actor::Property::NAME, "Green"); + green.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + green.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(0.4f, 0.4f, 0.5f)); + green.SetProperty(Actor::Property::SIZE, actorSize); + + // Add the actors to the view + stage.Add(blue); + stage.Add(green); + + // Render and notify + application.SendNotification(); + application.Render(0); + application.Render(10); + + { + HitTestAlgorithm::Results results; + HitTest(stage, Vector2(240.0f, 400.0f), results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == green); + DALI_TEST_EQUALS(results.actorCoordinates, actorSize * 0.6f, 0.01f, TEST_LOCATION); + } + + { + HitTestAlgorithm::Results results; + HitTest(stage, Vector2(0.001f, 0.001f), results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == blue); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2(0.001f, 0.001f), 0.001f, TEST_LOCATION); + } + + { + HitTestAlgorithm::Results results; + HitTest(stage, stageSize, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(!results.actor); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2::ZERO, TEST_LOCATION); + } + + // Just inside green + { + HitTestAlgorithm::Results results; + HitTest(stage, stageSize * 0.69f, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == green); + DALI_TEST_EQUALS(results.actorCoordinates, actorSize * 0.98f, 0.01f, TEST_LOCATION); + } + + END_TEST; +} + +int UtcDaliGeoHitTestAlgorithmClippingActor(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm with a stencil"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + Actor rootLayer = stage.GetRootLayer(); + rootLayer.SetProperty(Actor::Property::NAME, "RootLayer"); + + // Create a layer + Layer layer = Layer::New(); + layer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + layer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + layer.SetProperty(Actor::Property::NAME, "layer"); + stage.Add(layer); + + // Create a clipping actor and add it to the layer. + Actor clippingActor = CreateRenderableActor(); + clippingActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + clippingActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + clippingActor.SetProperty(Actor::Property::SIZE, Vector2(50.0f, 50.0f)); + clippingActor.SetProperty(Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_CHILDREN); + clippingActor.SetProperty(Actor::Property::NAME, "clippingActor"); + layer.Add(clippingActor); + + // Create a renderable actor and add it to the clipping actor. + Actor childActor = CreateRenderableActor(); + childActor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + childActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + childActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + childActor.SetProperty(Actor::Property::NAME, "childActor"); + clippingActor.Add(childActor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Hit within clippingActor and childActor. + HitTestAlgorithm::Results results; + HitTest(stage, Vector2(10.0f, 10.0f), results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == childActor); + tet_printf("Hit: %s\n", (results.actor ? results.actor.GetProperty(Actor::Property::NAME).c_str() : "NULL")); + + // Hit within childActor but outside of clippingActor, should hit the root-layer instead. + HitTest(stage, Vector2(60.0f, 60.0f), results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == rootLayer); + tet_printf("Hit: %s\n", (results.actor ? results.actor.GetProperty(Actor::Property::NAME).c_str() : "NULL")); + + END_TEST; +} + +int UtcDaliGeoHitTestAlgorithmClippingActorStress(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm with many many stencil"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + Actor rootLayer = stage.GetRootLayer(); + rootLayer.SetProperty(Actor::Property::NAME, "RootLayer"); + + // Create a layer + Layer layer = Layer::New(); + layer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + layer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + layer.SetProperty(Actor::Property::NAME, "layer"); + stage.Add(layer); + + // Create a clipping actor and add it to the layer. + Actor clippingActor = CreateRenderableActor(); + clippingActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + clippingActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + clippingActor.SetProperty(Actor::Property::SIZE, Vector2(220.0f, 220.0f)); + clippingActor.SetProperty(Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_TO_BOUNDING_BOX); + clippingActor.SetProperty(Actor::Property::NAME, "clippingActor"); + layer.Add(clippingActor); + + // Create a renderable actor and add it to the clipping actor. + Actor latestActor = clippingActor; + const int depthMax = 100; + for(int i = 0; i < depthMax; i++) + { + char tmp[29]; + sprintf(tmp, "depth%03d", i); + + Actor childActor = CreateRenderableActor(); + childActor.SetProperty(Actor::Property::SIZE, Vector2(220.0f, 220.0f)); + childActor.SetProperty(Actor::Property::POSITION, Vector2(200.0f / depthMax, 200.0f / depthMax)); + childActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + childActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + childActor.SetProperty(Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_TO_BOUNDING_BOX); + childActor.SetProperty(Actor::Property::NAME, tmp); + + latestActor.Add(childActor); + latestActor = childActor; + } + // NOTE : latestActor's TOP_LEFT position become 200.f, 200.0f + + // Render and notify + application.SendNotification(); + application.Render(); + + // Hit within clippingActor and latestActor. + HitTestAlgorithm::Results results; + HitTest(stage, Vector2(201.0f, 201.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("Hit: %s\n", (results.actor ? results.actor.GetProperty(Actor::Property::NAME).c_str() : "NULL")); + DALI_TEST_CHECK(results.actor == latestActor); + + // Hit within childActor but outside of clippingActor, should hit the root-layer instead. + HitTest(stage, Vector2(221.0f, 221.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("Hit: %s\n", (results.actor ? results.actor.GetProperty(Actor::Property::NAME).c_str() : "NULL")); + DALI_TEST_CHECK(results.actor == rootLayer); + + END_TEST; +} + +int UtcDaliGeoHitTestAlgorithmOverlay(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm with overlay actors"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + RenderTask defaultRenderTask = renderTaskList.GetTask(0u); + Dali::CameraActor cameraActor = defaultRenderTask.GetCameraActor(); + + Vector2 stageSize(stage.GetSize()); + cameraActor.SetOrthographicProjection(stageSize); + cameraActor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 1600.0f)); + + Vector2 actorSize(stageSize * 0.5f); + // Create two actors with half the size of the stage and set them to be partially overlapping + Actor blue = Actor::New(); + blue.SetProperty(Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D); + blue.SetProperty(Actor::Property::NAME, "Blue"); + blue.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + blue.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(1.0f / 3.0f, 1.0f / 3.0f, 0.5f)); + blue.SetProperty(Actor::Property::SIZE, actorSize); + blue.SetProperty(Actor::Property::POSITION_Z, 30.0f); + + Actor green = Actor::New(); + green.SetProperty(Actor::Property::NAME, "Green"); + green.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + green.SetProperty(Actor::Property::PARENT_ORIGIN, Vector3(2.0f / 3.0f, 2.0f / 3.0f, 0.5f)); + green.SetProperty(Actor::Property::SIZE, actorSize); + + // Add the actors to the view + stage.Add(blue); + stage.Add(green); + + // Render and notify + application.SendNotification(); + application.Render(0); + application.Render(10); + + HitTestAlgorithm::Results results; + + //Hit in the intersection. Should pick the blue actor since it is an overlay. + HitTest(stage, stageSize / 2.0f, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == blue); + DALI_TEST_EQUALS(results.actorCoordinates, actorSize * 5.0f / 6.0f, TEST_LOCATION); + + //Hit in the blue actor + HitTest(stage, stageSize / 3.0f, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == blue); + DALI_TEST_EQUALS(results.actorCoordinates, actorSize * 0.5f, TEST_LOCATION); + + //Hit in the green actor + HitTest(stage, stageSize * 2.0f / 3.0f, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == green); + DALI_TEST_EQUALS(results.actorCoordinates, actorSize * 0.5f, TEST_LOCATION); + + // Create new actor child as blue. It will be shown over the blue, and green. + Actor red = Actor::New(); + red.SetProperty(Actor::Property::NAME, "Red"); + red.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + red.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + red.SetProperty(Actor::Property::POSITION, Vector2(actorSize.x * 5.0f / 6.0f, -actorSize.y * 1.0f / 6.0f)); + red.SetProperty(Actor::Property::SIZE, actorSize); + + blue.Add(red); + + // Render and notify + application.SendNotification(); + application.Render(0); + application.Render(10); + + //Hit in the intersection red, green, blue. Should pick the red actor since it is an child of overlay. + HitTest(stage, Vector2(stageSize.x * 13.0f / 24.0f, stageSize.y * 11.0f / 24.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("%d %d %d , %f %f\n", results.actor == red ? 1 : 0, results.actor == green ? 1 : 0, results.actor == blue ? 1 : 0, results.actorCoordinates.x, results.actorCoordinates.y); + DALI_TEST_CHECK(results.actor == red); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 1.0f / 12.0f, actorSize.y * 11.0f / 12.0f), TEST_LOCATION); + + //Hit in the intersection red, blue. Should pick the red actor since it is an child of blue. + HitTest(stage, Vector2(stageSize.x * 13.0f / 24.0f, stageSize.y * 9.0f / 24.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("%d %d %d , %f %f\n", results.actor == red ? 1 : 0, results.actor == green ? 1 : 0, results.actor == blue ? 1 : 0, results.actorCoordinates.x, results.actorCoordinates.y); + DALI_TEST_CHECK(results.actor == red); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 1.0f / 12.0f, actorSize.y * 9.0f / 12.0f), TEST_LOCATION); + + //Hit in the intersection red, green. Should pick the green actor since red is outside the scope of its parent. + HitTest(stage, Vector2(stageSize.x * 15.0f / 24.0f, stageSize.y * 11.0f / 24.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("%d %d %d , %f %f\n", results.actor == red ? 1 : 0, results.actor == green ? 1 : 0, results.actor == blue ? 1 : 0, results.actorCoordinates.x, results.actorCoordinates.y); + DALI_TEST_CHECK(results.actor == green); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 5.0f / 12.0f, actorSize.y * 1.0f / 12.0f), TEST_LOCATION); + + //Hit in the intersection blue, green. Should pick the blue actor since it is an overlay. + HitTest(stage, Vector2(stageSize.x * 11.0f / 24.0f, stageSize.y * 13.0f / 24.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("%d %d %d , %f %f\n", results.actor == red ? 1 : 0, results.actor == green ? 1 : 0, results.actor == blue ? 1 : 0, results.actorCoordinates.x, results.actorCoordinates.y); + DALI_TEST_CHECK(results.actor == blue); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 9.0f / 12.0f, actorSize.y * 11.0f / 12.0f), TEST_LOCATION); + + // Change blue's draw mode as normal. now blue < red < green + blue.SetProperty(Actor::Property::DRAW_MODE, DrawMode::NORMAL); + + // Render and notify + application.SendNotification(); + application.Render(0); + application.Render(10); + + //Hit in the intersection red, green, blue. Should pick the green actor since it is latest ordered actor. + HitTest(stage, Vector2(stageSize.x * 13.0f / 24.0f, stageSize.y * 11.0f / 24.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("%d %d %d , %f %f\n", results.actor == red ? 1 : 0, results.actor == green ? 1 : 0, results.actor == blue ? 1 : 0, results.actorCoordinates.x, results.actorCoordinates.y); + DALI_TEST_CHECK(results.actor == green); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 3.0f / 12.0f, actorSize.y * 1.0f / 12.0f), TEST_LOCATION); + + //Hit in the intersection red, blue. Should pick the red actor since it is an child of blue. + HitTest(stage, Vector2(stageSize.x * 13.0f / 24.0f, stageSize.y * 9.0f / 24.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("%d %d %d , %f %f\n", results.actor == red ? 1 : 0, results.actor == green ? 1 : 0, results.actor == blue ? 1 : 0, results.actorCoordinates.x, results.actorCoordinates.y); + DALI_TEST_CHECK(results.actor == red); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 1.0f / 12.0f, actorSize.y * 9.0f / 12.0f), TEST_LOCATION); + + //Hit in the intersection red, green. Should pick the green actor since it is latest ordered actor. + HitTest(stage, Vector2(stageSize.x * 15.0f / 24.0f, stageSize.y * 11.0f / 24.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("%d %d %d , %f %f\n", results.actor == red ? 1 : 0, results.actor == green ? 1 : 0, results.actor == blue ? 1 : 0, results.actorCoordinates.x, results.actorCoordinates.y); + DALI_TEST_CHECK(results.actor == green); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 5.0f / 12.0f, actorSize.y * 1.0f / 12.0f), TEST_LOCATION); + + //Hit in the intersection blue, green. Should pick the green actor since it is latest ordered actor. + HitTest(stage, Vector2(stageSize.x * 11.0f / 24.0f, stageSize.y * 13.0f / 24.0f), results, &DefaultIsActorTouchableFunction, true); + tet_printf("%d %d %d , %f %f\n", results.actor == red ? 1 : 0, results.actor == green ? 1 : 0, results.actor == blue ? 1 : 0, results.actorCoordinates.x, results.actorCoordinates.y); + DALI_TEST_CHECK(results.actor == green); + DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 1.0f / 12.0f, actorSize.y * 3.0f / 12.0f), TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoHitTestAlgorithmOrder(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm between On/Off render task"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + Vector2 stageSize(stage.GetSize()); + + Actor blue = Actor::New(); + blue[Dali::Actor::Property::NAME] = "Blue"; + blue[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + blue[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + blue[Dali::Actor::Property::WIDTH_RESIZE_POLICY] = ResizePolicy::FILL_TO_PARENT; + blue[Dali::Actor::Property::HEIGHT_RESIZE_POLICY] = ResizePolicy::FILL_TO_PARENT; + + Actor green = Actor::New(); + green[Dali::Actor::Property::NAME] = "Green"; + green[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + green[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + green[Dali::Actor::Property::WIDTH_RESIZE_POLICY] = ResizePolicy::FILL_TO_PARENT; + green[Dali::Actor::Property::HEIGHT_RESIZE_POLICY] = ResizePolicy::FILL_TO_PARENT; + + stage.Add(blue); + stage.Add(green); + + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + RenderTask offRenderTask = renderTaskList.CreateTask(); + + Dali::CameraActor cameraActor = Dali::CameraActor::New(stageSize); + cameraActor[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + cameraActor[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + stage.Add(cameraActor); + + offRenderTask.SetExclusive(true); + offRenderTask.SetInputEnabled(true); + offRenderTask.SetCameraActor(cameraActor); + offRenderTask.SetSourceActor(green); + offRenderTask.SetScreenToFrameBufferMappingActor(green); + + Dali::Texture texture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGB888, unsigned(stageSize.width), unsigned(stageSize.height)); + FrameBuffer renderTarget = FrameBuffer::New(stageSize.width, stageSize.height, FrameBuffer::Attachment::DEPTH); + renderTarget.AttachColorTexture(texture); + offRenderTask.SetFrameBuffer(renderTarget); + + // Render and notify + application.SendNotification(); + application.Render(10); + + HitTestAlgorithm::Results results; + HitTest(stage, stageSize / 2.0f, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == green); + + END_TEST; +} + +int UtcDaliGeoHitTestAlgorithmExclusiveMultiple(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm between On/Off render task with multiple exclusived"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + Vector2 stageSize(stage.GetSize()); + + Actor blue = Actor::New(); + blue[Dali::Actor::Property::NAME] = "Blue"; + blue[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + blue[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + blue[Dali::Actor::Property::WIDTH_RESIZE_POLICY] = ResizePolicy::FILL_TO_PARENT; + blue[Dali::Actor::Property::HEIGHT_RESIZE_POLICY] = ResizePolicy::FILL_TO_PARENT; + + Actor green = Actor::New(); + green[Dali::Actor::Property::NAME] = "Green"; + green[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + green[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + green[Dali::Actor::Property::WIDTH_RESIZE_POLICY] = ResizePolicy::FILL_TO_PARENT; + green[Dali::Actor::Property::HEIGHT_RESIZE_POLICY] = ResizePolicy::FILL_TO_PARENT; + + stage.Add(blue); + stage.Add(green); + + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + RenderTask offRenderTask = renderTaskList.CreateTask(); + RenderTask offRenderTask2 = renderTaskList.CreateTask(); + + Dali::CameraActor cameraActor = Dali::CameraActor::New(stageSize); + cameraActor[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + cameraActor[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + stage.Add(cameraActor); + + offRenderTask.SetExclusive(true); + offRenderTask.SetInputEnabled(true); + offRenderTask.SetCameraActor(cameraActor); + offRenderTask.SetSourceActor(green); + offRenderTask.SetScreenToFrameBufferMappingActor(green); + + Dali::Texture texture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGB888, unsigned(stageSize.width), unsigned(stageSize.height)); + FrameBuffer renderTarget = FrameBuffer::New(stageSize.width, stageSize.height, FrameBuffer::Attachment::DEPTH); + renderTarget.AttachColorTexture(texture); + offRenderTask.SetFrameBuffer(renderTarget); + + offRenderTask2.SetExclusive(true); + offRenderTask2.SetInputEnabled(true); + offRenderTask2.SetCameraActor(cameraActor); + offRenderTask2.SetSourceActor(green); + offRenderTask2.SetScreenToFrameBufferMappingActor(green); + offRenderTask2.SetFrameBuffer(renderTarget); + + // Render and notify + application.SendNotification(); + application.Render(10); + + HitTestAlgorithm::Results results; + HitTest(stage, stageSize / 2.0f, results, &DefaultIsActorTouchableFunction, true); + DALI_TEST_CHECK(results.actor == green); + + END_TEST; +} + +int UtcDaliGeoHitTestAlgorithmBuildPickingRay01(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm::BuildPickingRay positive test"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + RenderTask defaultRenderTask = renderTaskList.GetTask(0u); + Dali::CameraActor cameraActor = defaultRenderTask.GetCameraActor(); + + Vector2 stageSize(stage.GetSize()); + + Vector2 actorSize(stageSize * 0.5f); + // Create two actors with half the size of the stage and set them to be overlapping + Actor blue = Actor::New(); + blue.SetProperty(Actor::Property::NAME, "Blue"); + blue.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + blue.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER); + blue.SetProperty(Actor::Property::SIZE, actorSize); + + Actor green = Actor::New(); + green.SetProperty(Actor::Property::NAME, "Green"); + green.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + green.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER); + green.SetProperty(Actor::Property::SIZE, actorSize); + + // Add the actors to the view + stage.Add(blue); + stage.Add(green); + + // Render and notify + application.SendNotification(); + application.Render(0); + + Vector2 screenCoords(stageSize * 0.5f); // touch center of screen + Vector3 origin; + Vector3 direction; + bool built = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction); + + Vector3 camPos = cameraActor[Actor::Property::POSITION]; + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, -Vector3::ZAXIS, 0.01f, TEST_LOCATION); + + screenCoords.x = stageSize.width * 0.75f; + built = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(0.075f, 0.0f, -1.0f), 0.01f, TEST_LOCATION); + + screenCoords.x = 0.0f; + screenCoords.y = 0.0f; + built = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(-0.144f, -0.24f, -0.96f), 0.01f, TEST_LOCATION); + + screenCoords.x = stageSize.width; + screenCoords.y = stageSize.height; + built = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(0.144f, 0.24f, -0.96f), 0.01f, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoHitTestAlgorithmBuildPickingRay02(void) +{ + TestApplication application; + tet_infoline("Testing Dali::HitTestAlgorithm::BuildPickingRay positive test for offscreen"); + + application.GetScene().SetGeometryHittestEnabled(true); + + Stage stage = Stage::GetCurrent(); + RenderTaskList renderTaskList = stage.GetRenderTaskList(); + RenderTask defaultRenderTask = renderTaskList.GetTask(0u); + RenderTask offRenderTask = renderTaskList.CreateTask(); + + Dali::CameraActor defaultCameraActor = defaultRenderTask.GetCameraActor(); + + Vector2 stageSize(stage.GetSize()); + + Vector2 actorSize(stageSize * 0.5f); + Vector2 offscreenSize(1920.0f, 1080.0f); // Quit big size. + + // Create two actors with half the size of the stage and set them to be partial-overlapping + Actor blue = Actor::New(); + blue.SetProperty(Actor::Property::NAME, "Blue"); + blue.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + blue.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER); + blue.SetProperty(Actor::Property::SIZE, actorSize); + blue.SetProperty(Actor::Property::POSITION, -actorSize * 0.25f); + + Actor green = Actor::New(); + green.SetProperty(Actor::Property::NAME, "Green"); + green.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + green.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER); + green.SetProperty(Actor::Property::SIZE, actorSize); + green.SetProperty(Actor::Property::POSITION, actorSize * 0.25f); + + Actor red = Actor::New(); + red.SetProperty(Actor::Property::NAME, "Red"); + red.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + red.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER); + red.SetProperty(Actor::Property::SIZE, offscreenSize * 0.5f); + + Dali::CameraActor offscreenCameraActor = Dali::CameraActor::New(offscreenSize); + offscreenCameraActor[Dali::Actor::Property::ANCHOR_POINT] = AnchorPoint::CENTER; + offscreenCameraActor[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER; + stage.Add(offscreenCameraActor); + + offRenderTask.SetExclusive(true); + offRenderTask.SetInputEnabled(true); + offRenderTask.SetCameraActor(offscreenCameraActor); + offRenderTask.SetSourceActor(red); + offRenderTask.SetScreenToFrameBufferMappingActor(green); + + Dali::Texture texture = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(actorSize.width), unsigned(actorSize.height)); + FrameBuffer renderTarget = FrameBuffer::New(actorSize.width, actorSize.height, FrameBuffer::Attachment::DEPTH_STENCIL); + renderTarget.AttachColorTexture(texture); + offRenderTask.SetFrameBuffer(renderTarget); + + // Add the actors to the view + stage.Add(blue); + stage.Add(green); + stage.Add(red); + + // Render and notify + application.SendNotification(); + application.Render(0); + + Vector2 screenCoords(stageSize * 0.5f); // touch center of screen + Vector3 origin; + Vector3 direction; + bool built = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction); + + Vector3 camPos = defaultCameraActor[Actor::Property::POSITION]; + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, -Vector3::ZAXIS, 0.01f, TEST_LOCATION); + + screenCoords.x = stageSize.width * 0.75f; + built = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(0.075f, 0.0f, -1.0f), 0.01f, TEST_LOCATION); + + screenCoords.x = 0.0f; + screenCoords.y = 0.0f; + built = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(-0.144f, -0.24f, -0.96f), 0.01f, TEST_LOCATION); + + screenCoords.x = stageSize.width; + screenCoords.y = stageSize.height; + built = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(0.144f, 0.24f, -0.96f), 0.01f, TEST_LOCATION); + + // For offscreen picking ray + camPos = Vector3(offscreenCameraActor[Actor::Property::POSITION]); + + const float ELLIPSION = 0.001f; ///< tiny margin to avoid non-hitting cases + + // Center of green + screenCoords = stageSize * 0.5f + actorSize * 0.25f; + built = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction); + + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, -Vector3::ZAXIS, 0.01f, TEST_LOCATION); + + // Center right of green + screenCoords.x = stageSize.width * 0.5f + actorSize.width * 0.75f - ELLIPSION; + built = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(0.242533f, 0.0f, -0.970143f), 0.01f, TEST_LOCATION); + + // Top left of green + screenCoords = stageSize * 0.5f - actorSize * 0.25f + Vector2(ELLIPSION, ELLIPSION); + built = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(-0.240308f, -0.135174f, -0.961239f), 0.01f, TEST_LOCATION); + + // Bottom right of green + screenCoords = stageSize * 0.5f + actorSize * 0.75f - Vector2(ELLIPSION, ELLIPSION); + built = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(0.240308f, 0.135174f, -0.961239f), 0.01f, TEST_LOCATION); + + // Rotate green + green.SetProperty(Actor::Property::ORIENTATION, Quaternion(Radian(Degree(90.0f)), Vector3::ZAXIS)); + + // Render and notify + application.SendNotification(); + application.Render(0); + + // Top left of green, but ray directoin is bottom left + screenCoords.x = stageSize.width * 0.5f + actorSize.width * 0.25f - actorSize.height * 0.5f + ELLIPSION; + screenCoords.y = stageSize.height * 0.5f + actorSize.height * 0.25f - actorSize.width * 0.5f + ELLIPSION; + built = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(-0.240308f, 0.135174f, -0.961239f), 0.01f, TEST_LOCATION); + + // Bottom right of green, but ray direction is top right + screenCoords.x = stageSize.width * 0.5f + actorSize.width * 0.25f + actorSize.height * 0.5f - ELLIPSION; + screenCoords.y = stageSize.height * 0.5f + actorSize.height * 0.25f + actorSize.width * 0.5f - ELLIPSION; + built = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, true, TEST_LOCATION); + DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION); + direction.Normalize(); + DALI_TEST_EQUALS(direction, Vector3(0.240308f, -0.135174f, -0.961239f), 0.01f, TEST_LOCATION); + + // Out of green. BuildPickingRay failed. + screenCoords = stageSize * 0.5f - actorSize * 0.5f; + built = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction); + DALI_TEST_EQUALS(built, false, TEST_LOCATION); + + END_TEST; +} diff --git a/automated-tests/src/dali/utc-Dali-GeoHoverProcessing.cpp b/automated-tests/src/dali/utc-Dali-GeoHoverProcessing.cpp new file mode 100644 index 0000000..625788e --- /dev/null +++ b/automated-tests/src/dali/utc-Dali-GeoHoverProcessing.cpp @@ -0,0 +1,1387 @@ +/* + * Copyright (c) 2023 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace Dali; + +void utc_dali_geo_hover_processing_startup(void) +{ + test_return_value = TET_UNDEF; +} + +void utc_dali_geo_hover_processing_cleanup(void) +{ + test_return_value = TET_PASS; +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace +{ +// Stores data that is populated in the callback and will be read by the TET cases +struct SignalData +{ + SignalData() + : functorCalled(false), + hoverEvent(), + hoveredActor() + { + } + + void Reset() + { + functorCalled = false; + hoverEvent.Reset(); + hoveredActor.Reset(); + } + + bool functorCalled; + HoverEvent hoverEvent; + Actor hoveredActor; +}; + +// Functor that sets the data when called +struct HoverEventFunctor +{ + /** + * Constructor. + * @param[in] data Reference to the data to store callback information. + * @param[in] returnValue What the functor should return. + */ + HoverEventFunctor(SignalData& data, bool returnValue = true) + : signalData(data), + returnValue(returnValue) + { + } + + bool operator()(Actor actor, const HoverEvent& hoverEvent) + { + signalData.functorCalled = true; + signalData.hoveredActor = actor; + signalData.hoverEvent = hoverEvent; + + return returnValue; + } + + SignalData& signalData; + bool returnValue; +}; + +// Functor that removes the actor when called. +struct RemoveActorFunctor : public HoverEventFunctor +{ + /** + * Constructor. + * @param[in] data Reference to the data to store callback information. + * @param[in] returnValue What the functor should return. + */ + RemoveActorFunctor(SignalData& data, bool returnValue = true) + : HoverEventFunctor(data, returnValue) + { + } + + bool operator()(Actor actor, const HoverEvent& hoverEvent) + { + Actor parent(actor.GetParent()); + if(parent) + { + parent.Remove(actor); + } + + return HoverEventFunctor::operator()(actor, hoverEvent); + } +}; + +Integration::HoverEvent GenerateSingleHover(PointState::Type state, const Vector2& screenPosition) +{ + Integration::HoverEvent hoverEvent; + Integration::Point point; + point.SetState(state); + point.SetScreenPosition(screenPosition); + hoverEvent.points.push_back(point); + return hoverEvent; +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// + +int UtcDaliGeoHoverNormalProcessing(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + Vector2 screenCoordinates(10.0f, 10.0f); + Vector2 localCoordinates; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, data.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(localCoordinates, data.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(0, data.hoverEvent.GetDeviceId(0), TEST_LOCATION); + DALI_TEST_EQUALS(0u, data.hoverEvent.GetTime(), TEST_LOCATION); + DALI_TEST_EQUALS(actor, data.hoverEvent.GetHitActor(0), TEST_LOCATION); + DALI_TEST_EQUALS(-1, data.hoverEvent.GetDeviceId(1), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::FINISHED, data.hoverEvent.GetState(1), TEST_LOCATION); + DALI_TEST_EQUALS(Vector2::ZERO, data.hoverEvent.GetScreenPosition(1), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2::ZERO, data.hoverEvent.GetLocalPosition(1), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(Dali::Actor(), data.hoverEvent.GetHitActor(1), TEST_LOCATION); + data.Reset(); + + // Emit a motion signal + screenCoordinates.x = screenCoordinates.y = 11.0f; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::MOTION, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, data.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(localCoordinates, data.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(0, data.hoverEvent.GetDeviceId(0), TEST_LOCATION); + DALI_TEST_EQUALS(0u, data.hoverEvent.GetTime(), TEST_LOCATION); + DALI_TEST_EQUALS(actor, data.hoverEvent.GetHitActor(0), TEST_LOCATION); + DALI_TEST_EQUALS(-1, data.hoverEvent.GetDeviceId(1), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::FINISHED, data.hoverEvent.GetState(1), TEST_LOCATION); + DALI_TEST_EQUALS(Vector2::ZERO, data.hoverEvent.GetScreenPosition(1), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2::ZERO, data.hoverEvent.GetLocalPosition(1), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(Dali::Actor(), data.hoverEvent.GetHitActor(1), TEST_LOCATION); + data.Reset(); + + // Emit a finished signal + screenCoordinates.x = screenCoordinates.y = 12.0f; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleHover(PointState::FINISHED, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::FINISHED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, data.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(localCoordinates, data.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(0, data.hoverEvent.GetDeviceId(0), TEST_LOCATION); + DALI_TEST_EQUALS(0u, data.hoverEvent.GetTime(), TEST_LOCATION); + DALI_TEST_EQUALS(actor, data.hoverEvent.GetHitActor(0), TEST_LOCATION); + DALI_TEST_EQUALS(-1, data.hoverEvent.GetDeviceId(1), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::FINISHED, data.hoverEvent.GetState(1), TEST_LOCATION); + DALI_TEST_EQUALS(Vector2::ZERO, data.hoverEvent.GetScreenPosition(1), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2::ZERO, data.hoverEvent.GetLocalPosition(1), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(Dali::Actor(), data.hoverEvent.GetHitActor(1), TEST_LOCATION); + data.Reset(); + + // Emit a started signal where the actor is not present + screenCoordinates.x = screenCoordinates.y = 200.0f; + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, screenCoordinates)); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(!data.hoverEvent); + + END_TEST; +} + +int UtcDaliGeoHoverOutsideCameraNearFarPlanes(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + Integration::Scene stage = application.GetScene(); + Vector2 stageSize = stage.GetSize(); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + stage.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Get the camera's near and far planes + RenderTaskList taskList = stage.GetRenderTaskList(); + Dali::RenderTask task = taskList.GetTask(0); + CameraActor camera = task.GetCameraActor(); + float nearPlane = camera.GetNearClippingPlane(); + float farPlane = camera.GetFarClippingPlane(); + + // Calculate the current distance of the actor from the camera + float tanHalfFov = tanf(camera.GetFieldOfView() * 0.5f); + float distance = (stageSize.y * 0.5f) / tanHalfFov; + + // Connect to actor's hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + Vector2 screenCoordinates(stageSize.x * 0.5f, stageSize.y * 0.5f); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Emit a started signal where actor is just at the camera's near plane + actor.SetProperty(Actor::Property::POSITION_Z, distance - nearPlane); + + // Render and notify + application.SendNotification(); + application.Render(); + + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Emit a started signal where actor is closer than the camera's near plane + actor.SetProperty(Actor::Property::POSITION_Z, (distance - nearPlane) + 1.0f); + + // Render and notify + application.SendNotification(); + application.Render(); + + // When hover event leave outside of actor, the actor receive a Leave event + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::LEAVE, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + // Emit a started signal where actor is just at the camera's far plane + actor.SetProperty(Actor::Property::POSITION_Z, distance - farPlane); + + // Render and notify + application.SendNotification(); + application.Render(); + + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Emit a started signal where actor is further than the camera's far plane + actor.SetProperty(Actor::Property::POSITION_Z, (distance - farPlane) - 1.0f); + + // Render and notify + application.SendNotification(); + application.Render(); + + // When hover event leave outside of actor, the actor receive a Leave event + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::LEAVE, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoHoverEmitEmpty(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + try + { + // Emit an empty HoverEvent + Integration::HoverEvent event; + application.ProcessEvent(event); + tet_result(TET_FAIL); + } + catch(Dali::DaliException& e) + { + DALI_TEST_ASSERT(e, "!event.points.empty()", TEST_LOCATION); + } + END_TEST; +} + +int UtcDaliGeoHoverInterrupted(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + // Emit an interrupted signal, we should be signalled regardless of whether there is a hit or not. + application.ProcessEvent(GenerateSingleHover(PointState::INTERRUPTED, Vector2(200.0f, 200.0f /* Outside actor */))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + // Emit another interrupted signal, our signal handler should not be called. + application.ProcessEvent(GenerateSingleHover(PointState::INTERRUPTED, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoHoverParentConsumer(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Actor rootActor(application.GetScene().GetRootLayer()); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data, false); + actor.HoveredSignal().Connect(&application, functor); + + // Connect to root actor's hovered signal + SignalData rootData; + HoverEventFunctor rootFunctor(rootData); // Consumes signal + rootActor.HoveredSignal().Connect(&application, rootFunctor); + + Vector2 screenCoordinates(10.0f, 10.0f); + Vector2 actorCoordinates, rootCoordinates; + actor.ScreenToLocal(actorCoordinates.x, actorCoordinates.y, screenCoordinates.x, screenCoordinates.y); + rootActor.ScreenToLocal(rootCoordinates.x, rootCoordinates.y, screenCoordinates.x, screenCoordinates.y); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(1u, rootData.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, data.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, rootData.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(actorCoordinates, data.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(rootCoordinates, rootData.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoverEvent.GetHitActor(0)); + DALI_TEST_CHECK(actor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Emit a motion signal + screenCoordinates.x = screenCoordinates.y = 11.0f; + actor.ScreenToLocal(actorCoordinates.x, actorCoordinates.y, screenCoordinates.x, screenCoordinates.y); + rootActor.ScreenToLocal(rootCoordinates.x, rootCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(1u, rootData.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::MOTION, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::MOTION, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, data.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, rootData.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(actorCoordinates, data.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(rootCoordinates, rootData.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoverEvent.GetHitActor(0)); + DALI_TEST_CHECK(actor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Emit a finished signal + screenCoordinates.x = screenCoordinates.y = 12.0f; + actor.ScreenToLocal(actorCoordinates.x, actorCoordinates.y, screenCoordinates.x, screenCoordinates.y); + rootActor.ScreenToLocal(rootCoordinates.x, rootCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleHover(PointState::FINISHED, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(1u, rootData.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::FINISHED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::FINISHED, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, data.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, rootData.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(actorCoordinates, data.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(rootCoordinates, rootData.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoverEvent.GetHitActor(0)); + DALI_TEST_CHECK(actor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Emit a started signal where the actor is not present, will hit the root actor though + screenCoordinates.x = screenCoordinates.y = 200.0f; + rootActor.ScreenToLocal(rootCoordinates.x, rootCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, screenCoordinates)); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, rootData.hoverEvent.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, rootData.hoverEvent.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(rootCoordinates, rootData.hoverEvent.GetLocalPosition(0), 0.1f, TEST_LOCATION); + DALI_TEST_CHECK(rootActor == rootData.hoverEvent.GetHitActor(0)); + END_TEST; +} + +int UtcDaliGeoHoverInterruptedParentConsumer(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Actor rootActor(application.GetScene().GetRootLayer()); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data, false); + actor.HoveredSignal().Connect(&application, functor); + + // Connect to root actor's hovered signal + SignalData rootData; + HoverEventFunctor rootFunctor(rootData); // Consumes signal + rootActor.HoveredSignal().Connect(&application, rootFunctor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoverEvent.GetHitActor(0)); + DALI_TEST_CHECK(actor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Emit an interrupted signal + application.ProcessEvent(GenerateSingleHover(PointState::INTERRUPTED, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoverEvent.GetHitActor(0)); + DALI_TEST_CHECK(actor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Emit another started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, rootData.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + rootData.Reset(); + + // Remove actor from Stage + application.GetScene().Remove(actor); + data.Reset(); + rootData.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit an interrupted signal, only root actor's signal should be called. + application.ProcessEvent(GenerateSingleHover(PointState::INTERRUPTED, Vector2(200.0f, 200.0f /* Outside actor */))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_CHECK(rootActor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Emit another interrupted state, none of the signal's should be called. + application.ProcessEvent(GenerateSingleHover(PointState::INTERRUPTED, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, rootData.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoHoverLeave(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + // Emit a motion signal outside of actor, should be signalled with a Leave + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::LEAVE, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + // Another motion outside of actor, no signalling + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(201.0f, 201.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Another motion event inside actor, signalled with start. This is because a new hover event was started on that actor. + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + END_TEST; +} + +int UtcDaliGeoHoverLeaveParentConsumer(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Actor rootActor(application.GetScene().GetRootLayer()); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data, false); + actor.HoveredSignal().Connect(&application, functor); + + // Connect to root actor's hovered signal + SignalData rootData; + HoverEventFunctor rootFunctor(rootData); // Consumes signal + rootActor.HoveredSignal().Connect(&application, rootFunctor); + + // Set actor to require leave events + actor.SetProperty(Actor::Property::LEAVE_REQUIRED, true); + rootActor.SetProperty(Actor::Property::LEAVE_REQUIRED, true); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoverEvent.GetHitActor(0)); + DALI_TEST_CHECK(actor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Emit a motion signal outside of actor, should be signalled with a Leave + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::LEAVE, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::MOTION, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoverEvent.GetHitActor(0)); + DALI_TEST_CHECK(rootActor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Another motion outside of actor, only rootActor signalled + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(201.0f, 201.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::MOTION, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_CHECK(rootActor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Another motion event inside actor, signalled with start. This is because a new hover event was started on that actor. + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::MOTION, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoverEvent.GetHitActor(0)); + DALI_TEST_CHECK(actor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + END_TEST; +} + +int UtcDaliGeoHoverActorBecomesInsensitive(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + // Change actor to insensitive + actor.SetProperty(Actor::Property::SENSITIVE, false); + + // Emit a motion signal, signalled with an interrupted + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoHoverActorBecomesInsensitiveParentConsumer(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Actor rootActor(application.GetScene().GetRootLayer()); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data, false); + actor.HoveredSignal().Connect(&application, functor); + + // Connect to root actor's hovered signal + SignalData rootData; + HoverEventFunctor rootFunctor(rootData, false); + rootActor.HoveredSignal().Connect(&application, rootFunctor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, rootData.hoverEvent.GetState(0), TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoverEvent.GetHitActor(0)); + DALI_TEST_CHECK(actor == rootData.hoverEvent.GetHitActor(0)); + data.Reset(); + rootData.Reset(); + + // Remove actor from Stage + application.GetScene().Remove(actor); + + // Because it was removed, it gets interrupted. + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + rootData.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Make root actor insensitive + rootActor.SetProperty(Actor::Property::SENSITIVE, false); + + // Because it is insensitive, it does not receive the event. + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, rootData.functorCalled, TEST_LOCATION); + data.Reset(); + rootData.Reset(); + + END_TEST; +} + +int UtcDaliGeoHoverActorBecomesUserInteractionDisabled(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::STARTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + // Change actor to disable user interaction. + actor.SetProperty(DevelActor::Property::USER_INTERACTION_ENABLED, false); + + // Emit a motion signal, signalled with an interrupted + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoHoverMultipleLayers(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Actor rootActor(application.GetScene().GetRootLayer()); + + // Connect to actor's hovered signal + SignalData data; + HoverEventFunctor functor(data); + + Layer layer1(Layer::New()); + layer1.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + layer1.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + application.GetScene().Add(layer1); + + Actor actor1(Actor::New()); + actor1.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor1.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + actor1.SetProperty(Actor::Property::POSITION_Z, 1.0f); // Should hit actor1 in this layer + layer1.Add(actor1); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to layer1 and actor1 + layer1.HoveredSignal().Connect(&application, functor); + actor1.HoveredSignal().Connect(&application, functor); + + // Hit in hittable area, actor1 should be hit + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.hoveredActor == actor1); + data.Reset(); + + // Make layer1 insensitive, nothing should be hit + layer1.SetProperty(Actor::Property::SENSITIVE, false); + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Make layer1 sensitive again, again actor1 will be hit + layer1.SetProperty(Actor::Property::SENSITIVE, true); + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.hoveredActor == actor1); + data.Reset(); + + // Make rootActor insensitive, nothing should be hit + rootActor.SetProperty(Actor::Property::SENSITIVE, false); + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Make rootActor sensitive + rootActor.SetProperty(Actor::Property::SENSITIVE, true); + + // Add another layer + Layer layer2(Layer::New()); + layer2.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + layer2.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + layer2.SetProperty(Actor::Property::POSITION_Z, 10.0f); // Should hit layer2 in this layer rather than actor2 + application.GetScene().Add(layer2); + + Actor actor2(Actor::New()); + actor2.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor2.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + layer2.Add(actor2); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to layer2 and actor2 + layer2.HoveredSignal().Connect(&application, functor); + actor2.HoveredSignal().Connect(&application, functor); + + // Emit an event, should hit layer2 + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + //DALI_TEST_CHECK( data.hoveredActor == layer2 ); // TODO: Uncomment this after removing renderable hack! + data.Reset(); + + // Make layer2 insensitive, should hit actor1 + layer2.SetProperty(Actor::Property::SENSITIVE, false); + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.hoveredActor == actor1); + data.Reset(); + + // Make layer2 sensitive again, should hit layer2 + layer2.SetProperty(Actor::Property::SENSITIVE, true); + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + //DALI_TEST_CHECK( data.hoveredActor == layer2 ); // TODO: Uncomment this after removing renderable hack! + data.Reset(); + + // Make layer2 invisible, render and notify + layer2.SetProperty(Actor::Property::VISIBLE, false); + application.SendNotification(); + application.Render(); + + // Should hit actor1 + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.hoveredActor == actor1); + data.Reset(); + + // Make rootActor invisible, render and notify + rootActor.SetProperty(Actor::Property::VISIBLE, false); + + // Because visible became false, we receive interrupted + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + application.SendNotification(); + application.Render(); + + // Should not hit anything + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoHoverMultipleRenderTasks(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Integration::Scene stage(application.GetScene()); + Vector2 stageSize(stage.GetSize()); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + stage.Add(actor); + + // Create render task + Viewport viewport(stageSize.width * 0.5f, stageSize.height * 0.5f, stageSize.width * 0.5f, stageSize.height * 0.5f); + RenderTask renderTask(application.GetScene().GetRenderTaskList().CreateTask()); + renderTask.SetViewport(viewport); + renderTask.SetInputEnabled(true); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Ensure renderTask actor can be hit too. + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(viewport.x + 5.0f, viewport.y + 5.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Disable input on renderTask, should not be hittable + renderTask.SetInputEnabled(false); + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(viewport.x + 5.0f, viewport.y + 5.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::LEAVE, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoHoverMultipleRenderTasksWithChildLayer(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Integration::Scene stage(application.GetScene()); + Vector2 stageSize(stage.GetSize()); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + stage.Add(actor); + + Layer layer = Layer::New(); + layer.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + layer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + actor.Add(layer); + + // Create render task + Viewport viewport(stageSize.width * 0.5f, stageSize.height * 0.5f, stageSize.width * 0.5f, stageSize.height * 0.5f); + RenderTask renderTask(application.GetScene().GetRenderTaskList().CreateTask()); + renderTask.SetViewport(viewport); + renderTask.SetInputEnabled(true); + renderTask.SetSourceActor(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to layer's hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + layer.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Ensure renderTask actor can be hit too. + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(viewport.x + 5.0f, viewport.y + 5.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Disable input on renderTask, should not be hittable + renderTask.SetInputEnabled(false); + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(viewport.x + 5.0f, viewport.y + 5.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::LEAVE, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoHoverOffscreenRenderTasks(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Integration::Scene stage(application.GetScene()); + Vector2 stageSize(stage.GetSize()); + + // FrameBufferImage for offscreen RenderTask + FrameBuffer frameBuffer = FrameBuffer::New(stageSize.width, stageSize.height); + + // Create a renderable actor to display the FrameBufferImage + Actor renderableActor = CreateRenderableActor(frameBuffer.GetColorTexture()); + renderableActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + renderableActor.SetProperty(Actor::Property::SIZE, Vector2(stageSize.x, stageSize.y)); + renderableActor.ScaleBy(Vector3(1.0f, -1.0f, 1.0f)); // FIXME + stage.Add(renderableActor); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + stage.Add(actor); + application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE); // Ensure framebuffer connects + + stage.GetRenderTaskList().GetTask(0u).SetScreenToFrameBufferFunction(RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION); + + // Create a RenderTask + RenderTask renderTask = stage.GetRenderTaskList().CreateTask(); + renderTask.SetSourceActor(actor); + renderTask.SetFrameBuffer(frameBuffer); + renderTask.SetInputEnabled(true); + + // Create another RenderTask + RenderTask renderTask2(stage.GetRenderTaskList().CreateTask()); + renderTask2.SetInputEnabled(true); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoHoverMultipleRenderableActors(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Integration::Scene stage(application.GetScene()); + Vector2 stageSize(stage.GetSize()); + + Actor parent = CreateRenderableActor(); + parent.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + parent.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + stage.Add(parent); + + Actor actor = CreateRenderableActor(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + parent.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to layer's hovered signal + SignalData data; + HoverEventFunctor functor(data); + parent.HoveredSignal().Connect(&application, functor); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.hoveredActor); + END_TEST; +} + +int UtcDaliGeoHoverActorRemovedInSignal(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + 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 hovered signal + SignalData data; + RemoveActorFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Register for leave events + actor.SetProperty(Actor::Property::LEAVE_REQUIRED, true); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Re-add, render and notify + application.GetScene().Add(actor); + application.SendNotification(); + application.Render(); + + // Emit another signal outside of actor's area, should not get anything as the scene has changed. + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(210.0f, 210.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit another signal outside of actor's area, should not get anything as the scene has changed. + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(210.0f, 210.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Re-add actor back to stage, render and notify + application.GetScene().Add(actor); + application.SendNotification(); + application.Render(); + + // Emit another started event + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Completely delete the actor + actor.Reset(); + + // Emit event, should not crash and should not receive an event. + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(210.0f, 210.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoHoverActorSignalNotConsumed(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data, false); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoHoverActorUnStaged(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + + 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 hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started signal + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Remove actor from stage + application.GetScene().Remove(actor); + + // Interrupted is received because the actor receiving the event removed. + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit a move at the same point, we should not be signalled. + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoHoverLeaveActorReadded(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Integration::Scene stage = application.GetScene(); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + stage.Add(actor); + + // Set actor to receive hover-events + actor.SetProperty(Actor::Property::LEAVE_REQUIRED, true); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started and motion + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(11.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Remove actor from stage and add again + stage.Remove(actor); + stage.Add(actor); + + // Emit a motion within the actor's bounds + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(12.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Emit a motion outside the actor's bounds + application.ProcessEvent(GenerateSingleHover(PointState::MOTION, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::LEAVE, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + END_TEST; +} + +int UtcDaliGeoHoverClippingActor(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Integration::Scene stage = application.GetScene(); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + stage.Add(actor); + + Actor clippingActor = Actor::New(); + clippingActor.SetProperty(Actor::Property::SIZE, Vector2(50.0f, 50.0f)); + clippingActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + clippingActor.SetProperty(Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_CHILDREN); + stage.Add(clippingActor); + + // Add a child to the clipped region. + Actor clippingChild = Actor::New(); + clippingChild.SetProperty(Actor::Property::SIZE, Vector2(50.0f, 50.0f)); + clippingChild.SetProperty(Actor::Property::POSITION, Vector2(25.0f, 25.0f)); + clippingChild.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + clippingActor.Add(clippingChild); + + // Render and notify. + application.SendNotification(); + application.Render(); + + // Connect to actor's hovered signal. + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit an event within clipped area - we should have a hit. + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Emit an event outside the clipped area but within the actor area, we should have a hit. + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(60.0f, 60.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + clippingChild.HoveredSignal().Connect(&application, functor); + + // Emit an event inside part of the child which is within the clipped area, we should have a hit. + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(30.0f, 30.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + END_TEST; +} + +int UtcDaliGeoHoverActorHide(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + Integration::Scene stage = application.GetScene(); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + stage.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's hovered signal + SignalData data; + HoverEventFunctor functor(data); + actor.HoveredSignal().Connect(&application, functor); + + // Emit a started + application.ProcessEvent(GenerateSingleHover(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + actor.SetProperty(Actor::Property::VISIBLE, false); + + // flush the queue and render once + application.SendNotification(); + application.Render(); + + // Interrupted is received because the actor receiving the event hides. + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.hoverEvent.GetState(0), TEST_LOCATION); + data.Reset(); + + END_TEST; +} diff --git a/automated-tests/src/dali/utc-Dali-GeoTouchProcessing.cpp b/automated-tests/src/dali/utc-Dali-GeoTouchProcessing.cpp new file mode 100644 index 0000000..51f22e8 --- /dev/null +++ b/automated-tests/src/dali/utc-Dali-GeoTouchProcessing.cpp @@ -0,0 +1,2378 @@ +/* + * Copyright (c) 2023 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace Dali; + +void utc_dali_geo_touch_processing_startup(void) +{ + test_return_value = TET_UNDEF; +} + +void utc_dali_geo_touch_processing_cleanup(void) +{ + test_return_value = TET_PASS; +} + +/////////////////////////////////////////////////////////////////////////////// + +namespace +{ +struct TestPoint +{ + int32_t deviceId{-1}; + PointState::Type state{PointState::FINISHED}; + Actor hitActor; + Vector2 local; + Vector2 screen; + float radius{0}; + Vector2 ellipseRadius; + float pressure{0}; + Degree angle; + Device::Class::Type deviceClass{Device::Class::NONE}; + Device::Subclass::Type deviceSubclass{Device::Subclass::NONE}; + + TestPoint() = default; + static const TestPoint ZERO; +}; + +const TestPoint TestPoint::ZERO; + +// Stores data that is populated in the callback and will be read by the TET cases +struct SignalData +{ + SignalData() + : functorCalled(false), + receivedTouch(), + touchedActor() + { + } + + struct TestTouchEvent + { + unsigned long time; + std::vector points; + + const TestPoint& GetPoint(size_t i) + { + if(i < points.size()) + { + return points[i]; + } + return TestPoint::ZERO; + } + size_t GetPointCount() + { + return points.size(); + } + }; + + void Reset() + { + functorCalled = false; + + receivedTouch.time = 0u; + receivedTouch.points.clear(); + + touchedActor.Reset(); + } + + bool functorCalled; + TestTouchEvent receivedTouch; + Actor touchedActor; +}; + +// Functor that sets the data when called +struct TouchEventFunctor +{ + /** + * Constructor. + * @param[in] data Reference to the data to store callback information. + * @param[in] returnValue What the functor should return. + */ + TouchEventFunctor(SignalData& data, bool returnValue = true) + : signalData(data), + returnValue(returnValue) + { + } + + bool operator()(Actor actor, const TouchEvent& touch) + { + signalData.functorCalled = true; + signalData.touchedActor = actor; + + signalData.receivedTouch.time = touch.GetTime(); + signalData.receivedTouch.points.clear(); + + for(size_t i = 0; i < touch.GetPointCount(); ++i) + { + TestPoint p; + p.deviceId = touch.GetDeviceId(i); + p.state = touch.GetState(i); + p.hitActor = touch.GetHitActor(i); + p.local = touch.GetLocalPosition(i); + p.screen = touch.GetScreenPosition(i); + p.radius = touch.GetRadius(i); + p.ellipseRadius = touch.GetEllipseRadius(i); + p.pressure = touch.GetPressure(i); + p.angle = touch.GetAngle(i); + p.deviceClass = touch.GetDeviceClass(i); + p.deviceSubclass = touch.GetDeviceSubclass(i); + signalData.receivedTouch.points.push_back(p); + } + + return returnValue; + } + + SignalData& signalData; + bool returnValue; +}; + +struct HandleData +{ + bool signalReceived; + TouchEvent receivedTouchHandle; + + HandleData() + : signalReceived(false) + { + } +}; + +struct TouchEventHandleFunctor +{ + /** + * Constructor. + * @param[in] data Reference to the data to store callback information. + * @param[in] returnValue What the functor should return. + */ + TouchEventHandleFunctor(HandleData& handleData, bool returnValue = true) + : handleData(handleData), + returnValue(returnValue) + { + } + + bool operator()(Actor actor, const TouchEvent& someTouchEvent) + { + handleData.signalReceived = true; + handleData.receivedTouchHandle = someTouchEvent; + return returnValue; + } + + HandleData& handleData; + bool returnValue; +}; + +// Functor that removes the actor when called. +struct RemoveActorFunctor : public TouchEventFunctor +{ + /** + * Constructor. + * @param[in] data Reference to the data to store callback information. + * @param[in] returnValue What the functor should return. + */ + RemoveActorFunctor(SignalData& data, bool returnValue = true) + : TouchEventFunctor(data, returnValue) + { + } + + bool operator()(Actor actor, const TouchEvent& touch) + { + Actor parent(actor.GetParent()); + if(parent) + { + parent.Remove(actor); + } + + return TouchEventFunctor::operator()(actor, touch); + } +}; + +struct OutOfBoundsData +{ + TestPoint point; + bool functorCalled; + + OutOfBoundsData() + : functorCalled(false) + { + } +}; + +// Functor that reads out of bounds data when called +struct OutOfBoundsFunctor +{ + /** + * Constructor. + * @param[in] data Reference to the data to store callback information. + * @param[in] returnValue What the functor should return. + */ + OutOfBoundsFunctor(OutOfBoundsData& data, bool returnValue = true) + : outOfBoundsData(data), + returnValue(returnValue) + { + } + + bool operator()(Actor actor, const TouchEvent& touch) + { + outOfBoundsData.functorCalled = true; + size_t count = touch.GetPointCount(); + + // Read out of bounds data + outOfBoundsData.point.deviceId = touch.GetDeviceId(count + 1); + outOfBoundsData.point.state = touch.GetState(count + 1); + outOfBoundsData.point.hitActor = touch.GetHitActor(count + 1); + outOfBoundsData.point.local = touch.GetLocalPosition(count + 1); + outOfBoundsData.point.screen = touch.GetScreenPosition(count + 1); + + return returnValue; + } + + OutOfBoundsData& outOfBoundsData; + bool returnValue; +}; + +Integration::TouchEvent GenerateSingleTouch(PointState::Type state, const Vector2& screenPosition) +{ + Integration::TouchEvent touchEvent; + Integration::Point point; + point.SetState(state); + point.SetScreenPosition(screenPosition); + point.SetDeviceClass(Device::Class::TOUCH); + point.SetDeviceSubclass(Device::Subclass::NONE); + touchEvent.points.push_back(point); + return touchEvent; +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// + +int UtcDaliGeoTouchEventNormalProcessing01(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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 touch signal + SignalData data; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + Vector2 screenCoordinates(10.0f, 10.0f); + Vector2 localCoordinates; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + const TestPoint* point1 = &data.receivedTouch.GetPoint(0); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.receivedTouch.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, point1->state, TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, point1->screen, TEST_LOCATION); + DALI_TEST_EQUALS(localCoordinates, point1->local, 0.1f, TEST_LOCATION); + data.Reset(); + + // Emit a motion signal + screenCoordinates.x = screenCoordinates.y = 11.0f; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, screenCoordinates)); + const TestPoint* point2 = &data.receivedTouch.GetPoint(0); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.receivedTouch.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::MOTION, point2->state, TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, point2->screen, TEST_LOCATION); + DALI_TEST_EQUALS(localCoordinates, point2->local, 0.1f, TEST_LOCATION); + data.Reset(); + + // Emit an up signal + screenCoordinates.x = screenCoordinates.y = 12.0f; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, screenCoordinates)); + const TestPoint* point3 = &data.receivedTouch.GetPoint(0); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.receivedTouch.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::UP, point3->state, TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, point3->screen, TEST_LOCATION); + DALI_TEST_EQUALS(localCoordinates, point3->local, 0.1f, TEST_LOCATION); + data.Reset(); + + // Emit a down signal where the actor is not present + screenCoordinates.x = screenCoordinates.y = 200.0f; + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoTouchEventNormalProcessing02(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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 + HandleData handleData; + TouchEventHandleFunctor functor(handleData); + actor.TouchedSignal().Connect(&application, functor); + + Vector2 screenCoordinates(10.0f, 10.0f); + Vector2 localCoordinates; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + DALI_TEST_EQUALS(true, handleData.signalReceived, TEST_LOCATION); + DALI_TEST_EQUALS(1u, handleData.receivedTouchHandle.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, handleData.receivedTouchHandle.GetState(0), TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, handleData.receivedTouchHandle.GetScreenPosition(0), TEST_LOCATION); + DALI_TEST_EQUALS(localCoordinates, handleData.receivedTouchHandle.GetLocalPosition(0), 0.1f, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventAPINegative(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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 + OutOfBoundsData data; + OutOfBoundsFunctor functor(data, true); + actor.TouchedSignal().Connect(&application, functor); + + Vector2 screenCoordinates(10.0f, 10.0f); + Vector2 localCoordinates; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(-1, data.point.deviceId, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::FINISHED, data.point.state, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2::ZERO, data.point.screen, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2::ZERO, data.point.local, 0.1f, TEST_LOCATION); + DALI_TEST_CHECK(!data.point.hitActor); + + END_TEST; +} + +int UtcDaliGeoTouchEventOutsideCameraNearFarPlanes(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Integration::Scene scene = application.GetScene(); + Vector2 sceneSize = scene.GetSize(); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER); + actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + scene.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Get the camera's near and far planes + RenderTaskList taskList = scene.GetRenderTaskList(); + Dali::RenderTask task = taskList.GetTask(0); + CameraActor camera = task.GetCameraActor(); + float nearPlane = camera.GetNearClippingPlane(); + float farPlane = camera.GetFarClippingPlane(); + + // Calculate the current distance of the actor from the camera + float tanHalfFov = tanf(camera.GetFieldOfView() * 0.5f); + float distance = (sceneSize.y * 0.5f) / tanHalfFov; + + // Connect to actor's touched signal + SignalData data; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + Vector2 screenCoordinates(sceneSize.x * 0.5f, sceneSize.y * 0.5f); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, screenCoordinates)); + data.Reset(); + + // Emit a down signal where actor is just at the camera's near plane + actor.SetProperty(Actor::Property::POSITION_Z, distance - nearPlane); + + // Render and notify + application.SendNotification(); + application.Render(); + + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, screenCoordinates)); + data.Reset(); + + // Emit a down signal where actor is closer than the camera's near plane + actor.SetProperty(Actor::Property::POSITION_Z, (distance - nearPlane) + 1.0f); + + // Render and notify + application.SendNotification(); + application.Render(); + + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, screenCoordinates)); + data.Reset(); + + // Emit a down signal where actor is just at the camera's far plane + actor.SetProperty(Actor::Property::POSITION_Z, distance - farPlane); + + // Render and notify + application.SendNotification(); + application.Render(); + + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, screenCoordinates)); + data.Reset(); + + // Emit a down signal where actor is further than the camera's far plane + actor.SetProperty(Actor::Property::POSITION_Z, (distance - farPlane) - 1.0f); + + // Render and notify + application.SendNotification(); + application.Render(); + + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, screenCoordinates)); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoTouchEventEmitEmpty(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + try + { + // Emit an empty TouchEvent + Integration::TouchEvent event; + application.ProcessEvent(event); + tet_result(TET_FAIL); + } + catch(Dali::DaliException& e) + { + DALI_TEST_ASSERT(e, "!event.points.empty()", TEST_LOCATION); + } + END_TEST; +} + +int UtcDaliGeoTouchEventInterrupted(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + data.Reset(); + + // Emit an interrupted signal, we should be signalled regardless of whether there is a hit or not. + application.ProcessEvent(GenerateSingleTouch(PointState::INTERRUPTED, Vector2(200.0f, 200.0f /* Outside actor */))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.receivedTouch.points[0].state, TEST_LOCATION); + data.Reset(); + + // Emit another interrupted signal, our signal handler should not be called. + application.ProcessEvent(GenerateSingleTouch(PointState::INTERRUPTED, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoTouchEventParentConsumer(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Actor rootActor(application.GetScene().GetRootLayer()); + + 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; + TouchEventFunctor functor(data, false); + actor.TouchedSignal().Connect(&application, functor); + + // Connect to root actor's touched signal + SignalData rootData; + TouchEventFunctor rootFunctor(rootData); // Consumes signal + rootActor.TouchedSignal().Connect(&application, rootFunctor); + + Vector2 screenCoordinates(10.0f, 10.0f); + Vector2 actorCoordinates, rootCoordinates; + actor.ScreenToLocal(actorCoordinates.x, actorCoordinates.y, screenCoordinates.x, screenCoordinates.y); + rootActor.ScreenToLocal(rootCoordinates.x, rootCoordinates.y, screenCoordinates.x, screenCoordinates.y); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, data.receivedTouch.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(1u, rootData.receivedTouch.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, rootData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, data.receivedTouch.points[0].screen, TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, rootData.receivedTouch.points[0].screen, TEST_LOCATION); + DALI_TEST_EQUALS(actorCoordinates, data.receivedTouch.points[0].local, 0.1f, TEST_LOCATION); + DALI_TEST_EQUALS(rootCoordinates, rootData.receivedTouch.points[0].local, 0.1f, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(actor == rootData.receivedTouch.points[0].hitActor); + data.Reset(); + rootData.Reset(); + + // Emit a motion signal + screenCoordinates.x = screenCoordinates.y = 11.0f; + actor.ScreenToLocal(actorCoordinates.x, actorCoordinates.y, screenCoordinates.x, screenCoordinates.y); + rootActor.ScreenToLocal(rootCoordinates.x, rootCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, screenCoordinates)); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, rootData.receivedTouch.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::MOTION, rootData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, rootData.receivedTouch.points[0].screen, TEST_LOCATION); + DALI_TEST_EQUALS(rootCoordinates, rootData.receivedTouch.points[0].local, 0.1f, TEST_LOCATION); + DALI_TEST_CHECK(rootActor == rootData.receivedTouch.points[0].hitActor); + data.Reset(); + rootData.Reset(); + + // Emit an up signal + screenCoordinates.x = screenCoordinates.y = 12.0f; + actor.ScreenToLocal(actorCoordinates.x, actorCoordinates.y, screenCoordinates.x, screenCoordinates.y); + rootActor.ScreenToLocal(rootCoordinates.x, rootCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, screenCoordinates)); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, rootData.receivedTouch.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::UP, rootData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, rootData.receivedTouch.points[0].screen, TEST_LOCATION); + DALI_TEST_EQUALS(rootCoordinates, rootData.receivedTouch.points[0].local, 0.1f, TEST_LOCATION); + DALI_TEST_CHECK(rootActor == rootData.receivedTouch.points[0].hitActor); + data.Reset(); + rootData.Reset(); + + // Emit a down signal where the actor is not present, will hit the root actor though + screenCoordinates.x = screenCoordinates.y = 200.0f; + rootActor.ScreenToLocal(rootCoordinates.x, rootCoordinates.y, screenCoordinates.x, screenCoordinates.y); + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(1u, rootData.receivedTouch.GetPointCount(), TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, rootData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(screenCoordinates, rootData.receivedTouch.points[0].screen, TEST_LOCATION); + DALI_TEST_EQUALS(rootCoordinates, rootData.receivedTouch.points[0].local, 0.1f, TEST_LOCATION); + DALI_TEST_CHECK(rootActor == rootData.receivedTouch.points[0].hitActor); + END_TEST; +} + +int UtcDaliGeoTouchEventInterruptedParentConsumer(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Actor rootActor(application.GetScene().GetRootLayer()); + + 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; + TouchEventFunctor functor(data, false); + actor.TouchedSignal().Connect(&application, functor); + + // Connect to root actor's touched signal + SignalData rootData; + TouchEventFunctor rootFunctor(rootData); // Consumes signal + rootActor.TouchedSignal().Connect(&application, rootFunctor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, rootData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(actor == rootData.receivedTouch.points[0].hitActor); + data.Reset(); + rootData.Reset(); + + // Emit an interrupted signal + application.ProcessEvent(GenerateSingleTouch(PointState::INTERRUPTED, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, rootData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(rootActor == rootData.receivedTouch.points[0].hitActor); + data.Reset(); + rootData.Reset(); + + // Emit another down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, rootData.receivedTouch.points[0].state, TEST_LOCATION); + data.Reset(); + rootData.Reset(); + + // Remove actor from scene + application.GetScene().Remove(actor); + data.Reset(); + rootData.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit an interrupted signal, only root actor's signal should be called. + application.ProcessEvent(GenerateSingleTouch(PointState::INTERRUPTED, Vector2(200.0f, 200.0f /* Outside actor */))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, rootData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(rootActor == rootData.receivedTouch.points[0].hitActor); + data.Reset(); + rootData.Reset(); + + // Emit another interrupted state, none of the signal's should be called. + application.ProcessEvent(GenerateSingleTouch(PointState::INTERRUPTED, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, rootData.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoTouchEventActorBecomesInsensitive(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + data.Reset(); + + // Change actor to insensitive + actor.SetProperty(Actor::Property::SENSITIVE, false); + + // Emit a motion signal, signalled with an interrupted + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.receivedTouch.points[0].state, TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoTouchEventActorBecomesInsensitiveParentConsumer(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Actor rootActor(application.GetScene().GetRootLayer()); + + 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; + TouchEventFunctor functor(data, false); + actor.TouchedSignal().Connect(&application, functor); + + // Connect to root actor's touched signal + SignalData rootData; + TouchEventFunctor rootFunctor(rootData); // Consumes signal + rootActor.TouchedSignal().Connect(&application, rootFunctor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, rootData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(actor == rootData.receivedTouch.points[0].hitActor); + data.Reset(); + rootData.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Make root actor insensitive + rootActor.SetProperty(Actor::Property::SENSITIVE, false); + + // Emit a motion signal, signalled with an interrupted (should get interrupted even if within root actor) + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, rootData.receivedTouch.points[0].state, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoTouchEventActorBecomesUserInteractionDisabled(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + data.Reset(); + + // Change actor to disable user interaction. + actor.SetProperty(DevelActor::Property::USER_INTERACTION_ENABLED, false); + + // Emit a motion signal, signalled with an interrupted + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(200.0f, 200.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.receivedTouch.points[0].state, TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoTouchEventMultipleLayers(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Actor rootActor(application.GetScene().GetRootLayer()); + + // Connect to actor's touched signal + SignalData data; + TouchEventFunctor functor(data); + + Layer layer1(Layer::New()); + layer1.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + layer1.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + application.GetScene().Add(layer1); + + Actor actor1(Actor::New()); + actor1.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor1.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + actor1.SetProperty(Actor::Property::POSITION_Z, 1.0f); // Should hit actor1 in this layer + layer1.Add(actor1); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to layer1 and actor1 + layer1.TouchedSignal().Connect(&application, functor); + actor1.TouchedSignal().Connect(&application, functor); + + // Hit in hittable area, actor1 should be hit + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.touchedActor == actor1); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Make layer1 insensitive, nothing should be hit + layer1.SetProperty(Actor::Property::SENSITIVE, false); + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Make layer1 sensitive again, again actor1 will be hit + layer1.SetProperty(Actor::Property::SENSITIVE, true); + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.touchedActor == actor1); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Make rootActor insensitive, nothing should be hit + rootActor.SetProperty(Actor::Property::SENSITIVE, false); + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Make rootActor sensitive + rootActor.SetProperty(Actor::Property::SENSITIVE, true); + + // Add another layer + Layer layer2(Layer::New()); + layer2.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + layer2.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + layer2.SetProperty(Actor::Property::POSITION_Z, 10.0f); // Should hit layer2 in this layer rather than actor2 + application.GetScene().Add(layer2); + + Actor actor2(Actor::New()); + actor2.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor2.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + layer2.Add(actor2); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to layer2 and actor2 + layer2.TouchedSignal().Connect(&application, functor); + actor2.TouchedSignal().Connect(&application, functor); + + // Emit an event, should hit layer2 + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + //DALI_TEST_CHECK( data.touchedActor == layer2 ); // TODO: Uncomment this after removing renderable hack! + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Make layer2 insensitive, should hit actor1 + layer2.SetProperty(Actor::Property::SENSITIVE, false); + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.touchedActor == actor1); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Make layer2 sensitive again, should hit layer2 + layer2.SetProperty(Actor::Property::SENSITIVE, true); + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + //DALI_TEST_CHECK( data.touchedActor == layer2 ); // TODO: Uncomment this after removing renderable hack! + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Make layer2 invisible, render and notify + layer2.SetProperty(Actor::Property::VISIBLE, false); + application.SendNotification(); + application.Render(); + + // Should hit actor1 + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.touchedActor == actor1); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Make rootActor invisible, render and notify + rootActor.SetProperty(Actor::Property::VISIBLE, false); + application.SendNotification(); + application.Render(); + + // Should not hit anything + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoTouchEventMultipleRenderTasks(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + + Integration::Scene scene(application.GetScene()); + Vector2 sceneSize(scene.GetSize()); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + scene.Add(actor); + + // Create render task + Viewport viewport(sceneSize.width * 0.5f, sceneSize.height * 0.5f, sceneSize.width * 0.5f, sceneSize.height * 0.5f); + RenderTask renderTask(application.GetScene().GetRenderTaskList().CreateTask()); + renderTask.SetViewport(viewport); + renderTask.SetInputEnabled(true); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's touched signal + SignalData data; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Ensure renderTask actor can be hit too. + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(viewport.x + 5.0f, viewport.y + 5.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Disable input on renderTask, should not be hittable + renderTask.SetInputEnabled(false); + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(viewport.x + 5.0f, viewport.y + 5.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoTouchEventMultipleRenderTasksWithChildLayer(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Integration::Scene scene(application.GetScene()); + Vector2 sceneSize(scene.GetSize()); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + scene.Add(actor); + + Layer layer = Layer::New(); + layer.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + layer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + actor.Add(layer); + + // Create render task + Viewport viewport(sceneSize.width * 0.5f, sceneSize.height * 0.5f, sceneSize.width * 0.5f, sceneSize.height * 0.5f); + RenderTask renderTask(application.GetScene().GetRenderTaskList().CreateTask()); + renderTask.SetViewport(viewport); + renderTask.SetInputEnabled(true); + renderTask.SetSourceActor(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to layer's touched signal + SignalData data; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + layer.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Ensure renderTask actor can be hit too. + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(viewport.x + 5.0f, viewport.y + 5.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Disable input on renderTask, should not be hittable + renderTask.SetInputEnabled(false); + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(viewport.x + 5.0f, viewport.y + 5.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoTouchEventOffscreenRenderTasks(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Integration::Scene scene(application.GetScene()); + Vector2 sceneSize(scene.GetSize()); + + // FrameBufferImage for offscreen RenderTask + FrameBuffer frameBuffer = FrameBuffer::New(sceneSize.width, sceneSize.height); + + // Create a renderable actor to display the FrameBufferImage + Actor renderableActor = CreateRenderableActor(frameBuffer.GetColorTexture()); + renderableActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER); + renderableActor.SetProperty(Actor::Property::SIZE, Vector2(sceneSize.x, sceneSize.y)); + renderableActor.ScaleBy(Vector3(1.0f, -1.0f, 1.0f)); // FIXME + scene.Add(renderableActor); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + scene.Add(actor); + application.GetGlAbstraction().SetCheckFramebufferStatusResult(GL_FRAMEBUFFER_COMPLETE); // Ensure framebuffer connects + + scene.GetRenderTaskList().GetTask(0u).SetScreenToFrameBufferFunction(RenderTask::FULLSCREEN_FRAMEBUFFER_FUNCTION); + + // Create a RenderTask + RenderTask renderTask = scene.GetRenderTaskList().CreateTask(); + renderTask.SetSourceActor(actor); + renderTask.SetFrameBuffer(frameBuffer); + renderTask.SetInputEnabled(true); + + // Create another RenderTask + RenderTask renderTask2(scene.GetRenderTaskList().CreateTask()); + renderTask2.SetInputEnabled(true); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's touched signal + SignalData data; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoTouchEventMultipleRenderableActors(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Integration::Scene scene(application.GetScene()); + Vector2 sceneSize(scene.GetSize()); + + Actor parent = CreateRenderableActor(); + parent.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + parent.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + scene.Add(parent); + + Actor actor = CreateRenderableActor(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + parent.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to layer's touched signal + SignalData data; + TouchEventFunctor functor(data); + parent.TouchedSignal().Connect(&application, functor); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.touchedActor); + END_TEST; +} + +int UtcDaliGeoTouchEventActorRemovedInSignal(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + RemoveActorFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Register for leave events + actor.SetProperty(Actor::Property::LEAVE_REQUIRED, true); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Re-add, render and notify + application.GetScene().Add(actor); + application.SendNotification(); + application.Render(); + + // Emit another signal outside of actor's area, should not get anything as the scene has changed. + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(210.0f, 210.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit another signal outside of actor's area, should not get anything as the scene has changed. + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(210.0f, 210.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Re-add actor back to scene, render and notify + application.GetScene().Add(actor); + application.SendNotification(); + application.Render(); + + // Emit another down event + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Completely delete the actor + actor.Reset(); + + // Emit event, should not crash and should not receive an event. + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(210.0f, 210.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoTouchEventActorSignalNotConsumed(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data, false); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoTouchEventActorRemovedFromScene(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Remove actor from scene + application.GetScene().Remove(actor); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit a move at the same point, we should not be signalled. + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + END_TEST; +} + +int UtcDaliGeoTouchEventLayerConsumesTouch(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Add a layer to overlap the actor + Layer layer = Layer::New(); + layer.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + layer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + application.GetScene().Add(layer); + layer.RaiseToTop(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit a few touch signals + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Set layer to consume all touch + layer.SetProperty(Layer::Property::CONSUMES_TOUCH, true); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit the same signals again, should not receive + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + END_TEST; +} + +int UtcDaliGeoTouchEventClippedActor(void) +{ + TestApplication application; + Integration::Scene scene = application.GetScene(); + + scene.SetGeometryHittestEnabled(true); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + scene.Add(actor); + + Actor clippingActor = Actor::New(); + clippingActor.SetProperty(Actor::Property::SIZE, Vector2(50.0f, 50.0f)); + clippingActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + clippingActor.SetProperty(Actor::Property::CLIPPING_MODE, ClippingMode::CLIP_CHILDREN); + scene.Add(clippingActor); + + // Add a child to the clipped region. + Actor clippingChild = Actor::New(); + clippingChild.SetProperty(Actor::Property::SIZE, Vector2(50.0f, 50.0f)); + clippingChild.SetProperty(Actor::Property::POSITION, Vector2(25.0f, 25.0f)); + clippingChild.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + clippingActor.Add(clippingChild); + + // Render and notify. + application.SendNotification(); + application.Render(); + + // Connect to actor's touch signal. + SignalData data; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit an event within clipped area - we should have a hit. + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(10.0f, 10.0f))); + data.Reset(); + + // Emit an event within clipped child area - we should still have a hit. + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(40.0f, 40.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(40.0f, 40.0f))); + data.Reset(); + + // Now connect to the clippingChild's touch signal + SignalData clippingChildData; + TouchEventFunctor clippingChildFunctor(clippingChildData); + clippingChild.TouchedSignal().Connect(&application, clippingChildFunctor); + + // Emit an event within clipped child area - no hit on actor, but hit on clipped child. + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(40.0f, 40.0f))); + DALI_TEST_EQUALS(true, clippingChildData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(40.0f, 40.0f))); + data.Reset(); + clippingChildData.Reset(); + + // Emit an event outside the clipped area but within the actor area, we should have a hit. + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(60.0f, 60.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(60.0f, 60.0f))); + data.Reset(); + clippingChildData.Reset(); + + // Emit an event inside part of the child which is within the clipped area, we should have a hit on the clipped child but not the actor. + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(30.0f, 30.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, clippingChildData.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(30.0f, 30.0f))); + data.Reset(); + clippingChildData.Reset(); + + END_TEST; +} + +int UtcDaliGeoTouchEventActorUnparented(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Unparent the actor + actor.Unparent(); + + // Should receive an interrupted event + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.receivedTouch.points[0].state, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoTouchEventParentRemovedFromScene(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Actor parent = Actor::New(); + parent.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + parent.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + application.GetScene().Add(parent); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + parent.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's touched signal + SignalData data; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Unparent the parent of the touchable actor + parent.Unparent(); + + // Should receive an interrupted event + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.receivedTouch.points[0].state, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoTouchEventActorRemovedFromSceneDifferentConsumer(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Actor parent = Actor::New(); + parent.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + parent.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + application.GetScene().Add(parent); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + parent.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's touched signal + SignalData data; + TouchEventFunctor functor(data, false /* Do not consume */); + actor.TouchedSignal().Connect(&application, functor); + + // Connect to parent's touched signal + SignalData parentData; + TouchEventFunctor parentFunctor(parentData); + parent.TouchedSignal().Connect(&application, parentFunctor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(actor == data.touchedActor); + DALI_TEST_EQUALS(true, parentData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, parentData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == parentData.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(parent == parentData.touchedActor); + data.Reset(); + parentData.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Unparent the actor + actor.Unparent(); + + // Should receive an interrupted event for both actor & parent + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, parentData.functorCalled, TEST_LOCATION); + data.Reset(); + parentData.Reset(); + + // Readd actor to parent + parent.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit a motion signal + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, parentData.functorCalled, TEST_LOCATION); + data.Reset(); + parentData.Reset(); + + // Parent is now consumer, connect again to the touched signal of the actor so that it becomes the consumer + SignalData secondData; + TouchEventFunctor secondFunctor(secondData /* Consume */); + actor.TouchedSignal().Connect(&application, secondFunctor); + + // Unparent the actor + actor.Unparent(); + + // Should receive an interrupted event for both actor functors & the parent as well as it was last consumer + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, parentData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, secondData.functorCalled, TEST_LOCATION); + data.Reset(); + parentData.Reset(); + secondData.Reset(); + + END_TEST; +} + +int UtcDaliGeoTouchEventInterruptedDifferentConsumer(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Actor rootActor(application.GetScene().GetRootLayer()); + + Actor parent = Actor::New(); + parent.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + parent.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + application.GetScene().Add(parent); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + parent.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's touched signal + SignalData data; + TouchEventFunctor functor(data, false /* Do not consume */); + actor.TouchedSignal().Connect(&application, functor); + + // Connect to parent's touched signal + SignalData parentData; + TouchEventFunctor parentFunctor(parentData, false /* Do not consume */); + parent.TouchedSignal().Connect(&application, parentFunctor); + + // Connect to root's touched signal and consume + SignalData rootData; + TouchEventFunctor rootFunctor(rootData); + rootActor.TouchedSignal().Connect(&application, rootFunctor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(actor == data.touchedActor); + DALI_TEST_EQUALS(true, parentData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, parentData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == parentData.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(parent == parentData.touchedActor); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, rootData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == rootData.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(rootActor == rootData.touchedActor); + data.Reset(); + parentData.Reset(); + rootData.Reset(); + + // Root is now consumer, connect to the touched signal of the parent so that it becomes the consumer + SignalData secondData; + TouchEventFunctor secondFunctor(secondData /* Consume */); + parent.TouchedSignal().Connect(&application, secondFunctor); + + // Emit an interrupted signal, Since rootActor has already comsume, only rootActor gets INTERRUPTED. + application.ProcessEvent(GenerateSingleTouch(PointState::INTERRUPTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, parentData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, rootData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, rootData.receivedTouch.points[0].state, TEST_LOCATION); + data.Reset(); + parentData.Reset(); + rootData.Reset(); + + END_TEST; +} + +int UtcDaliGeoTouchEventGetRadius(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal with an angle + Integration::TouchEvent touchEvent = GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f)); + touchEvent.points[0].SetRadius(100.0f); + application.ProcessEvent(touchEvent); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(100.0f, data.receivedTouch.points[0].radius, TEST_LOCATION); + DALI_TEST_EQUALS(100.0f, data.receivedTouch.points[0].ellipseRadius.x, TEST_LOCATION); + DALI_TEST_EQUALS(100.0f, data.receivedTouch.points[0].ellipseRadius.y, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventGetEllipseRadius(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal with an angle + Integration::TouchEvent touchEvent = GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f)); + touchEvent.points[0].SetRadius(100.0f, Vector2(20.0f, 10.0f)); + application.ProcessEvent(touchEvent); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(100.0f, data.receivedTouch.points[0].radius, TEST_LOCATION); + DALI_TEST_EQUALS(20.0f, data.receivedTouch.points[0].ellipseRadius.x, TEST_LOCATION); + DALI_TEST_EQUALS(10.0f, data.receivedTouch.points[0].ellipseRadius.y, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventGetAngle(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal with an angle + Integration::TouchEvent touchEvent = GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f)); + touchEvent.points[0].SetAngle(Degree(90.0f)); + application.ProcessEvent(touchEvent); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(Degree(90.0f), data.receivedTouch.points[0].angle, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventGetPressure(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal with an angle + Integration::TouchEvent touchEvent = GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f)); + touchEvent.points[0].SetPressure(10.0f); + application.ProcessEvent(touchEvent); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(10.0f, data.receivedTouch.points[0].pressure, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventUsage(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal with an angle + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventGetDeviceAPINegative(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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 + HandleData handleData; + TouchEventHandleFunctor functor(handleData); + actor.TouchedSignal().Connect(&application, functor); + + Vector2 screenCoordinates(10.0f, 10.0f); + Vector2 localCoordinates; + actor.ScreenToLocal(localCoordinates.x, localCoordinates.y, screenCoordinates.x, screenCoordinates.y); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, screenCoordinates)); + + TouchEvent data = handleData.receivedTouchHandle; + DALI_TEST_EQUALS(data.GetDeviceClass(-1), Device::Class::NONE, TEST_LOCATION); + DALI_TEST_EQUALS(data.GetDeviceSubclass(-1), Device::Subclass::NONE, TEST_LOCATION); + END_TEST; +} + +int UtcDaliGeoTouchEventGetMouseButtonPositive(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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 + HandleData handleData; + TouchEventHandleFunctor functor(handleData); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal with MouseButton + Integration::TouchEvent touchEvent = GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f)); + touchEvent.points[0].SetMouseButton(static_cast(3)); + application.ProcessEvent(touchEvent); + + TouchEvent data = handleData.receivedTouchHandle; + DALI_TEST_EQUALS(data.GetMouseButton(0), MouseButton::SECONDARY, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventGetMouseButtonNagative(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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 + HandleData handleData; + TouchEventHandleFunctor functor(handleData); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal with MouseButton + Integration::TouchEvent touchEvent = GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f)); + touchEvent.points[0].SetMouseButton(static_cast(2)); + application.ProcessEvent(touchEvent); + + TouchEvent data = handleData.receivedTouchHandle; + DALI_TEST_EQUALS(data.GetMouseButton(0), MouseButton::TERTIARY, TEST_LOCATION); + DALI_TEST_EQUALS(data.GetMouseButton(3), MouseButton::INVALID, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventCapturePropertySet(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().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 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.receivedTouch.GetPoint(0).state, PointState::FINISHED, TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventIntegNewTouchEvent(void) +{ + TestApplication application; + application.GetScene().SetGeometryHittestEnabled(true); + uint32_t timestamp = 92858u; + TouchPoint tp(1, PointState::STARTED, 34.4f, 123.89f, 5.0f, 7.0f); + Dali::TouchEvent touchEvent = Integration::NewTouchEvent(timestamp, tp); + + DALI_TEST_EQUALS(touchEvent.GetPointCount(), 1u, TEST_LOCATION); + DALI_TEST_EQUALS(touchEvent.GetState(0), PointState::STARTED, TEST_LOCATION); + DALI_TEST_EQUALS(touchEvent.GetLocalPosition(0), Vector2(5.0f, 7.0f), TEST_LOCATION); + DALI_TEST_EQUALS(touchEvent.GetScreenPosition(0), Vector2(34.4f, 123.89f), TEST_LOCATION); + + END_TEST; +} + +int UtcDaliGeoTouchEventIntercept01(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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 intercept touched signal + SignalData data; + TouchEventFunctor functor(data, false /* Do not consume */); + Dali::DevelActor::InterceptTouchedSignal(actor).Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + + // It should be able to receive touch events by registering only InterceptTouchEvent. + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(actor == data.touchedActor); + data.Reset(); + + END_TEST; +} + +int UtcDaliGeoTouchEventIntercept02(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + Actor parent = Actor::New(); + parent.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + parent.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + application.GetScene().Add(parent); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + parent.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Connect to actor's touched signal + SignalData data; + TouchEventFunctor functor(data, false /* Do not consume */); + actor.TouchedSignal().Connect(&application, functor); + + // Connect to parent's touched signal + SignalData parentData; + TouchEventFunctor parentFunctor(parentData, false /* Do not consume */); + parent.TouchedSignal().Connect(&application, parentFunctor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + + data.Reset(); + parentData.Reset(); + + // Connect to parent's intercept touched signal + SignalData interceptData; + TouchEventFunctor interceptFunctor(interceptData, true /* Do intercept */); + Dali::DevelActor::InterceptTouchedSignal(parent).Connect(&application, interceptFunctor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + + // The actor gets interrupted. Because touch is intercepted by parent. + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::INTERRUPTED, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_EQUALS(true, interceptData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, interceptData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == interceptData.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(parent == interceptData.touchedActor); + DALI_TEST_EQUALS(true, parentData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, parentData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == parentData.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(parent == parentData.touchedActor); + data.Reset(); + interceptData.Reset(); + parentData.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit a move signal + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(20.0f, 20.0f))); + + // Since InterceptTouchEvent is not called because it has already been intercepted by the parent, only the parent will receive the touchEvent. + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, interceptData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, parentData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::MOTION, parentData.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == parentData.receivedTouch.points[0].hitActor); + DALI_TEST_CHECK(parent == parentData.touchedActor); + data.Reset(); + interceptData.Reset(); + parentData.Reset(); + + + END_TEST; +} + +int UtcDaliGeoTouchEventIntercept03(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + // Add a layer to overlap the actor + Layer layer = Layer::New(); + layer.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + layer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + application.GetScene().Add(layer); + layer.RaiseToTop(); + + // Set layer to consume all touch + layer.SetProperty(Layer::Property::CONSUMES_TOUCH, true); + + // Render and notify + application.SendNotification(); + application.Render(); + + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + layer.Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + Actor rootActor(application.GetScene().GetRootLayer()); + + // Connect to root actor's intercept touched signal + SignalData sceneData; + TouchEventFunctor sceneFunctor(sceneData); + Dali::DevelActor::InterceptTouchedSignal(rootActor).Connect(&application, sceneFunctor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(10.0f, 10.0f))); + + // Even if the layer is touch consumed, the root actor must be able to intercept touch. + DALI_TEST_EQUALS(true, sceneData.functorCalled, TEST_LOCATION); + sceneData.Reset(); + + + END_TEST; +} + +int UtcDaliGeoTouchAreaOffset(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data, false /* Do not consume */); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(110.0f, 110.0f))); + // The actor touched signal is not called because the touch area is outside actor. + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(110.0f, 110.0f))); + data.Reset(); + + // set a bigger touch area + actor.SetProperty(DevelActor::Property::TOUCH_AREA_OFFSET, Rect(-70, 70, 70, -70)); // left, right, bottom, top + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(150.0f, 150.0f))); + // The actor touched signal is called because the touch area is inside touchArea. + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(150.0f, 150.0f))); + data.Reset(); + + // set a offset touch area + actor.SetProperty(DevelActor::Property::TOUCH_AREA_OFFSET, Rect(50, 100, -50, 0)); // left, right, bottom, top + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(190.0f, 25.0f))); + // The actor touched signal is called because the touch area is inside touchArea. + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(190.0f, 25.0f))); + data.Reset(); + + // set a smaller touch area + actor.SetProperty(DevelActor::Property::TOUCH_AREA_OFFSET, Rect(50, 0, 0, 50)); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(40.0f, 40.0f))); + // The actor touched signal is not called because the touch area is outside touchArea. + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(40.0f, 40.0f))); + data.Reset(); + + // Emit a down signal + application.ProcessEvent(GenerateSingleTouch(PointState::DOWN, Vector2(90.0f, 90.0f))); + // The actor touched signal is called because the touch area is inside touchArea. + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(PointState::DOWN, data.receivedTouch.points[0].state, TEST_LOCATION); + DALI_TEST_CHECK(actor == data.receivedTouch.points[0].hitActor); + application.ProcessEvent(GenerateSingleTouch(PointState::UP, Vector2(90.0f, 90.0f))); + data.Reset(); + + END_TEST; +} + +int UtcDaliGeoTouchEventAllowOnlyOwnTouchPropertySet(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // AllowOnlyOwnTouch is default. We don't turn this on/off. + // Now set the only allow own touch property + // actor.SetProperty(DevelActor::Property::ALLOW_ONLY_OWN_TOUCH, true); + + // Emit a down signal outside of actor, we should not receive the event + application.ProcessEvent(GenerateSingleTouch(PointState::STARTED, Vector2(110.0f, 110.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Now motion inside of actor, we should NOT receive the event + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(80.0f, 80.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Up event, should not receive the event + application.ProcessEvent(GenerateSingleTouch(PointState::FINISHED, Vector2(110.0f, 110.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Emit a down signal inside of actor, we should receive the event + application.ProcessEvent(GenerateSingleTouch(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Now motion inside of actor, we should receive the event + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(80.0f, 80.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Now motion outsize of actor, we should receive the event + // CAPTURE_ALL_TOUCH_AFTER_START is now the default policy. We don't turn this on/off. + // So, even though it is outside the actor, it receives the event. + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(110.0f, 110.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Up event, should receive an finished + application.ProcessEvent(GenerateSingleTouch(PointState::FINISHED, Vector2(110.0f, 110.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(data.receivedTouch.GetPoint(0).state, PointState::FINISHED, TEST_LOCATION); + data.Reset(); + + END_TEST; +} + +int UtcDaliGeoTouchEventDispatchTouchMotionPropertySet(void) +{ + TestApplication application; + + application.GetScene().SetGeometryHittestEnabled(true); + + 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; + TouchEventFunctor functor(data); + actor.TouchedSignal().Connect(&application, functor); + + // Emit a down signal actor, we should receive the event + application.ProcessEvent(GenerateSingleTouch(PointState::STARTED, Vector2(10.0f, 10.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(data.receivedTouch.GetPoint(0).state, PointState::STARTED, TEST_LOCATION); + data.Reset(); + + // Emit a motion signal actor, we should receive the event + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(20.0f, 20.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(data.receivedTouch.GetPoint(0).state, PointState::MOTION, TEST_LOCATION); + data.Reset(); + + // Now set the dispatch touch motion property + actor.SetProperty(DevelActor::Property::DISPATCH_TOUCH_MOTION, false); + + // Emit a motion signal actor, we should not receive the event + application.ProcessEvent(GenerateSingleTouch(PointState::MOTION, Vector2(30.0f, 30.0f))); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Up event, should receive the event + application.ProcessEvent(GenerateSingleTouch(PointState::FINISHED, Vector2(40.0f, 40.0f))); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(data.receivedTouch.GetPoint(0).state, PointState::FINISHED, TEST_LOCATION); + data.Reset(); + + data.Reset(); + + END_TEST; +} diff --git a/automated-tests/src/dali/utc-Dali-Scene.cpp b/automated-tests/src/dali/utc-Dali-Scene.cpp index 98bc7b0..3e6df52 100644 --- a/automated-tests/src/dali/utc-Dali-Scene.cpp +++ b/automated-tests/src/dali/utc-Dali-Scene.cpp @@ -2931,3 +2931,105 @@ int UtcDaliSceneEnableDisablePartialUpdate(void) END_TEST; } + +int UtcDaliSceneGeoTouchedEnabledDisabled(void) +{ + TestApplication application; + Dali::Integration::Scene scene = application.GetScene(); + DALI_TEST_EQUALS(scene.IsGeometryHittestEnabled(), false, TEST_LOCATION); + + TouchedSignalData data; + TouchFunctor functor(data); + scene.TouchedSignal().Connect(&application, functor); + + // Render and notify. + application.SendNotification(); + application.Render(); + + // Confirm functor not called before there has been any touch event. + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + + // No actors, single touch, down, motion then up. + { + GenerateTouch(application, PointState::DOWN, Vector2(10.0f, 10.0f)); + + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.receivedTouchEvent.GetPointCount() != 0u); + DALI_TEST_CHECK(!data.receivedTouchEvent.GetHitActor(0)); + + data.Reset(); + + // Confirm there is no signal when the touchpoint is only moved. + GenerateTouch(application, PointState::MOTION, Vector2(1200.0f, 10.0f)); // Some motion + + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Confirm a following up event generates a signal. + GenerateTouch(application, PointState::UP, Vector2(1200.0f, 10.0f)); + + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.receivedTouchEvent.GetPointCount() != 0u); + DALI_TEST_CHECK(!data.receivedTouchEvent.GetHitActor(0)); + data.Reset(); + } + + // Add an actor to the scene. + Actor actor = Actor::New(); + actor.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f)); + actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); + actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); + actor.TouchedSignal().Connect(&DummyTouchCallback); + scene.Add(actor); + + // Render and notify. + application.SendNotification(); + application.Render(); + + // Actor on scene, single touch, down in actor, motion, then up outside actor. + { + GenerateTouch(application, PointState::DOWN, Vector2(10.0f, 10.0f)); + + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.receivedTouchEvent.GetPointCount() != 0u); + DALI_TEST_CHECK(data.receivedTouchEvent.GetHitActor(0) == actor); + data.Reset(); + + GenerateTouch(application, PointState::MOTION, Vector2(150.0f, 10.0f)); // Some motion + + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + GenerateTouch(application, PointState::UP, Vector2(150.0f, 10.0f)); // Some motion + + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.receivedTouchEvent.GetPointCount() != 0u); + DALI_TEST_CHECK(!data.receivedTouchEvent.GetHitActor(0)); + data.Reset(); + } + + scene.SetGeometryHittestEnabled(true); + DALI_TEST_EQUALS(scene.IsGeometryHittestEnabled(), true, TEST_LOCATION); + { + GenerateTouch(application, PointState::DOWN, Vector2(10.0f, 10.0f)); + + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.receivedTouchEvent.GetPointCount() != 0u); + DALI_TEST_CHECK(data.receivedTouchEvent.GetHitActor(0) == actor); + data.Reset(); + + GenerateTouch(application, PointState::MOTION, Vector2(150.0f, 10.0f)); // Some motion + + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + GenerateTouch(application, PointState::UP, Vector2(150.0f, 10.0f)); // Some motion + + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_CHECK(data.receivedTouchEvent.GetPointCount() != 0u); + DALI_TEST_CHECK(data.receivedTouchEvent.GetHitActor(0) == actor); + data.Reset(); + } + + END_TEST; +} diff --git a/dali/devel-api/events/hit-test-algorithm.cpp b/dali/devel-api/events/hit-test-algorithm.cpp index b8714de..9609a1b 100644 --- a/dali/devel-api/events/hit-test-algorithm.cpp +++ b/dali/devel-api/events/hit-test-algorithm.cpp @@ -29,10 +29,10 @@ namespace Dali { namespace HitTestAlgorithm { -bool HitTest(Stage stage, const Vector2& screenCoordinates, Results& results, HitTestFunction func) +bool HitTest(Stage stage, const Vector2& screenCoordinates, Results& results, HitTestFunction func, bool isGeometry) { Internal::Stage& stageImpl = GetImplementation(stage); - return Internal::HitTestAlgorithm::HitTest(stageImpl.GetSize(), stageImpl.GetRenderTaskList(), stageImpl.GetLayerList(), screenCoordinates, results, func); + return Internal::HitTestAlgorithm::HitTest(stageImpl.GetSize(), stageImpl.GetRenderTaskList(), stageImpl.GetLayerList(), screenCoordinates, results, func, isGeometry); } bool BuildPickingRay(RenderTask renderTask, const Vector2& screenCoordinates, Vector3& origin, Vector3& direction) diff --git a/dali/devel-api/events/hit-test-algorithm.h b/dali/devel-api/events/hit-test-algorithm.h index cc7aad9..6481e9e 100644 --- a/dali/devel-api/events/hit-test-algorithm.h +++ b/dali/devel-api/events/hit-test-algorithm.h @@ -139,9 +139,10 @@ using HitTestFunction = bool (*)(Actor, TraverseType); * @param[in] screenCoordinates The screen coordinates. * @param[out] results The results of the hit-test, only modified if something is hit * @param[in] func The function to use in the hit-test algorithm. + * @param[in] isGeometry If true, hittest works in a geometry way. * @return true if something was hit */ -DALI_CORE_API bool HitTest(Stage stage, const Vector2& screenCoordinates, Results& results, HitTestFunction func); +DALI_CORE_API bool HitTest(Stage stage, const Vector2& screenCoordinates, Results& results, HitTestFunction func, bool isGeometry = false); /** * @brief Given screen coordinates, this method returns the camera origin in world coordinates and the direction of the picking ray in world-space. diff --git a/dali/integration-api/scene.cpp b/dali/integration-api/scene.cpp index 960c2a7..5bf411f 100644 --- a/dali/integration-api/scene.cpp +++ b/dali/integration-api/scene.cpp @@ -228,6 +228,16 @@ bool Scene::IsPartialUpdateEnabled() const return GetImplementation(*this).IsPartialUpdateEnabled(); } +void Scene::SetGeometryHittestEnabled(bool enabled) +{ + GetImplementation(*this).SetGeometryHittestEnabled(enabled); +} + +bool Scene::IsGeometryHittestEnabled() +{ + return GetImplementation(*this).IsGeometryHittestEnabled(); +} + Scene::EventProcessingFinishedSignalType& Scene::EventProcessingFinishedSignal() { return GetImplementation(*this).EventProcessingFinishedSignal(); diff --git a/dali/integration-api/scene.h b/dali/integration-api/scene.h index e0ec85f..e0205e3 100644 --- a/dali/integration-api/scene.h +++ b/dali/integration-api/scene.h @@ -416,6 +416,20 @@ public: bool IsPartialUpdateEnabled() const; /** + * @brief Sets whether the processes using geometry event propagation touch and hover events. + * + * @param[in] enabled True if the processes using geometry event propagation touch and hover events. + */ + void SetGeometryHittestEnabled(bool enabled); + + /** + * @brief Queries whether the scene using geometry event propagation touch and hover events. + * + * @return True if the scene using geometry event propagation touch and hover events. + */ + bool IsGeometryHittestEnabled(); + + /** * @brief This signal is emitted just after the event processing is finished. * * @return The signal to connect to diff --git a/dali/internal/event/actors/actor-impl.cpp b/dali/internal/event/actors/actor-impl.cpp index 1574f07..ea5cb70 100644 --- a/dali/internal/event/actors/actor-impl.cpp +++ b/dali/internal/event/actors/actor-impl.cpp @@ -1116,6 +1116,7 @@ Actor::Actor(DerivedType derivedType, const SceneGraph::Node& node) mDrawMode(DrawMode::NORMAL), mColorMode(Node::DEFAULT_COLOR_MODE), mClippingMode(ClippingMode::DISABLED), + mHoverState(PointState::FINISHED), mBlendEquation(DevelBlendEquation::ADD) { } diff --git a/dali/internal/event/actors/actor-impl.h b/dali/internal/event/actors/actor-impl.h index f60e531..cd71a09 100644 --- a/dali/internal/event/actors/actor-impl.h +++ b/dali/internal/event/actors/actor-impl.h @@ -1350,6 +1350,23 @@ public: return mDispatchHoverMotion; } + /** + * @brief Sets the hover state of actor + * @param state The PointState + */ + void SetHoverState(PointState::Type state) + { + mHoverState = state; + } + + /** + * @brief Gets the hover state of actor + * @return PointState::Type + */ + PointState::Type GetHoverState() const + { + return mHoverState; + } // Gestures /** @@ -1978,6 +1995,7 @@ protected: DrawMode::Type mDrawMode : 3; ///< Cached: How the actor and its children should be drawn ColorMode mColorMode : 3; ///< Cached: Determines whether mWorldColor is inherited ClippingMode::Type mClippingMode : 3; ///< Cached: Determines which clipping mode (if any) to use. + PointState::Type mHoverState : 3; ///< Stores the HoverEvent state of actor. DevelBlendEquation::Type mBlendEquation : 16; ///< Cached: Determines which blend equation will be used to render renderers. private: diff --git a/dali/internal/event/common/scene-impl.cpp b/dali/internal/event/common/scene-impl.cpp index 9b34b5f..8808e2f 100644 --- a/dali/internal/event/common/scene-impl.cpp +++ b/dali/internal/event/common/scene-impl.cpp @@ -58,6 +58,7 @@ Scene::Scene() mBackgroundColor(DEFAULT_BACKGROUND_COLOR), mDepthTreeDirty(false), mPartialUpdateEnabled(true), + mGeometryHittest(false), mEventProcessor(*this, ThreadLocalStorage::GetInternal()->GetGestureEventProcessor()), mSurfaceOrientation(0), mScreenOrientation(0) @@ -499,6 +500,16 @@ bool Scene::IsPartialUpdateEnabled() const return mPartialUpdateEnabled; } +void Scene::SetGeometryHittestEnabled(bool enabled) +{ + mGeometryHittest = enabled; +} + +bool Scene::IsGeometryHittestEnabled() const +{ + return mGeometryHittest; +} + Integration::Scene::KeyEventSignalType& Scene::KeyEventSignal() { return mKeyEventSignal; diff --git a/dali/internal/event/common/scene-impl.h b/dali/internal/event/common/scene-impl.h index 12f377a..56b2f7b 100644 --- a/dali/internal/event/common/scene-impl.h +++ b/dali/internal/event/common/scene-impl.h @@ -243,6 +243,16 @@ public: void SetSurfaceRenderTarget(const Graphics::RenderTargetCreateInfo& renderTargetCreateInfo); /** + * @copydoc Dali::Integration::Scene::SetGeometryHittestEnabled + */ + void SetGeometryHittestEnabled(bool enabled); + + /** + * @copydoc Dali::Integration::Scene::IsGeometryHittestEnabled + */ + bool IsGeometryHittestEnabled() const; + + /** * Used by the EventProcessor to emit key event signals. * @param[in] event The key event. */ @@ -423,6 +433,7 @@ private: bool mDepthTreeDirty : 1; ///< True if the depth tree needs recalculating bool mPartialUpdateEnabled : 1; ///< True if the partial update is enabled + bool mGeometryHittest : 1; ///< True if the geometry hittest is enabled EventProcessor mEventProcessor; diff --git a/dali/internal/event/events/gesture-processor.cpp b/dali/internal/event/events/gesture-processor.cpp index 1cf4b26..a2d581c 100644 --- a/dali/internal/event/events/gesture-processor.cpp +++ b/dali/internal/event/events/gesture-processor.cpp @@ -61,7 +61,7 @@ struct GestureHitTestCheck : public HitTestAlgorithm::HitTestInterface return layer->IsTouchConsumed(); } - bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override + bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, bool isGeometry) override { return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp); } diff --git a/dali/internal/event/events/hit-test-algorithm-impl.cpp b/dali/internal/event/events/hit-test-algorithm-impl.cpp index ef398e2..e441aeb 100644 --- a/dali/internal/event/events/hit-test-algorithm-impl.cpp +++ b/dali/internal/event/events/hit-test-algorithm-impl.cpp @@ -89,9 +89,14 @@ struct HitTestFunctionWrapper : public HitTestInterface return false; } - bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override + bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, bool isGeometry) override { - return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp); + // Geometry way does not require Hittest from the client. + if(!isGeometry) + { + return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp); + } + return true; } Dali::HitTestAlgorithm::HitTestFunction mFunc; @@ -120,13 +125,19 @@ struct ActorTouchableCheck : public HitTestInterface return layer->IsTouchConsumed(); } - bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override + bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, bool isGeometry) override { - if(point.GetState() != PointState::STARTED && actor->IsAllowedOnlyOwnTouch() && ownActor != actor) + // The Geometry way behaves like AllowedOnlyOwnTouch is enabled. + if(point.GetState() != PointState::STARTED && (isGeometry || actor->IsAllowedOnlyOwnTouch()) && ownActor != actor) { return false; } - return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp); + // Geometry way does not require Hittest from the client. + if(!isGeometry) + { + return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp); + } + return true; } void SetOwnActor(const Actor* actor) @@ -182,7 +193,8 @@ void HitTestActor(const RenderTask& renderTask, bool overlayedActor, Actor& actor, bool& overlayHit, - HitActor& hit) + HitActor& hit, + bool isGeometry) { if(clippingActor || hitCheck.IsActorHittable(&actor)) { @@ -218,7 +230,7 @@ void HitTestActor(const RenderTask& renderTask, } // If the hit actor does not want to hit, the hit-test continues. - if(hitCheck.ActorRequiresHitResultCheck(&actor, point, hitPointLocal, eventTime)) + if(hitCheck.ActorRequiresHitResultCheck(&actor, point, hitPointLocal, eventTime, isGeometry)) { hit.actor = &actor; hit.hitPosition = hitPointLocal; @@ -298,7 +310,9 @@ HitActor HitTestWithinLayer(Actor& actor, bool layerIs3d, const RayTest& rayTest, const Integration::Point& point, - const uint32_t eventTime) + const uint32_t eventTime, + std::list& actorLists, + bool isGeometry) { HitActor hit; @@ -314,18 +328,35 @@ HitActor HitTestWithinLayer(Actor& actor, bool overlayedActor = overlayed || actor.IsOverlay(); // If we are a clipping actor or hittable... - HitTestActor(renderTask, rayOrigin, rayDir, nearClippingPlane, farClippingPlane, hitCheck, rayTest, point, eventTime, clippingActor, overlayedActor, actor, overlayHit, hit); + HitTestActor(renderTask, rayOrigin, rayDir, nearClippingPlane, farClippingPlane, hitCheck, rayTest, point, eventTime, clippingActor, overlayedActor, actor, overlayHit, hit, isGeometry); // If current actor is clipping, and hit failed, We should not checkup child actors. Fast return if(clippingActor && !(hit.actor)) { return hit; } + else if (isGeometry && hit.actor) + { + // Saves the actors that can be hit as a list + actorLists.push_back(hit.actor); + } // Find a child hit, until we run out of actors in the current layer. HitActor childHit; if(actor.GetChildCount() > 0) { + // If the child touches outside the parent's size boundary, it should not be hit. + if(isGeometry && !actor.IsLayer()) + { + Vector2 hitPointLocal; + float distance; + if(!(rayTest.SphereTest(actor, rayOrigin, rayDir) && + rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))) + { + return hit; + } + } + childHit.distance = std::numeric_limits::max(); childHit.depth = std::numeric_limits::min(); ActorContainer& children = actor.GetChildrenInternal(); @@ -340,20 +371,21 @@ HitActor HitTestWithinLayer(Actor& actor, (hitCheck.DescendActorHierarchy((*iter).Get()))) // We can descend into child hierarchy { HitActor currentHit(HitTestWithinLayer((*iter->Get()), - renderTask, - exclusives, - rayOrigin, - rayDir, - nearClippingPlane, - farClippingPlane, - hitCheck, - overlayedActor, - overlayHit, - layerIs3d, - rayTest, - point, - eventTime)); - + renderTask, + exclusives, + rayOrigin, + rayDir, + nearClippingPlane, + farClippingPlane, + hitCheck, + overlayedActor, + overlayHit, + layerIs3d, + rayTest, + point, + eventTime, + actorLists, + isGeometry)); // 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. @@ -440,6 +472,118 @@ void GetCameraClippingPlane(RenderTask& renderTask, float& nearClippingPlane, fl farClippingPlane = cameraActor->GetFarClippingPlane(); } +void GeoHitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives, + const Vector2& sceneSize, + LayerList& layers, + RenderTask& renderTask, + Vector2 screenCoordinates, + Results& results, + HitTestInterface& hitCheck, + const RayTest& rayTest) +{ + if(renderTask.IsHittable(screenCoordinates)) + { + Viewport viewport; + renderTask.GetHittableViewport(viewport); + + if(screenCoordinates.x < static_cast(viewport.x) || + screenCoordinates.x > static_cast(viewport.x + viewport.width) || + screenCoordinates.y < static_cast(viewport.y) || + screenCoordinates.y > static_cast(viewport.y + viewport.height)) + { + // The screen coordinate is outside the viewport of render task. The viewport clips all layers. + return; + } + + float nearClippingPlane, farClippingPlane; + GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane); + + // Determine the layer depth of the source actor + Actor* sourceActor(renderTask.GetSourceActor()); + if(sourceActor) + { + Dali::Layer sourceLayer(sourceActor->GetLayer()); + if(sourceLayer) + { + const uint32_t sourceActorDepth(sourceLayer.GetProperty(Dali::Layer::Property::DEPTH)); + CameraActor* cameraActor = renderTask.GetCameraActor(); + bool pickingPossible = cameraActor->BuildPickingRay(screenCoordinates, + viewport, + results.rayOrigin, + results.rayDirection); + if(!pickingPossible) + { + return; + } + + // Hit test starting with the top layer, working towards the bottom layer. + bool overlayHit = false; + + for(uint32_t i = 0; i < layers.GetLayerCount(); ++i) + { + Layer* layer(layers.GetLayer(i)); + overlayHit = false; + HitActor hit; + + // Ensure layer is touchable (also checks whether ancestors are also touchable) + 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 == i) + { + // Recursively hit test the source actor & children, without crossing into other layers. + hit = HitTestWithinLayer(*sourceActor, + renderTask, + exclusives, + results.rayOrigin, + results.rayDirection, + nearClippingPlane, + farClippingPlane, + hitCheck, + overlayHit, + overlayHit, + layer->GetBehavior() == Dali::Layer::LAYER_3D, + rayTest, + results.point, + results.eventTime, + results.actorLists, + true); + } + else if(IsWithinSourceActors(*sourceActor, *layer)) + { + // Recursively hit test all the actors, without crossing into other layers. + hit = HitTestWithinLayer(*layer, + renderTask, + exclusives, + results.rayOrigin, + results.rayDirection, + nearClippingPlane, + farClippingPlane, + hitCheck, + overlayHit, + overlayHit, + layer->GetBehavior() == Dali::Layer::LAYER_3D, + rayTest, + results.point, + results.eventTime, + results.actorLists, + true); + } + } + + if(hit.actor) + { + results.renderTask = RenderTaskPtr(&renderTask); + results.actor = Dali::Actor(hit.actor); + results.actorCoordinates = hit.hitPosition; + } + } + } + } + } + return; +} + /** * Hit test a RenderTask */ @@ -536,7 +680,9 @@ bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives, layer->GetBehavior() == Dali::Layer::LAYER_3D, rayTest, results.point, - results.eventTime); + results.eventTime, + results.actorLists, + false); } else if(IsWithinSourceActors(*sourceActor, *layer)) { @@ -555,7 +701,9 @@ bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives, layer->GetBehavior() == Dali::Layer::LAYER_3D, rayTest, results.point, - results.eventTime); + results.eventTime, + results.actorLists, + false); } // If this layer is set to consume the hit, then do not check any layers behind it @@ -603,6 +751,7 @@ bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives, * @param[in] taskList The list of render tasks * @param[out] results Ray information calculated by the camera * @param[in] hitCheck The hit testing interface object to use + * @param[in] isGeometry Whether the scene using geometry event propagation touch and hover events. * @return True if we have a hit, false otherwise */ bool HitTestRenderTaskList(const Vector2& sceneSize, @@ -610,25 +759,44 @@ bool HitTestRenderTaskList(const Vector2& sceneSize, RenderTaskList& taskList, const Vector2& screenCoordinates, Results& results, - HitTestInterface& hitCheck) + HitTestInterface& hitCheck, + bool isGeometry) { - RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks(); - RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend(); - const auto& exclusives = taskList.GetExclusivesList(); - RayTest rayTest; - - // Hit test order should be reverse of draw order - for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter) + if(isGeometry) { - RenderTask& renderTask = *iter->Get(); - if(HitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest)) + RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks(); + RenderTaskList::RenderTaskContainer::iterator endIter = tasks.end(); + const auto& exclusives = taskList.GetExclusivesList(); + RayTest rayTest; + + // Hit test order should be of draw order + for(RenderTaskList::RenderTaskContainer::iterator iter = tasks.begin(); endIter != iter; ++iter) { - // Return true when an actor is hit (or layer in our render-task consumes the hit) - return true; + RenderTask& renderTask = *iter->Get(); + GeoHitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest); } + + return !results.actorLists.empty(); } + else + { + RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks(); + RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend(); + const auto& exclusives = taskList.GetExclusivesList(); + RayTest rayTest; - return false; + // Hit test order should be reverse of draw order + for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter) + { + RenderTask& renderTask = *iter->Get(); + 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; + } + } + return false; + } } /** @@ -639,6 +807,7 @@ bool HitTestRenderTaskList(const Vector2& sceneSize, * @param[in] taskList The list of render tasks * @param[out] results Ray information calculated by the camera * @param[in] hitCheck The hit testing interface object to use + * @param[in] isGeometry Whether the scene using geometry event propagation touch and hover events. * @return True if we have a hit, false otherwise */ bool HitTestForEachRenderTask(const Vector2& sceneSize, @@ -646,11 +815,12 @@ bool HitTestForEachRenderTask(const Vector2& sceneSize, RenderTaskList& taskList, const Vector2& screenCoordinates, Results& results, - HitTestInterface& hitCheck) + HitTestInterface& hitCheck, + bool isGeometry) { bool result = false; - if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck)) + if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck, isGeometry)) { // Found hit. result = true; @@ -663,13 +833,13 @@ bool HitTestForEachRenderTask(const Vector2& sceneSize, HitTestInterface::~HitTestInterface() = default; -bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, 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 isGeometry) { bool wasHit(false); // Hit-test the regular on-scene actors Results hitTestResults; HitTestFunctionWrapper hitTestFunctionWrapper(func); - if(HitTestForEachRenderTask(sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper)) + if(HitTestForEachRenderTask(sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper, isGeometry)) { results.actor = hitTestResults.actor; results.actorCoordinates = hitTestResults.actorCoordinates; @@ -678,23 +848,23 @@ bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& laye return wasHit; } -bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface) +bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface, bool isGeometry) { bool wasHit(false); // Hit-test the regular on-scene actors if(!wasHit) { - wasHit = HitTestForEachRenderTask(sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface); + wasHit = HitTestForEachRenderTask(sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface, isGeometry); } return wasHit; } -bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor) +bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor, bool isGeometry) { ActorTouchableCheck actorTouchableCheck; actorTouchableCheck.SetOwnActor(ownActor); - return HitTest(sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck); + return HitTest(sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck, isGeometry); } } // namespace HitTestAlgorithm diff --git a/dali/internal/event/events/hit-test-algorithm-impl.h b/dali/internal/event/events/hit-test-algorithm-impl.h index 5a4c2fc..7a37204 100644 --- a/dali/internal/event/events/hit-test-algorithm-impl.h +++ b/dali/internal/event/events/hit-test-algorithm-impl.h @@ -38,13 +38,14 @@ namespace HitTestAlgorithm { struct Results { - RenderTaskPtr renderTask; ///< The render-task displaying the actor. - Dali::Actor actor; ///< The hit actor. - Vector2 actorCoordinates; ///< The actor coordinates. - Vector4 rayOrigin; ///< The point of origin of the ray. - Vector4 rayDirection; ///< The direction vector of the ray. - Integration::Point point; ///< The point of event touched. - uint32_t eventTime; ///< The time the event occurred. + RenderTaskPtr renderTask; ///< The render-task displaying the actor. + Dali::Actor actor; ///< The hit actor. + Vector2 actorCoordinates; ///< The actor coordinates. + Vector4 rayOrigin; ///< The point of origin of the ray. + Vector4 rayDirection; ///< The direction vector of the ray. + Integration::Point point; ///< The point of event touched. + uint32_t eventTime; ///< The time the event occurred. + std::list actorLists; ///< If the geometry hittest way is used, a list of actors that can be hit is stored. }; /** @@ -94,10 +95,11 @@ struct HitTestInterface * @param[in] point The point of event touched. * @param[in] hitPointLocal The hit point in the Actor's local reference system. * @param[in] timeStamp The time the event occurred. + * @param[in] isGeometry If true, hittest works in a geometry way. * * @return true if the actor should be the hit, false otherwise. */ - virtual bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) = 0; + virtual bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, bool isGeometry) = 0; protected: /** @@ -115,11 +117,12 @@ protected: * @param[in] screenCoordinates The screen coordinates. * @param[out] results The results of the hit-test. * @param[in] func The function to use in the hit-test algorithm. + * @param[in] isGeometry If true, hittest works in a geometry way. * @return true if something was hit * * @see HitTest(Stage&, const Vector2&, Results&, HitTestInterface&) */ -bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func); +bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func, bool isGeometry = false); /** * Given screen coordinates, this method returns the hit actor & the local coordinates relative to the actor etc. @@ -129,6 +132,7 @@ bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList * @param[in] screenCoordinates The screen coordinates. * @param[out] results The results of the hit-test. * @param[in] hitTestInterface Used to determine whether the actor is hit or whether we walk down its hierarchy + * @param[in] isGeometry If true, hittest works in a geometry way. * @return true if something was hit * *

Hit Test Algorithm:

@@ -145,7 +149,7 @@ bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList * @note Currently, we prefer a child hit over a parent (regardless of the distance from the * camera) unless the parent is a RenderableActor but this is subject to change. */ -bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface); +bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface, bool isGeometry = false); /** * Default HitTest where we check if a touch is required. @@ -156,11 +160,12 @@ bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList * @param[in] screenCoordinates The screen coordinates. * @param[out] results The results of the hit-test. * @param[in] ownActor The actor from which the touch down was started. + * @param[in] isGeometry If true, hittest works in a geometry way. * @return true if something was hit * * @see HitTest(Stage&, const Vector2&, Results&, HitTestInterface&) */ -bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor = nullptr); +bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor = nullptr, bool isGeometry = false); } // namespace HitTestAlgorithm diff --git a/dali/internal/event/events/hover-event-processor.cpp b/dali/internal/event/events/hover-event-processor.cpp index 0b4ae9e..ba824c6 100644 --- a/dali/internal/event/events/hover-event-processor.cpp +++ b/dali/internal/event/events/hover-event-processor.cpp @@ -109,6 +109,49 @@ Dali::Actor EmitHoverSignals(Dali::Actor actor, const Dali::HoverEvent& event) return consumedActor; } +/** + * Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached. + */ +Dali::Actor EmitGeoHoverSignals(std::list& actorLists, const Dali::HoverEvent& hoverEvent) +{ + Dali::Actor consumedActor; + + std::list::reverse_iterator rIter = actorLists.rbegin(); + for (; rIter != actorLists.rend(); rIter++) + { + Actor* actorImpl(*rIter); + // Only emit the signal if the actor's hover signal has connections (or derived actor implementation requires hover). + if(ShouldEmitHoverEvent(*actorImpl, hoverEvent)) + { + DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_HOVER_EVENT_SIGNAL"); + PointState::Type currentState = actorImpl->GetHoverState(); + actorImpl->SetHoverState(hoverEvent.GetState(0)); + // If hover event is newly entering the actor, update it to the started state. + if(hoverEvent.GetState(0) == PointState::MOTION && + (currentState == PointState::FINISHED || currentState == PointState::INTERRUPTED || currentState == PointState::LEAVE)) + { + HoverEventPtr newHoverEvent = HoverEvent::Clone(GetImplementation(hoverEvent)); + newHoverEvent->GetPoint(0).SetState(PointState::STARTED); + actorImpl->SetHoverState(PointState::STARTED); //update state + if(actorImpl->EmitHoverEventSignal(Dali::HoverEvent(newHoverEvent.Get()))) + { + // One of this actor's listeners has consumed the event so set this actor as the consumed actor. + consumedActor = Dali::Actor(actorImpl); + break; + } + } + else if(actorImpl->EmitHoverEventSignal(hoverEvent)) + { + // One of this actor's listeners has consumed the event so set this actor as the consumed actor. + consumedActor = Dali::Actor(actorImpl); + break; + } + } + } + return consumedActor; +} + + Dali::Actor AllocAndEmitHoverSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point) { HoverEventPtr hoverEvent(new HoverEvent(time)); @@ -119,14 +162,26 @@ Dali::Actor AllocAndEmitHoverSignals(unsigned long time, Dali::Actor actor, cons return EmitHoverSignals(actor, hoverEventHandle); } +Dali::Actor GeoAllocAndEmitHoverSignals(std::list& actorLists, unsigned long time, const Integration::Point& point) +{ + HoverEventPtr hoverEvent(new HoverEvent(time)); + Dali::HoverEvent hoverEventHandle(hoverEvent.Get()); + + hoverEvent->AddPoint(point); + + return EmitGeoHoverSignals(actorLists, hoverEventHandle); +} + + /** * Changes the state of the primary point to leave and emits the hover signals */ -Dali::Actor EmitHoverSignals(Actor* actor, RenderTask& renderTask, const HoverEventPtr& originalEvent, PointState::Type state) +Dali::Actor EmitHoverSignals(Actor* actor, RenderTask& renderTask, const HoverEventPtr& originalEvent, PointState::Type state, bool isGeometry) { HoverEventPtr hoverEvent = HoverEvent::Clone(*originalEvent.Get()); DALI_ASSERT_DEBUG(NULL != actor && "NULL actor pointer"); + Dali::Actor consumingActor; if(actor) { Integration::Point& primaryPoint = hoverEvent->GetPoint(0); @@ -140,7 +195,17 @@ Dali::Actor EmitHoverSignals(Actor* actor, RenderTask& renderTask, const HoverEv primaryPoint.SetState(state); } - return EmitHoverSignals(Dali::Actor(actor), Dali::HoverEvent(hoverEvent.Get())); + if(isGeometry) + { + std::list actorLists; + actorLists.push_back(actor); + consumingActor = EmitGeoHoverSignals(actorLists, Dali::HoverEvent(hoverEvent.Get())); + } + else + { + consumingActor = EmitHoverSignals(Dali::Actor(actor), Dali::HoverEvent(hoverEvent.Get())); + } + return consumingActor; } /** @@ -165,7 +230,7 @@ struct ActorHoverableCheck : public HitTestAlgorithm::HitTestInterface return layer->IsHoverConsumed(); } - bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override + bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, bool isGeometry) override { // Hover event is always hit. return true; @@ -204,7 +269,16 @@ void HoverEventProcessor::SendInterruptedHoverEvent(Dali::Internal::Actor* actor Integration::Point point; point.SetState(PointState::INTERRUPTED); point.SetHitActor(Dali::Actor(actor)); - AllocAndEmitHoverSignals(GetMilliSeconds(), point.GetHitActor(), point); + if(mScene.IsGeometryHittestEnabled()) + { + std::list actorLists; + actorLists.push_back(actor); + GeoAllocAndEmitHoverSignals(actorLists, 0, point); + } + else + { + AllocAndEmitHoverSignals(GetMilliSeconds(), point.GetHitActor(), point); + } Clear(); } } @@ -220,6 +294,8 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_HOVER_EVENT"); + bool isGeometry = mScene.IsGeometryHittestEnabled(); + // Copy so we can add the results of a hit-test. HoverEventPtr hoverEvent(new HoverEvent(event.time)); @@ -236,30 +312,55 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event { Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor); currentPoint.SetHitActor(lastPrimaryHitActorHandle); - consumingActor = AllocAndEmitHoverSignals(event.time, lastPrimaryHitActorHandle, currentPoint); + if(isGeometry) + { + consumingActor = GeoAllocAndEmitHoverSignals(mCandidateActorLists, event.time, currentPoint); + } + else + { + consumingActor = AllocAndEmitHoverSignals(event.time, lastPrimaryHitActorHandle, currentPoint); + } } // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed). Actor* lastConsumedActor(mLastConsumedActor.GetActor()); if(lastConsumedActor && - lastConsumedActor != lastPrimaryHitActor && - lastConsumedActor != consumingActor) + lastConsumedActor != lastPrimaryHitActor && + lastConsumedActor != consumingActor) { Dali::Actor lastConsumedActorHandle(lastConsumedActor); currentPoint.SetHitActor(lastConsumedActorHandle); - AllocAndEmitHoverSignals(event.time, lastConsumedActorHandle, currentPoint); + if(isGeometry) + { + std::list actorLists; + actorLists.push_back(lastConsumedActor); + GeoAllocAndEmitHoverSignals(actorLists, event.time, currentPoint); + } + else + { + AllocAndEmitHoverSignals(event.time, lastConsumedActorHandle, currentPoint); + } } // Tell the hover-start consuming actor as well, if required Actor* hoverStartConsumedActor(mHoverStartConsumedActor.GetActor()); if(hoverStartConsumedActor && - hoverStartConsumedActor != lastPrimaryHitActor && - hoverStartConsumedActor != lastConsumedActor && - hoverStartConsumedActor != consumingActor) + hoverStartConsumedActor != lastPrimaryHitActor && + hoverStartConsumedActor != lastConsumedActor && + hoverStartConsumedActor != consumingActor) { Dali::Actor hoverStartConsumedActorHandle(hoverStartConsumedActor); currentPoint.SetHitActor(hoverStartConsumedActorHandle); - AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, currentPoint); + if(isGeometry) + { + std::list actorLists; + actorLists.push_back(hoverStartConsumedActor); + GeoAllocAndEmitHoverSignals(actorLists, event.time, currentPoint); + } + else + { + AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, currentPoint); + } } Clear(); @@ -281,7 +382,7 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event { HitTestAlgorithm::Results hitTestResults; ActorHoverableCheck actorHoverableCheck; - HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, actorHoverableCheck); + HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, actorHoverableCheck, isGeometry); Integration::Point newPoint(currentPoint); newPoint.SetHitActor(hitTestResults.actor); @@ -296,6 +397,7 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event { firstPointParsed = true; currentRenderTask = hitTestResults.renderTask; + mCandidateActorLists = hitTestResults.actorLists; } } @@ -310,18 +412,26 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event if(currentRenderTask) { Dali::Actor hitActor = hoverEvent->GetHitActor(0); - // If the actor is hit first, the hover is started. - if(hitActor && - mLastPrimaryHitActor.GetActor() != hitActor && - state == PointState::MOTION) + + if(isGeometry) + { + consumedActor = EmitGeoHoverSignals(mCandidateActorLists, hoverEventHandle); + } + else { - Actor* hitActorImpl = &GetImplementation(hitActor); - if(hitActorImpl->GetLeaveRequired()) + // If the actor is hit first, the hover is started. + if(hitActor && + mLastPrimaryHitActor.GetActor() != hitActor && + state == PointState::MOTION) { - hoverEvent->GetPoint(0).SetState(PointState::STARTED); + Actor* hitActorImpl = &GetImplementation(hitActor); + if(hitActorImpl->GetLeaveRequired()) + { + hoverEvent->GetPoint(0).SetState(PointState::STARTED); + } } + consumedActor = EmitHoverSignals(hitActor, hoverEventHandle); } - consumedActor = EmitHoverSignals(hitActor, hoverEventHandle); if(hoverEvent->GetPoint(0).GetState() != PointState::MOTION) { @@ -350,15 +460,46 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get(); if(lastPrimaryHitActor && - lastPrimaryHitActor != primaryHitActor && - lastPrimaryHitActor != consumedActor) + lastPrimaryHitActor != primaryHitActor && + lastPrimaryHitActor != consumedActor) { if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor)) { - if(lastPrimaryHitActor->GetLeaveRequired()) + if(isGeometry) + { + // This is a situation where actors who received a hover event must leave. + // Compare the lastActorList that received the hover event and the CandidateActorList that can receive the new hover event + // If the hover event can no longer be received, Leave is sent. + std::list::reverse_iterator rLastIter = mLastActorLists.rbegin(); + for(; rLastIter != mLastActorLists.rend(); rLastIter++) + { + bool find = false; + std::list::reverse_iterator rCandidateIter = mCandidateActorLists.rbegin(); + for(; rCandidateIter != mCandidateActorLists.rend(); rCandidateIter++) + { + if(*rCandidateIter == *rLastIter) + { + find = true; + break; + } + } + if(!find) + { + DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast(*rLastIter), (*rLastIter)->GetId(), (*rLastIter)->GetName().data()); + leaveEventConsumer = EmitHoverSignals(*rLastIter, lastRenderTaskImpl, hoverEvent, PointState::LEAVE, isGeometry); + } + // If the actor has been consumed, you do not need to proceed. + if(*rLastIter == lastConsumedActor) + { + break; + } + } + } + else if(lastPrimaryHitActor->GetLeaveRequired()) { + // In the case of isGeometry, it is not propagated but only sent to actors who are not hittable. DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data()); - leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::LEAVE); + leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::LEAVE, isGeometry); } } else if(primaryPointState != PointState::STARTED) @@ -366,7 +507,7 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one. // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls) DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data()); - leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED); + leaveEventConsumer = EmitHoverSignals(mLastPrimaryHitActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED, isGeometry); } } @@ -374,17 +515,17 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event // consumed actor's listeners may need to be informed (through a leave event). // Further checks here to ensure we do not signal the same actor twice for the same event. if(lastConsumedActor && - lastConsumedActor != consumedActor && - lastConsumedActor != lastPrimaryHitActor && - lastConsumedActor != primaryHitActor && - lastConsumedActor != leaveEventConsumer) + lastConsumedActor != consumedActor && + lastConsumedActor != lastPrimaryHitActor && + lastConsumedActor != primaryHitActor && + lastConsumedActor != leaveEventConsumer) { if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor)) { - if(lastConsumedActor->GetLeaveRequired()) + if(lastConsumedActor->GetLeaveRequired() && !isGeometry) // For geometry, we have already sent leave. There is no need to send leave repeatedly. { DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data()); - EmitHoverSignals(lastConsumedActor, lastRenderTaskImpl, hoverEvent, PointState::LEAVE); + EmitHoverSignals(lastConsumedActor, lastRenderTaskImpl, hoverEvent, PointState::LEAVE, isGeometry); } } else if(primaryPointState != PointState::STARTED) @@ -392,7 +533,7 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one. // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls) DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data()); - EmitHoverSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED); + EmitHoverSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, hoverEvent, PointState::INTERRUPTED, isGeometry); } } } @@ -422,6 +563,7 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event } mLastRenderTask = currentRenderTask; + mLastActorLists = mCandidateActorLists; } else { @@ -447,7 +589,16 @@ void HoverEventProcessor::ProcessHoverEvent(const Integration::HoverEvent& event Integration::Point primaryPoint = hoverEvent->GetPoint(0); primaryPoint.SetHitActor(hoverStartConsumedActorHandle); primaryPoint.SetState(PointState::INTERRUPTED); - AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, primaryPoint); + if(isGeometry) + { + std::list actorLists; + actorLists.push_back(hoverStartConsumedActor); + GeoAllocAndEmitHoverSignals(actorLists, event.time, primaryPoint); + } + else + { + AllocAndEmitHoverSignals(event.time, hoverStartConsumedActorHandle, primaryPoint); + } // Restore hover-event to original state primaryPoint.SetHitActor(primaryHitActor); @@ -476,6 +627,7 @@ void HoverEventProcessor::Clear() mLastPrimaryHitActor.SetActor(nullptr); mLastConsumedActor.SetActor(nullptr); mLastRenderTask.Reset(); + mLastActorLists.clear(); } void HoverEventProcessor::OnObservedActorDisconnected(Dali::Internal::Actor* actor) diff --git a/dali/internal/event/events/hover-event-processor.h b/dali/internal/event/events/hover-event-processor.h index 94c75ad..5ec5002 100644 --- a/dali/internal/event/events/hover-event-processor.h +++ b/dali/internal/event/events/hover-event-processor.h @@ -90,11 +90,13 @@ private: */ void OnObservedActorDisconnected(Dali::Internal::Actor* actor); - Scene& mScene; ///< Reference to the scene - ActorObserver mLastPrimaryHitActor; ///< Stores the last primary point hit actor - ActorObserver mLastConsumedActor; ///< Stores the last consumed actor - ActorObserver mHoverStartConsumedActor; ///< Stores the hover-start consumed actor - RenderTaskPtr mLastRenderTask; ///< The RenderTask used for the last hit actor + Scene& mScene; ///< Reference to the scene + ActorObserver mLastPrimaryHitActor; ///< Stores the last primary point hit actor + ActorObserver mLastConsumedActor; ///< Stores the last consumed actor + ActorObserver mHoverStartConsumedActor; ///< Stores the hover-start consumed actor + RenderTaskPtr mLastRenderTask; ///< The RenderTask used for the last hit actor + std::list mCandidateActorLists; ///< Stores a list of actors that can be touched, from leaf actor to root. + std::list mLastActorLists; ///< Stores a list of actors that can be touched, from leaf actor to root. }; } // namespace Internal diff --git a/dali/internal/event/events/touch-event-processor.cpp b/dali/internal/event/events/touch-event-processor.cpp index 1e86b4c..9a139f5 100644 --- a/dali/internal/event/events/touch-event-processor.cpp +++ b/dali/internal/event/events/touch-event-processor.cpp @@ -70,6 +70,7 @@ bool ShouldEmitTouchEvent(const Actor& actorImpl, const Dali::TouchEvent& event) return actorImpl.GetTouchRequired() && (state != PointState::MOTION || actorImpl.IsDispatchTouchMotion()); } +// child -> parent Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEvent) { Dali::Actor interceptedActor; @@ -98,7 +99,28 @@ Dali::Actor EmitInterceptTouchSignals(Dali::Actor actor, const Dali::TouchEvent& } } } + return interceptedActor; +} +// geometry +// child -> below +Dali::Actor EmitGeoInterceptTouchSignals(std::list& actorLists, std::list& interceptActorList, const Dali::TouchEvent& touchEvent) +{ + interceptActorList.clear(); + Dali::Actor interceptedActor; + for(auto&& actor : actorLists) + { + interceptActorList.push_back(actor); + if(ShouldEmitInterceptTouchEvent(*actor, touchEvent)) + { + DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_INTERCEPT_TOUCH_EVENT_SIGNAL"); + if(actor->EmitInterceptTouchEventSignal(touchEvent)) + { + interceptedActor = Dali::Actor(actor); + break; + } + } + } return interceptedActor; } @@ -146,6 +168,32 @@ Dali::Actor EmitTouchSignals(Dali::Actor actor, const Dali::TouchEvent& touchEve return consumedActor; } +/** + * Recursively deliver events to the actor and its below actor, until the event is consumed or the stage is reached. + */ +Dali::Actor EmitGeoTouchSignals(std::list& actorLists, const Dali::TouchEvent& touchEvent) +{ + Dali::Actor consumedActor; + + std::list::reverse_iterator rIter = actorLists.rbegin(); + for (; rIter != actorLists.rend(); rIter++) + { + Actor* actorImpl(*rIter); + // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch). + if(ShouldEmitTouchEvent(*actorImpl, touchEvent)) + { + DALI_TRACE_SCOPE(gTraceFilter, "DALI_EMIT_TOUCH_EVENT_SIGNAL"); + if(actorImpl->EmitTouchEventSignal(touchEvent)) + { + // One of this actor's listeners has consumed the event so set this actor as the consumed actor. + consumedActor = Dali::Actor(actorImpl); + break; + } + } + } + return consumedActor; +} + Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, const Integration::Point& point, RenderTaskPtr renderTask) { TouchEventPtr touchEvent(new TouchEvent(time)); @@ -157,10 +205,21 @@ Dali::Actor AllocAndEmitTouchSignals(unsigned long time, Dali::Actor actor, cons return EmitTouchSignals(actor, touchEventHandle); } +Dali::Actor GeoAllocAndEmitTouchSignals(std::list& actorLists, unsigned long time, const Integration::Point& point, RenderTaskPtr renderTask) +{ + TouchEventPtr touchEvent(new TouchEvent(time)); + Dali::TouchEvent touchEventHandle(touchEvent.Get()); + + touchEvent->AddPoint(point); + touchEvent->SetRenderTask(Dali::RenderTask(renderTask.Get())); + + return EmitGeoTouchSignals(actorLists, touchEventHandle); +} + /** * Changes the state of the primary point to leave and emits the touch signals */ -Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state) +Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEventPtr& originalTouchEvent, PointState::Type state, bool isGeometry) { Dali::Actor consumingActor; @@ -179,7 +238,16 @@ Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEv primaryPoint.SetHitActor(Dali::Actor(actor)); primaryPoint.SetState(state); - consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get())); + if(isGeometry) + { + std::list actorLists; + actorLists.push_back(actor); + consumingActor = EmitGeoTouchSignals(actorLists, Dali::TouchEvent(touchEventImpl.Get())); + } + else + { + consumingActor = EmitTouchSignals(Dali::Actor(actor), Dali::TouchEvent(touchEventImpl.Get())); + } } return consumingActor; @@ -193,6 +261,7 @@ Dali::Actor EmitTouchSignals(Actor* actor, RenderTask& renderTask, const TouchEv * @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 + * @param[in] actorLists The list of actors that can be touched, from leaf actor to root. */ void ParsePrimaryTouchPoint( HitTestAlgorithm::Results& hitTestResults, @@ -200,7 +269,8 @@ void ParsePrimaryTouchPoint( ActorObserver& ownTouchActorObserver, const RenderTaskPtr& lastRenderTask, const Integration::Point& currentPoint, - const Internal::Scene& scene) + const Internal::Scene& scene, + std::list& actorLists) { Actor* capturingTouchActor = capturingTouchActorObserver.GetActor(); @@ -215,20 +285,25 @@ void ParsePrimaryTouchPoint( else { Actor* ownTouchActor = ownTouchActorObserver.GetActor(); - HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor); + HitTestAlgorithm::HitTest(scene.GetSize(), scene.GetRenderTaskList(), scene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, ownTouchActor, scene.IsGeometryHittestEnabled()); if(currentPoint.GetState() == PointState::STARTED && hitTestResults.actor) { + bool isGeometry = scene.IsGeometryHittestEnabled(); // 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()) + if(hitActor->CapturesAllTouchAfterStart() || isGeometry) { capturingTouchActorObserver.SetActor(hitActor); } - if(hitActor->IsAllowedOnlyOwnTouch()) + if(hitActor->IsAllowedOnlyOwnTouch() || isGeometry) { ownTouchActorObserver.SetActor(hitActor); } + if(isGeometry) + { + actorLists = hitTestResults.actorLists; + } } } } @@ -274,6 +349,8 @@ bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event DALI_TRACE_SCOPE(gTraceFilter, "DALI_PROCESS_TOUCH_EVENT"); + bool isGeometry = mScene.IsGeometryHittestEnabled(); + // 1) Check if it is an interrupted event - we should inform our last primary hit actor about this // and emit the stage signal as well. @@ -282,37 +359,71 @@ bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event Dali::Actor consumingActor; Integration::Point currentPoint(event.points[0]); - Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor()); - if(lastPrimaryHitActor) + if(isGeometry) { - Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor); - currentPoint.SetHitActor(lastPrimaryHitActorHandle); + // Since the geometry way only receives touch events from the consumed actor, + // it first searches for whether there is a consumed actor and then sends the event + Actor* touchConsumedActor(mLastConsumedActor.GetActor()); + Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor()); + Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor()); + if(touchConsumedActor) + { + Dali::Actor touchConsumedActorHandle(touchConsumedActor); + currentPoint.SetHitActor(touchConsumedActorHandle); + std::list actorLists; + actorLists.push_back(touchConsumedActor); + GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask); + } + else if(touchDownConsumedActor) + { + Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor); + currentPoint.SetHitActor(touchDownConsumedActorHandle); + std::list actorLists; + actorLists.push_back(touchDownConsumedActor); + GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, mLastRenderTask); + } + else if(lastPrimaryHitActor) + { + Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor); + currentPoint.SetHitActor(lastPrimaryHitActorHandle); - consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, mLastRenderTask); + GeoAllocAndEmitTouchSignals(mCandidateActorLists, event.time, currentPoint, mLastRenderTask); + } } - - // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed). - Actor* lastConsumedActor(mLastConsumedActor.GetActor()); - if(lastConsumedActor && - lastConsumedActor != lastPrimaryHitActor && - lastConsumedActor != consumingActor) + else { - Dali::Actor lastConsumedActorHandle(lastConsumedActor); - currentPoint.SetHitActor(lastConsumedActorHandle); - AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, mLastRenderTask); - } + Actor* lastPrimaryHitActor(mLastPrimaryHitActor.GetActor()); + if(lastPrimaryHitActor) + { + Dali::Actor lastPrimaryHitActorHandle(lastPrimaryHitActor); + currentPoint.SetHitActor(lastPrimaryHitActorHandle); - // Tell the touch-down consuming actor as well, if required - Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor()); - if(touchDownConsumedActor && - touchDownConsumedActor != lastPrimaryHitActor && - touchDownConsumedActor != lastConsumedActor && - touchDownConsumedActor != consumingActor) - { - Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor); + consumingActor = AllocAndEmitTouchSignals(event.time, lastPrimaryHitActorHandle, currentPoint, mLastRenderTask); + } + + // If the last consumed actor was different to the primary hit actor then inform it as well (if it has not already been informed). + Actor* lastConsumedActor(mLastConsumedActor.GetActor()); + if(lastConsumedActor && + lastConsumedActor != lastPrimaryHitActor && + lastConsumedActor != consumingActor) + { + Dali::Actor lastConsumedActorHandle(lastConsumedActor); + currentPoint.SetHitActor(lastConsumedActorHandle); + AllocAndEmitTouchSignals(event.time, lastConsumedActorHandle, currentPoint, mLastRenderTask); + } - currentPoint.SetHitActor(touchDownConsumedActorHandle); - AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, mLastRenderTask); + // Tell the touch-down consuming actor as well, if required + Actor* touchDownConsumedActor(mTouchDownConsumedActor.GetActor()); + if(touchDownConsumedActor && + touchDownConsumedActor != lastPrimaryHitActor && + touchDownConsumedActor != lastConsumedActor && + touchDownConsumedActor != consumingActor) + { + Dali::Actor touchDownConsumedActorHandle(touchDownConsumedActor); + + currentPoint.SetHitActor(touchDownConsumedActorHandle); + AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, mLastRenderTask); + } } Clear(); @@ -347,7 +458,7 @@ bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event if(!firstPointParsed) { firstPointParsed = true; - ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene); + ParsePrimaryTouchPoint(hitTestResults, mCapturingTouchActor, mOwnTouchActor, mLastRenderTask, currentPoint, mScene, mCandidateActorLists); // Only set the currentRenderTask for the primary hit actor. currentRenderTask = hitTestResults.renderTask; @@ -355,7 +466,7 @@ bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event } else { - HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults); + HitTestAlgorithm::HitTest(mScene.GetSize(), mScene.GetRenderTaskList(), mScene.GetLayerList(), currentPoint.GetScreenPosition(), hitTestResults, nullptr, isGeometry); } Integration::Point newPoint(currentPoint); @@ -380,46 +491,107 @@ bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event if(currentRenderTask) { - Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor()); - if(interceptedTouchActor) + if(isGeometry) { - Dali::Actor interceptedTouchActorHandle(interceptedTouchActor); - consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle); + Actor* touchConsumedActor(mLastConsumedActor.GetActor()); + Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor()); + if(touchConsumedActor) // If there is a consultative actor, send events only to the consultative actor. + { + RenderTask& currentRenderTaskImpl = *currentRenderTask.Get(); + consumedActor = EmitTouchSignals(touchConsumedActor, currentRenderTaskImpl, touchEventImpl, primaryPointState, isGeometry); + } + else if(interceptedTouchActor) // If there is an intercepted actor, send a touch event starting from the intercepted actor. + { + Dali::Actor interceptedTouchActorHandle(interceptedTouchActor); + std::list interceptActorLists = mInterceptedActorLists; + consumedActor = EmitGeoTouchSignals(interceptActorLists, touchEventHandle); + } + else + { + Dali::Actor interceptedActor; + // Let's find out if there is an intercept actor. + interceptedActor = EmitGeoInterceptTouchSignals(mCandidateActorLists, mInterceptedActorLists, touchEventHandle); + if(interceptedActor) + { + mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor)); + + // If there is an interception, send an interrupted event to the last consumed actor or to the actor that hit previously. + if(mLastConsumedActor.GetActor() && + mLastConsumedActor.GetActor() != interceptedActor && + mLastRenderTask && + mLastPrimaryPointState != PointState::FINISHED) + { + EmitTouchSignals(mLastConsumedActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry); + mTouchDownConsumedActor.SetActor(nullptr); + mLastConsumedActor.SetActor(nullptr); + } + else if(mLastPrimaryHitActor.GetActor() && + mLastPrimaryHitActor.GetActor() != interceptedActor && + mLastRenderTask && + mLastPrimaryPointState != PointState::FINISHED) + { + std::list internalActorLists = mCandidateActorLists; + while(!internalActorLists.empty()) + { + Actor* actorImpl = internalActorLists.back(); + // Only emit the signal if the actor's touch signal has connections (or derived actor implementation requires touch). + if(actorImpl->GetTouchRequired()) + { + EmitTouchSignals(actorImpl, *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry); + } + internalActorLists.pop_back(); + } + } + } + + // Let's propagate touch events from the intercepted actor or start propagating touch events from the leaf actor. + consumedActor = EmitGeoTouchSignals(interceptedActor ? mInterceptedActorLists : mCandidateActorLists, touchEventHandle); + } } else { - // Emit the intercept touch signal - Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle); - if(interceptedActor) + Actor* interceptedTouchActor(mInterceptedTouchActor.GetActor()); + if(interceptedTouchActor) { - mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor)); - // If the child is being touched, INTERRUPTED is sent. - if(mLastPrimaryHitActor.GetActor() && - mLastPrimaryHitActor.GetActor() != interceptedActor && - mLastRenderTask && - mLastPrimaryPointState != PointState::FINISHED) - { - EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED); - mTouchDownConsumedActor.SetActor(nullptr); - } - consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle); + Dali::Actor interceptedTouchActorHandle(interceptedTouchActor); + consumedActor = EmitTouchSignals(interceptedTouchActorHandle, touchEventHandle); } else { - consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle); + // Emit the intercept touch signal + Dali::Actor interceptedActor = EmitInterceptTouchSignals(primaryHitActor, touchEventHandle); + if(interceptedActor) + { + mInterceptedTouchActor.SetActor(&GetImplementation(interceptedActor)); + // If the child is being touched, INTERRUPTED is sent. + if(mLastPrimaryHitActor.GetActor() && + mLastPrimaryHitActor.GetActor() != interceptedActor && + mLastRenderTask && + mLastPrimaryPointState != PointState::FINISHED) + { + EmitTouchSignals(mLastPrimaryHitActor.GetActor(), *mLastRenderTask.Get(), touchEventImpl, PointState::INTERRUPTED, isGeometry); + mTouchDownConsumedActor.SetActor(nullptr); + } + consumedActor = EmitTouchSignals(interceptedActor, touchEventHandle); + } + else + { + consumedActor = EmitTouchSignals(primaryHitActor, touchEventHandle); + } } } + consumed = consumedActor ? true : false; if(primaryPointState == PointState::MOTION) { - DALI_LOG_INFO(gLogFilter, Debug::Concise, "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f)\n", primaryHitActor ? reinterpret_cast(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty(Dali::Actor::Property::ID) : -1, primaryHitActor ? primaryHitActor.GetProperty(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], primaryPoint.GetScreenPosition().x, primaryPoint.GetScreenPosition().y); - DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor: (%p) id(%d), name(%s), state(%s)\n", consumedActor ? reinterpret_cast(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty(Dali::Actor::Property::ID) : -1, consumedActor ? consumedActor.GetProperty(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState]); + DALI_LOG_INFO(gLogFilter, Debug::Concise, "PrimaryHitActor: (%p) id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n", primaryHitActor ? reinterpret_cast(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty(Dali::Actor::Property::ID) : -1, primaryHitActor ? primaryHitActor.GetProperty(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], primaryPoint.GetScreenPosition().x, primaryPoint.GetScreenPosition().y, isGeometry); + DALI_LOG_INFO(gLogFilter, Debug::Concise, "ConsumedActor: (%p) id(%d), name(%s), state(%s), isGeo : %d \n", consumedActor ? reinterpret_cast(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty(Dali::Actor::Property::ID) : -1, consumedActor ? consumedActor.GetProperty(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], isGeometry); } else { - DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s), screenPosition(%f, %f)\n", primaryHitActor ? reinterpret_cast(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty(Dali::Actor::Property::ID) : -1, primaryHitActor ? primaryHitActor.GetProperty(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], primaryPoint.GetScreenPosition().x, primaryPoint.GetScreenPosition().y); - DALI_LOG_RELEASE_INFO("ConsumedActor: (%p), id(%d), name(%s), state(%s)\n", consumedActor ? reinterpret_cast(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty(Dali::Actor::Property::ID) : -1, consumedActor ? consumedActor.GetProperty(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState]); + DALI_LOG_RELEASE_INFO("PrimaryHitActor:(%p), id(%d), name(%s), state(%s), screenPosition(%f, %f), isGeo : %d \n", primaryHitActor ? reinterpret_cast(&primaryHitActor.GetBaseObject()) : NULL, primaryHitActor ? primaryHitActor.GetProperty(Dali::Actor::Property::ID) : -1, primaryHitActor ? primaryHitActor.GetProperty(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], primaryPoint.GetScreenPosition().x, primaryPoint.GetScreenPosition().y, isGeometry); + DALI_LOG_RELEASE_INFO("ConsumedActor: (%p), id(%d), name(%s), state(%s), isGeo : %d \n", consumedActor ? reinterpret_cast(&consumedActor.GetBaseObject()) : NULL, consumedActor ? consumedActor.GetProperty(Dali::Actor::Property::ID) : -1, consumedActor ? consumedActor.GetProperty(Dali::Actor::Property::NAME).c_str() : "", TOUCH_POINT_STATE[primaryPointState], isGeometry); } } @@ -449,52 +621,86 @@ bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event Dali::Actor leaveEventConsumer; RenderTask& lastRenderTaskImpl = *mLastRenderTask.Get(); - if(lastPrimaryHitActor && - lastPrimaryHitActor != primaryHitActor && - lastPrimaryHitActor != consumedActor) + if(isGeometry) { - if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor)) + if(lastPrimaryHitActor) { - if(lastPrimaryHitActor->GetLeaveRequired()) + if(!lastPrimaryHitActor->IsHittable() || !IsActuallySensitive(lastPrimaryHitActor)) { - DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data()); - leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE); + // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one. + // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls) + DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %s\n", reinterpret_cast(lastPrimaryHitActor), lastPrimaryHitActor->GetName().data()); + leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry); } } - else + + consumed |= leaveEventConsumer ? true : false; + + // Check if the motion event has been consumed by another actor's listener. In this case, the previously + // consumed actor's listeners may need to be informed (through a leave event). + // Further checks here to ensure we do not signal the same actor twice for the same event. + if(lastConsumedActor && + lastConsumedActor != lastPrimaryHitActor && + lastConsumedActor != leaveEventConsumer) { - // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one. - // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls) - DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data()); - leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED); + if(!lastConsumedActor->IsHittable() || !IsActuallySensitive(lastConsumedActor)) + { + // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one. + // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls) + DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %s\n", reinterpret_cast(lastConsumedActor), lastConsumedActor->GetName().data()); + EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry); + } } } - - consumed |= leaveEventConsumer ? true : false; - - // Check if the motion event has been consumed by another actor's listener. In this case, the previously - // consumed actor's listeners may need to be informed (through a leave event). - // Further checks here to ensure we do not signal the same actor twice for the same event. - if(lastConsumedActor && - lastConsumedActor != consumedActor && - lastConsumedActor != lastPrimaryHitActor && - lastConsumedActor != primaryHitActor && - lastConsumedActor != leaveEventConsumer) + else { - if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor)) + if(lastPrimaryHitActor && + lastPrimaryHitActor != primaryHitActor && + lastPrimaryHitActor != consumedActor) { - if(lastConsumedActor->GetLeaveRequired()) + if(lastPrimaryHitActor->IsHittable() && IsActuallySensitive(lastPrimaryHitActor)) + { + if(lastPrimaryHitActor->GetLeaveRequired()) + { + DALI_LOG_RELEASE_INFO("LeaveActor(Hit): (%p) %d %s\n", reinterpret_cast(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data()); + leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry); + } + } + else { - DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data()); - EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE); + // At this point mLastPrimaryHitActor was touchable and sensitive in the previous touch event process but is not in the current one. + // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls) + DALI_LOG_RELEASE_INFO("InterruptedActor(Hit): (%p) %d %s\n", reinterpret_cast(lastPrimaryHitActor), lastPrimaryHitActor->GetId(), lastPrimaryHitActor->GetName().data()); + leaveEventConsumer = EmitTouchSignals(lastPrimaryHitActor, lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry); } } - else + + consumed |= leaveEventConsumer ? true : false; + + // Check if the motion event has been consumed by another actor's listener. In this case, the previously + // consumed actor's listeners may need to be informed (through a leave event). + // Further checks here to ensure we do not signal the same actor twice for the same event. + if(lastConsumedActor && + lastConsumedActor != consumedActor && + lastConsumedActor != lastPrimaryHitActor && + lastConsumedActor != primaryHitActor && + lastConsumedActor != leaveEventConsumer) { - // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one. - // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls) - DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data()); - EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED); + if(lastConsumedActor->IsHittable() && IsActuallySensitive(lastConsumedActor)) + { + if(lastConsumedActor->GetLeaveRequired()) + { + DALI_LOG_RELEASE_INFO("LeaveActor(Consume): (%p) %d %s\n", reinterpret_cast(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data()); + EmitTouchSignals(lastConsumedActor, lastRenderTaskImpl, touchEventImpl, PointState::LEAVE, isGeometry); + } + } + else + { + // At this point mLastConsumedActor was touchable and sensitive in the previous touch event process but is not in the current one. + // An interrupted event is send to allow some actors to go back to their original state (i.e. Button controls) + DALI_LOG_RELEASE_INFO("InterruptedActor(Consume): (%p) %d %s\n", reinterpret_cast(lastConsumedActor), lastConsumedActor->GetId(), lastConsumedActor->GetName().data()); + EmitTouchSignals(mLastConsumedActor.GetActor(), lastRenderTaskImpl, touchEventImpl, PointState::INTERRUPTED, isGeometry); + } } } } @@ -520,7 +726,17 @@ bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event } else { - mLastConsumedActor.SetActor(nullptr); + if(isGeometry) + { + if(lastConsumedActor && !lastConsumedActor->OnScene()) + { + mLastConsumedActor.SetActor(nullptr); + } + } + else + { + mLastConsumedActor.SetActor(nullptr); + } } mLastRenderTask = currentRenderTask; @@ -553,7 +769,16 @@ bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event currentPoint.SetHitActor(touchDownConsumedActorHandle); currentPoint.SetState(PointState::INTERRUPTED); - AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */); + if(isGeometry) + { + std::list actorLists; + actorLists.push_back(touchDownConsumedActor); + GeoAllocAndEmitTouchSignals(actorLists, event.time, currentPoint, nullptr /* Not Required for this use case */); + } + else + { + AllocAndEmitTouchSignals(event.time, touchDownConsumedActorHandle, currentPoint, nullptr /* Not Required for this use case */); + } } mTouchDownConsumedActor.SetActor(nullptr); @@ -584,31 +809,53 @@ bool TouchEventProcessor::ProcessTouchEvent(const Integration::TouchEvent& event void TouchEventProcessor::OnObservedActorDisconnected(Actor* actor) { - if(actor == mLastPrimaryHitActor.GetActor()) + if(mScene.IsGeometryHittestEnabled() && (actor == mLastConsumedActor.GetActor() || actor == mLastPrimaryHitActor.GetActor())) { Dali::Actor actorHandle(actor); - Integration::Point point; point.SetState(PointState::INTERRUPTED); point.SetHitActor(actorHandle); - - TouchEventPtr touchEventImpl(new TouchEvent); - touchEventImpl->AddPoint(point); - Dali::TouchEvent touchEventHandle(touchEventImpl.Get()); - - Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle); - - if(mLastConsumedActor.GetActor() != eventConsumer) + if(actor == mLastConsumedActor.GetActor()) { - EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle); + std::list actorLists; + actorLists.push_back(mLastConsumedActor.GetActor()); + GeoAllocAndEmitTouchSignals(actorLists, 0, point, mLastRenderTask); + } + else if(!mLastConsumedActor.GetActor()) + { + GeoAllocAndEmitTouchSignals(mCandidateActorLists, 0, point, mLastRenderTask); } - // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers - mLastConsumedActor.SetActor(nullptr); mLastRenderTask.Reset(); mLastPrimaryPointState = PointState::FINISHED; } + else + { + if(actor == mLastPrimaryHitActor.GetActor()) + { + Dali::Actor actorHandle(actor); + Integration::Point point; + point.SetState(PointState::INTERRUPTED); + point.SetHitActor(actorHandle); + + TouchEventPtr touchEventImpl(new TouchEvent); + touchEventImpl->AddPoint(point); + Dali::TouchEvent touchEventHandle(touchEventImpl.Get()); + + Dali::Actor eventConsumer = EmitTouchSignals(actorHandle, touchEventHandle); + if(mLastConsumedActor.GetActor() != eventConsumer) + { + EmitTouchSignals(Dali::Actor(mLastConsumedActor.GetActor()), touchEventHandle); + } + + // Do not set mLastPrimaryHitActor to NULL we may be iterating through its observers + mLastConsumedActor.SetActor(nullptr); + mLastRenderTask.Reset(); + mLastPrimaryPointState = PointState::FINISHED; + } + } + } } // namespace Internal diff --git a/dali/internal/event/events/touch-event-processor.h b/dali/internal/event/events/touch-event-processor.h index a2ed681..6ecc67b 100644 --- a/dali/internal/event/events/touch-event-processor.h +++ b/dali/internal/event/events/touch-event-processor.h @@ -99,6 +99,8 @@ private: ActorObserver mInterceptedTouchActor; ///< Stores the intercepted actor RenderTaskPtr mLastRenderTask; ///< The RenderTask used for the last hit actor PointState::Type mLastPrimaryPointState; ///< Stores the last primary point state + std::list mInterceptedActorLists; ///< Stores the list from root to intercepted actors. + std::list mCandidateActorLists; ///< Stores a list of actors that can be touched, from leaf actor to root. }; } // namespace Internal -- 2.7.4