From f5888522257fb87367edaa0d8a08fc9a5fa42cce Mon Sep 17 00:00:00 2001 From: Adeel Kazmi Date: Fri, 4 Oct 2019 19:57:44 +0100 Subject: [PATCH] Added support for Rotation Gestures Change-Id: Ib824b72119425f7329e9514295e83ade767d5117 --- automated-tests/src/dali/CMakeLists.txt | 2 + .../test-gesture-generator.cpp | 32 + .../dali-test-suite-utils/test-gesture-generator.h | 20 + .../src/dali/utc-Dali-RotationGesture.cpp | 99 ++ .../src/dali/utc-Dali-RotationGestureDetector.cpp | 1105 ++++++++++++++++++++ automated-tests/src/dali/utc-Dali-TypeRegistry.cpp | 38 + dali/devel-api/events/gesture-devel.h | 47 + .../devel-api/events/rotation-gesture-detector.cpp | 68 ++ dali/devel-api/events/rotation-gesture-detector.h | 141 +++ dali/devel-api/events/rotation-gesture.cpp | 58 + dali/devel-api/events/rotation-gesture.h | 90 ++ dali/devel-api/file.list | 4 + dali/internal/event/actors/actor-impl.cpp | 4 +- dali/internal/event/actors/actor-impl.h | 6 +- dali/internal/event/events/actor-gesture-data.cpp | 39 +- dali/internal/event/events/actor-gesture-data.h | 10 +- .../event/events/gesture-detector-impl.cpp | 7 +- dali/internal/event/events/gesture-detector-impl.h | 11 +- .../event/events/gesture-event-processor.cpp | 58 +- .../event/events/gesture-event-processor.h | 2 + dali/internal/event/events/gesture-event.cpp | 12 +- dali/internal/event/events/gesture-event.h | 10 +- dali/internal/event/events/gesture-processor.cpp | 17 +- dali/internal/event/events/gesture-processor.h | 8 +- dali/internal/event/events/gesture-recognizer.h | 42 +- dali/internal/event/events/gesture-requests.h | 39 +- .../rotation-gesture-detector-impl.cpp | 94 ++ .../rotation-gesture-detector-impl.h | 145 +++ .../rotation-gesture/rotation-gesture-event.h | 77 ++ .../rotation-gesture-processor.cpp | 257 +++++ .../rotation-gesture/rotation-gesture-processor.h | 122 +++ .../rotation-gesture-recognizer.cpp | 224 ++++ .../rotation-gesture/rotation-gesture-recognizer.h | 105 ++ dali/internal/file.list | 3 + 34 files changed, 2906 insertions(+), 90 deletions(-) create mode 100644 automated-tests/src/dali/utc-Dali-RotationGesture.cpp create mode 100644 automated-tests/src/dali/utc-Dali-RotationGestureDetector.cpp create mode 100644 dali/devel-api/events/gesture-devel.h create mode 100644 dali/devel-api/events/rotation-gesture-detector.cpp create mode 100644 dali/devel-api/events/rotation-gesture-detector.h create mode 100644 dali/devel-api/events/rotation-gesture.cpp create mode 100644 dali/devel-api/events/rotation-gesture.h create mode 100644 dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.cpp create mode 100644 dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.h create mode 100644 dali/internal/event/events/rotation-gesture/rotation-gesture-event.h create mode 100644 dali/internal/event/events/rotation-gesture/rotation-gesture-processor.cpp create mode 100644 dali/internal/event/events/rotation-gesture/rotation-gesture-processor.h create mode 100644 dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.cpp create mode 100644 dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.h diff --git a/automated-tests/src/dali/CMakeLists.txt b/automated-tests/src/dali/CMakeLists.txt index 17c4767..c501853 100644 --- a/automated-tests/src/dali/CMakeLists.txt +++ b/automated-tests/src/dali/CMakeLists.txt @@ -79,6 +79,8 @@ SET(TC_SOURCES utc-Dali-RenderTask.cpp utc-Dali-RenderTaskList.cpp utc-Dali-ResourceImage.cpp + utc-Dali-RotationGesture.cpp + utc-Dali-RotationGestureDetector.cpp utc-Dali-Sampler.cpp utc-Dali-Scene.cpp utc-Dali-Scripting.cpp diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.cpp b/automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.cpp index a0ac206..c0b55ce 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.cpp +++ b/automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.cpp @@ -161,4 +161,36 @@ void TestGenerateTwoPointTap( TestApplication& application, float x1, float y1, application.ProcessEvent( GenerateDoubleTouch( PointState::UP, Vector2( x1, y1 ), PointState::UP, Vector2( x2, y2 ), time_down + 20 ) ); } +void TestGenerateRotation( TestApplication& application ) +{ + application.ProcessEvent( GenerateDoubleTouch( PointState::DOWN, Vector2( 20.0f, 20.0f ), PointState::DOWN, Vector2( 20.0f, 90.0f ), 150 ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, Vector2( 20.0f, 20.0f ), PointState::MOTION, Vector2( 25.0f, 95.0f ), 160 ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, Vector2( 20.0f, 20.0f ), PointState::MOTION, Vector2( 30.0f, 100.0f ), 170 ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, Vector2( 20.0f, 20.0f ), PointState::MOTION, Vector2( 35.0f, 105.0f ), 180 ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, Vector2( 20.0f, 20.0f ), PointState::MOTION, Vector2( 40.0f, 110.0f ), 190 ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::UP, Vector2( 20.0f, 20.0f ), PointState::UP, Vector2( 45.0f, 115.0f ), 200 ) ); +} + +void TestStartRotation( TestApplication& application, Vector2 a1, Vector2 b1, Vector2 a2, Vector2 b2, uint32_t time ) +{ + application.ProcessEvent( GenerateDoubleTouch( PointState::DOWN, a1, PointState::DOWN, b1, time ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, a2, PointState::MOTION, b2, time + 50 ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, a2, PointState::MOTION, b2, time + 100 ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, a2, PointState::MOTION, b2, time + 150 ) ); +} + +void TestContinueRotation( TestApplication& application, Vector2 a1, Vector2 b1, Vector2 a2, Vector2 b2, uint32_t time ) +{ + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, a1, PointState::MOTION, b1, time ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, a1, PointState::MOTION, b1, time + 50 ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, a2, PointState::MOTION, b2, time + 100 ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, a2, PointState::MOTION, b2, time +150 ) ); +} + +void TestEndRotation( TestApplication& application, Vector2 a1, Vector2 b1, Vector2 a2, Vector2 b2, uint32_t time ) +{ + application.ProcessEvent( GenerateDoubleTouch( PointState::MOTION, a1, PointState::MOTION, b1, time ) ); + application.ProcessEvent( GenerateDoubleTouch( PointState::UP, a2, PointState::UP, b2, time +50 ) ); +} + } // namespace Dali diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.h b/automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.h index 80611a0..18051cf 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.h @@ -103,6 +103,26 @@ void TestGenerateTap( TestApplication& application, float x = 20.0f, float y = 2 */ void TestGenerateTwoPointTap( TestApplication& application, float x1, float y1, float x2, float y2, uint32_t time_down ); +/** + * Produces a rotation gesture. + */ +void TestGenerateRotation( TestApplication& application ); + +/** + * Produces the gesture started event of a rotation, using 4 touches, 50ms apart, starting with 1, ending at 2 + */ +void TestStartRotation( TestApplication& application, Vector2 a1, Vector2 b1, Vector2 a2, Vector2 b2, uint32_t time ); + +/** + * Produces a gesture continuing event of a rotation, using 4 touches, 50ms apart, starting with 1, ending at 2 + */ +void TestContinueRotation( TestApplication& application, Vector2 a1, Vector2 b1, Vector2 a2, Vector2 b2, uint32_t time ); + +/** + * Produces a gesture finished event of a rotation, using 2 touches, 50ms apart + */ +void TestEndRotation( TestApplication& application, Vector2 a1, Vector2 b1, Vector2 a2, Vector2 b2, uint32_t time ); + } // namespace Dali #endif // DALI_TEST_GESTURE_GENERATOR_H diff --git a/automated-tests/src/dali/utc-Dali-RotationGesture.cpp b/automated-tests/src/dali/utc-Dali-RotationGesture.cpp new file mode 100644 index 0000000..4287ebf --- /dev/null +++ b/automated-tests/src/dali/utc-Dali-RotationGesture.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019 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 + +using namespace Dali; + +void utc_dali_rotation_gesture_startup(void) +{ + test_return_value = TET_UNDEF; +} + +void utc_dali_rotation_gesture_cleanup(void) +{ + test_return_value = TET_PASS; +} + +// Positive test case for a method +int UtcDaliRotationGestureConstructor(void) +{ + TestApplication application; // Reset all test adapter return codes + + RotationGesture gesture(Gesture::Started); + DALI_TEST_EQUALS(Gesture::Started, gesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(0.0f, gesture.rotation.radian, TEST_LOCATION); + DALI_TEST_EQUALS(DevelGesture::Rotation, static_cast< DevelGesture::Type >( gesture.type ), TEST_LOCATION); + + RotationGesture gesture2(Gesture::Continuing); + DALI_TEST_EQUALS(Gesture::Continuing, gesture2.state, TEST_LOCATION); + DALI_TEST_EQUALS(0.0f, gesture2.rotation.radian, TEST_LOCATION); + DALI_TEST_EQUALS(DevelGesture::Rotation, static_cast< DevelGesture::Type >( gesture2.type ), TEST_LOCATION); + + RotationGesture gesture3(Gesture::Finished); + DALI_TEST_EQUALS(Gesture::Finished, gesture3.state, TEST_LOCATION); + DALI_TEST_EQUALS(0.0f, gesture3.rotation.radian, TEST_LOCATION); + DALI_TEST_EQUALS(DevelGesture::Rotation, static_cast< DevelGesture::Type >( gesture3.type ), TEST_LOCATION); + + // Test copy constructor + gesture3.rotation = 3.0f; + + RotationGesture rotation(gesture3); + DALI_TEST_EQUALS(Gesture::Finished, rotation.state, TEST_LOCATION); + DALI_TEST_EQUALS(3.0f, rotation.rotation.radian, TEST_LOCATION); +DALI_TEST_EQUALS(DevelGesture::Rotation, static_cast< DevelGesture::Type >( rotation.type ), TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureAssignment(void) +{ + // Test Assignment operator + RotationGesture gesture(Gesture::Started); + DALI_TEST_EQUALS(Gesture::Started, gesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(0.0f, gesture.rotation.radian, TEST_LOCATION); + DALI_TEST_EQUALS(DevelGesture::Rotation, static_cast< DevelGesture::Type >( gesture.type ), TEST_LOCATION); + + RotationGesture gesture2(Gesture::Continuing); + DALI_TEST_EQUALS(Gesture::Continuing, gesture2.state, TEST_LOCATION); + DALI_TEST_EQUALS(0.0f, gesture2.rotation.radian, TEST_LOCATION); + DALI_TEST_EQUALS(DevelGesture::Rotation, static_cast< DevelGesture::Type >( gesture2.type ), TEST_LOCATION); + + gesture2.rotation.radian = 3.0f; + + gesture = gesture2; + DALI_TEST_EQUALS(Gesture::Continuing, gesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(3.0f, gesture.rotation.radian, TEST_LOCATION); + DALI_TEST_EQUALS(DevelGesture::Rotation, static_cast< DevelGesture::Type >( gesture.type ), TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureDynamicAllocation(void) +{ + RotationGesture* gesture = new RotationGesture( Gesture::Started ); + DALI_TEST_EQUALS(Gesture::Started, gesture->state, TEST_LOCATION); + DALI_TEST_EQUALS(0.0f, gesture->rotation.radian, TEST_LOCATION); + DALI_TEST_EQUALS(DevelGesture::Rotation, static_cast< DevelGesture::Type >( gesture->type ), TEST_LOCATION); + delete gesture; + + END_TEST; +} diff --git a/automated-tests/src/dali/utc-Dali-RotationGestureDetector.cpp b/automated-tests/src/dali/utc-Dali-RotationGestureDetector.cpp new file mode 100644 index 0000000..8d27655 --- /dev/null +++ b/automated-tests/src/dali/utc-Dali-RotationGestureDetector.cpp @@ -0,0 +1,1105 @@ +/* + * Copyright (c) 2019 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 +#include + +using namespace Dali; + +void utc_dali_rotation_gesture_detector_startup(void) +{ + test_return_value = TET_UNDEF; +} + +void utc_dali_rotation_gesture_detector_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), + voidFunctorCalled(false), + receivedGesture(Gesture::Started) + {} + + void Reset() + { + functorCalled = false; + voidFunctorCalled = false; + + receivedGesture.state = Gesture::Started; + receivedGesture.rotation = 0.0f; + receivedGesture.screenCenterPoint = Vector2(0.0f, 0.0f); + receivedGesture.localCenterPoint = Vector2(0.0f, 0.0f); + + rotatedActor.Reset(); + } + + bool functorCalled; + bool voidFunctorCalled; + RotationGesture receivedGesture; + Actor rotatedActor; +}; + +// Functor that sets the data when called +struct GestureReceivedFunctor +{ + GestureReceivedFunctor(SignalData& data) : signalData(data) { } + + void operator()(Actor actor, const RotationGesture& rotation) + { + signalData.functorCalled = true; + signalData.receivedGesture = rotation; + signalData.rotatedActor = actor; + } + + void operator()() + { + signalData.voidFunctorCalled = true; + } + + SignalData& signalData; +}; + +// Functor that removes the gestured actor from stage +struct UnstageActorFunctor : public GestureReceivedFunctor +{ + UnstageActorFunctor( SignalData& data, Gesture::State& stateToUnstage ) + : GestureReceivedFunctor( data ), + stateToUnstage( stateToUnstage ) + { + } + + void operator()( Actor actor, const RotationGesture& rotation ) + { + GestureReceivedFunctor::operator()( actor, rotation ); + + if ( rotation.state == stateToUnstage ) + { + Stage::GetCurrent().Remove( actor ); + } + } + + Gesture::State& stateToUnstage; +}; + +// Functor for receiving a touch event +struct TouchEventFunctor +{ + bool operator()(Actor actor, const TouchEvent& touch) + { + return false; + } +}; + +} // anon namespace + +/////////////////////////////////////////////////////////////////////////////// + +int UtcDaliRotationGestureDetectorConstructor(void) +{ + TestApplication application; + + RotationGestureDetector detector; + DALI_TEST_CHECK(!detector); + END_TEST; +} + +int UtcDaliRotationGestureDetectorCopyConstructorP(void) +{ + TestApplication application; + + RotationGestureDetector detector = RotationGestureDetector::New();; + + RotationGestureDetector copy( detector ); + DALI_TEST_CHECK( detector ); + END_TEST; +} + +int UtcDaliRotationGestureDetectorAssignmentOperatorP(void) +{ + TestApplication application; + + RotationGestureDetector detector = RotationGestureDetector::New();; + + RotationGestureDetector assign; + assign = detector; + DALI_TEST_CHECK( detector ); + + DALI_TEST_CHECK( detector == assign ); + END_TEST; +} + +int UtcDaliRotationGestureDetectorNew(void) +{ + TestApplication application; + + RotationGestureDetector detector = RotationGestureDetector::New(); + + DALI_TEST_CHECK(detector); + + // Attach an actor and emit a touch event on the actor to ensure complete line coverage + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + detector.Attach(actor); + + Integration::TouchEvent touchEvent(1); + Integration::Point point; + point.SetDeviceId( 1 ); + point.SetState( PointState::DOWN ); + point.SetScreenPosition( Vector2( 20.0f, 20.0f ) ); + touchEvent.AddPoint(point); + application.ProcessEvent(touchEvent); + + Integration::Point point2; + point.SetDeviceId( 1 ); + point.SetState( PointState::DOWN ); + point.SetScreenPosition( Vector2( 20.0f, 20.0f ) ); + touchEvent.AddPoint(point2); + application.ProcessEvent(touchEvent); + END_TEST; +} + +int UtcDaliRotationGestureDetectorDownCast(void) +{ + TestApplication application; + tet_infoline("Testing Dali::RotationGestureDetector::DownCast()"); + + RotationGestureDetector detector = RotationGestureDetector::New(); + + BaseHandle object(detector); + + RotationGestureDetector detector2 = RotationGestureDetector::DownCast(object); + DALI_TEST_CHECK(detector2); + + RotationGestureDetector detector3 = DownCast< RotationGestureDetector >(object); + DALI_TEST_CHECK(detector3); + + BaseHandle unInitializedObject; + RotationGestureDetector detector4 = RotationGestureDetector::DownCast(unInitializedObject); + DALI_TEST_CHECK(!detector4); + + RotationGestureDetector detector5 = DownCast< RotationGestureDetector >(unInitializedObject); + DALI_TEST_CHECK(!detector5); + + GestureDetector detector6 = RotationGestureDetector::New(); + RotationGestureDetector detector7 = RotationGestureDetector::DownCast(detector6); + DALI_TEST_CHECK(detector7); + END_TEST; +} + +// Negative test case for a method +int UtcDaliRotationGestureSignalReceptionNegative(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.DetectedSignal().Connect(&application, functor); + + // Do a rotation outside actor's area + TestStartRotation( application, Vector2( 112.0f, 62.0f ), Vector2( 112.0f, 162.0f ), + Vector2( 112.0f, 100.0f ), Vector2( 112.0f, 124.0f ), 100 ); + + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + + // Continue rotation into actor's area - we should still not receive the signal + data.Reset(); + TestContinueRotation( application, Vector2( 112.0f, 100.0f ), Vector2( 112.0f, 124.0f ), + Vector2( 5.0f, 5.0f ), Vector2( 35.0f, 35.0f ), 200 ); + + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + + // Stop rotating - we should still not receive the signal + data.Reset(); + TestEndRotation( application, Vector2( 6.0f, 6.0f ), Vector2( 18.0f, 18.0f ), + Vector2( 10.0f, 8.0f ), Vector2( 14.0f, 16.0f ), 300 ); + + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionDownMotionLeave(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.DetectedSignal().Connect(&application, functor); + + // Start pan within the actor's area + TestStartRotation( application, Vector2( 5.0f, 5.0f ), Vector2( 20.0f, 20.0f ), + Vector2( 5.0f, 5.0f ), Vector2( 20.0f, 30.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Started, data.receivedGesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(0.244f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(12.5f, 17.5f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + + // Continue the pan within the actor's area - we should still receive the signal + data.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 17.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 400 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Continuing, data.receivedGesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(-0.785398f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(21.0f, 20.0f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + + // Pan Gesture leaves actor's area - we should still receive the signal + data.Reset(); + TestContinueRotation( application, Vector2( 17.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 300.0f, 10.0f ), Vector2( 340.0f, 10.0f ), 1000 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Continuing, data.receivedGesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(-0.785398f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(320.0f, 10.0f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + + // Gesture ends - we would receive a finished state + data.Reset(); + TestEndRotation( application, Vector2( 300.0f, 10.0f ), Vector2( 340.0f, 10.0f ), + Vector2( 305.0f, 10.0f ), Vector2( 315.0f, 10.0f ), 1500); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Finished, data.receivedGesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(-0.785398f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(310.0f, 10.0f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionDownMotionUp(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.DetectedSignal().Connect(&application, functor); + + // Start rotation within the actor's area + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 31.0f, 29.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Started, data.receivedGesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(0.404892f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(20.5f, 24.5f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + + // Continue the rotation within the actor's area - we should still receive the signal + data.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 29.0f, 15.0f ), 500 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Continuing, data.receivedGesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(-0.343024f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(22.0f, 17.5f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + + // Gesture ends within actor's area - we would receive a finished state + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 29.0f, 15.0f ), 1000); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Finished, data.receivedGesture.state, TEST_LOCATION); + DALI_TEST_EQUALS(-0.463648f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(24.0f, 17.5f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionDetach(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.DetectedSignal().Connect(&application, functor); + + // Start rotation within the actor's area + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Started, data.receivedGesture.state, TEST_LOCATION); + + + // Continue the rotation within the actor's area - we should still receive the signal + data.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 500 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Continuing, data.receivedGesture.state, TEST_LOCATION); + + // Gesture ends within actor's area + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 1000); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Finished, data.receivedGesture.state, TEST_LOCATION); + + // Detach actor + detector.DetachAll(); + + // Ensure we are no longer signalled + data.Reset(); + TestGenerateRotation( application ); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionDetachWhileRotationing(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.DetectedSignal().Connect(&application, functor); + + // Start rotation within the actor's area + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Started, data.receivedGesture.state, TEST_LOCATION); + + // Continue the rotation within the actor's area - we should still receive the signal + data.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 500 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Continuing, data.receivedGesture.state, TEST_LOCATION); + + // Detach actor during the rotation, we should not receive the next event + detector.DetachAll(); + + // Gesture ends within actor's area + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 1000); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionActorDestroyedWhileRotationing(void) +{ + TestApplication application; + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.DetectedSignal().Connect(&application, functor); + + // Attach a temporary actor to stop detector being removed from RotationGestureProcessor when main actor + // is destroyed. + Actor tempActor = Actor::New(); + tempActor.SetSize(100.0f, 100.0f); + tempActor.SetAnchorPoint(AnchorPoint::BOTTOM_RIGHT); + Stage::GetCurrent().Add(tempActor); + detector.Attach(tempActor); + + // Actor lifetime is scoped + { + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + detector.Attach(actor); + + // Start rotation within the actor's area + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Started, data.receivedGesture.state, TEST_LOCATION); + + // Continue the rotation within the actor's area - we should still receive the signal + data.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 500 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(Gesture::Continuing, data.receivedGesture.state, TEST_LOCATION); + + // Remove the actor from stage and reset the data + Stage::GetCurrent().Remove(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + } + + // Actor should now have been destroyed + + // Gesture ends within the area where the actor used to be + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 1000); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionRotatedActor(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetOrientation(Dali::Degree(90.0f), Vector3::ZAXIS); + Stage::GetCurrent().Add(actor); + + // Render and notify a couple of times + application.SendNotification(); + application.Render(); + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.DetectedSignal().Connect(&application, functor); + + // Do an entire rotation, only check finished value + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 27.0f, 15.0f ), 1000); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(-0.558599f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(23.0f, 17.5f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + + // Rotate actor again and render and notify + actor.SetOrientation(Dali::Degree(180.0f), Vector3::ZAXIS); + application.SendNotification(); + application.Render(); + + // Do an entire rotation, only check finished value + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 2100 ); + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 27.0f, 15.0f ), 3000); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(-0.558599f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(23.0f, 17.5f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + + // Rotate actor again and render and notify + actor.SetOrientation(Dali::Degree(270.0f), Vector3::ZAXIS); + application.SendNotification(); + application.Render(); + + // Do an entire rotation, only check finished value + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 4100 ); + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 27.0f, 15.0f ), 5000); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(-0.558599f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(23.0f, 17.5f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionChildHit(void) +{ + TestApplication application; + + Actor parent = Actor::New(); + parent.SetSize(100.0f, 100.0f); + parent.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(parent); + + // Set child to completely cover parent. + // Change rotation of child to be different from parent so that we can check if our local coordinate + // conversion of the parent actor is correct. + Actor child = Actor::New(); + child.SetSize(100.0f, 100.0f); + child.SetAnchorPoint(AnchorPoint::CENTER); + child.SetParentOrigin(ParentOrigin::CENTER); + child.SetOrientation(Dali::Degree(90.0f), Vector3::ZAXIS); + parent.Add(child); + + TouchEventFunctor touchFunctor; + child.TouchedSignal().Connect(&application, touchFunctor); + + // Render and notify + application.SendNotification(); + application.Render(); + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(parent); + detector.DetectedSignal().Connect(&application, functor); + + // Do an entire pan, only check finished value - hits child area but parent should still receive it + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 29.0f, 25.0f ), 1000); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, parent == data.rotatedActor, TEST_LOCATION); + DALI_TEST_EQUALS(0.463648f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(24.0f, 22.5f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + + // Attach child and generate same touch points to yield same results + // (Also proves that you can detach and then re-attach another actor) + detector.Attach(child); + detector.Detach(parent); + + // Do an entire pan, only check finished value + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 2100 ); + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 29.0f, 35.0f ), 3000); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, child == data.rotatedActor, TEST_LOCATION); + DALI_TEST_EQUALS(0.982794f, data.receivedGesture.rotation.radian, 0.01f, TEST_LOCATION); + DALI_TEST_EQUALS(Vector2(24.0f, 27.5f), data.receivedGesture.screenCenterPoint, 0.01f, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionAttachDetachMany(void) +{ + TestApplication application; + + Actor first = Actor::New(); + first.SetSize(100.0f, 100.0f); + first.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(first); + + Actor second = Actor::New(); + second.SetSize(100.0f, 100.0f); + second.SetX(100.0f); + second.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(second); + + // Render and notify + application.SendNotification(); + application.Render(); + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(first); + detector.Attach(second); + detector.DetectedSignal().Connect(&application, functor); + + // Start rotation within second actor's area + TestStartRotation( application, Vector2( 102.0f, 20.0f ), Vector2( 138.0f, 20.0f ), + Vector2( 110.0f, 20.0f ), Vector2( 130.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, second == data.rotatedActor, TEST_LOCATION); + + // Rotation moves into first actor's area - second actor should receive the rotation + data.Reset(); + TestContinueRotation( application, Vector2( 110.0f, 20.0f ), Vector2( 130.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 500 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, second == data.rotatedActor, TEST_LOCATION); + + // Detach the second actor during the rotation, we should not receive the next event + detector.Detach(second); + + // Gesture ends within actor's area + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 119.0f, 20.0f ), Vector2( 121.0f, 20.0f ), 3000); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionActorBecomesUntouchable(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + SignalData data; + GestureReceivedFunctor functor(data); + + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.DetectedSignal().Connect(&application, functor); + + // Start rotation in actor's area + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + + // Pan continues within actor's area + data.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 500 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + + // Actor become invisible - actor should not receive the next rotation + actor.SetVisible(false); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Gesture ends within actor's area + data.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 3000); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionMultipleDetectorsOnActor(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + Actor actor2 = Actor::New(); + actor2.SetSize(100.0f, 100.0f); + actor2.SetAnchorPoint(AnchorPoint::BOTTOM_RIGHT); + Stage::GetCurrent().Add(actor2); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Attach actor to one detector + SignalData firstData; + GestureReceivedFunctor firstFunctor(firstData); + RotationGestureDetector firstDetector = RotationGestureDetector::New(); + firstDetector.Attach(actor); + firstDetector.DetectedSignal().Connect(&application, firstFunctor); + + // Attach actor to another detector + SignalData secondData; + GestureReceivedFunctor secondFunctor(secondData); + RotationGestureDetector secondDetector = RotationGestureDetector::New(); + secondDetector.Attach(actor); + secondDetector.DetectedSignal().Connect(&application, secondFunctor); + + // Add second actor to second detector, when we remove the actor, this will make sure that this + // gesture detector is not removed from the GestureDetectorProcessor. In this scenario, the + // functor should still not be called (which is what we're also testing). + secondDetector.Attach(actor2); + + // Rotation in actor's area - both detector's functors should be called + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, firstData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, secondData.functorCalled, TEST_LOCATION); + + // Rotation continues in actor's area - both detector's functors should be called + firstData.Reset(); + secondData.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 500 ); + DALI_TEST_EQUALS(true, firstData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, secondData.functorCalled, TEST_LOCATION); + + // Detach actor from firstDetector and emit rotation on actor, only secondDetector's functor should be called. + firstDetector.Detach(actor); + firstData.Reset(); + secondData.Reset(); + TestEndRotation( application, Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 1000); + DALI_TEST_EQUALS(false, firstData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, secondData.functorCalled, TEST_LOCATION); + + // New rotation on actor, only secondDetector has actor attached + firstData.Reset(); + secondData.Reset(); + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 1500 ); + DALI_TEST_EQUALS(false, firstData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(true, secondData.functorCalled, TEST_LOCATION); + + // Detach actor from secondDetector + secondDetector.Detach(actor); + firstData.Reset(); + secondData.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 2000 ); + DALI_TEST_EQUALS(false, firstData.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, secondData.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureSignalReceptionEnsureCorrectSignalling(void) +{ + TestApplication application; + + Actor actor1 = Actor::New(); + actor1.SetSize(100.0f, 100.0f); + actor1.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor1); + SignalData data1; + GestureReceivedFunctor functor1(data1); + RotationGestureDetector detector1 = RotationGestureDetector::New(); + detector1.Attach(actor1); + detector1.DetectedSignal().Connect(&application, functor1); + + Actor actor2 = Actor::New(); + actor2.SetSize(100.0f, 100.0f); + actor2.SetAnchorPoint(AnchorPoint::BOTTOM_RIGHT); + actor2.SetParentOrigin(ParentOrigin::BOTTOM_RIGHT); + Stage::GetCurrent().Add(actor2); + SignalData data2; + GestureReceivedFunctor functor2(data2); + RotationGestureDetector detector2 = RotationGestureDetector::New(); + detector2.Attach(actor2); + detector2.DetectedSignal().Connect(&application, functor2); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Start pan in actor1's area, only data1 should be set + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data1.functorCalled, TEST_LOCATION); + DALI_TEST_EQUALS(false, data2.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureActorUnstaged(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // State to remove actor in. + Gesture::State stateToUnstage( Gesture::Started ); + + // Attach actor to detector + SignalData data; + UnstageActorFunctor functor( data, stateToUnstage ); + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.DetectedSignal().Connect( &application, functor ); + + // Emit signals + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + TestEndRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 1000); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Re-add actor to stage + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Change state to Gesture::Continuing to remove + stateToUnstage = Gesture::Continuing; + + // Emit signals + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 500 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + TestEndRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 1000); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Re-add actor to stage + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Change state to Gesture::Continuing to remove + stateToUnstage = Gesture::Finished; + + // Emit signals + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 500 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + TestEndRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 1000); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + tet_result( TET_PASS ); // If we get here then we have handled actor stage removal gracefully. + END_TEST; +} + +int UtcDaliRotationGestureActorStagedAndDestroyed(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Create and add a second actor so that GestureDetector destruction does not come into play. + Actor dummyActor( Actor::New() ); + dummyActor.SetSize( 100.0f, 100.0f ); + dummyActor.SetPosition( 100.0f, 100.0f ); + dummyActor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(dummyActor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // State to remove actor in. + Gesture::State stateToUnstage( Gesture::Started ); + + // Attach actor to detector + SignalData data; + UnstageActorFunctor functor( data, stateToUnstage ); + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.Attach(dummyActor); + detector.DetectedSignal().Connect( &application, functor ); + + // Here we are testing a Started actor which is removed in the Started callback, but then added back + // before we get a continuing state. As we were removed from the stage, even if we're at the same + // position, we should still not be signalled. + + // Emit signals + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Re add to the stage, we should not be signalled + Stage::GetCurrent().Add(actor); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Continue signal emission + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 500 ); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + TestEndRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 1000); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Here we delete an actor in started, we should not receive any subsequent signalling. + + // Emit signals + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 1500 ); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Delete actor as well + actor.Reset(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Continue signal emission + TestContinueRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 15.0f, 20.0f ), Vector2( 25.0f, 20.0f ), 2000 ); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + TestEndRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 3000); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + END_TEST; +} + +int UtcDaliRotationGestureLayerConsumesTouch(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Add a detector + SignalData data; + GestureReceivedFunctor functor(data); + RotationGestureDetector detector = RotationGestureDetector::New(); + detector.Attach(actor); + detector.DetectedSignal().Connect( &application, functor ); + + // Add a layer to overlap the actor + Layer layer = Layer::New(); + layer.SetSize(100.0f, 100.0f); + layer.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add( layer ); + layer.RaiseToTop(); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit signals, should receive + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 100 ); + TestEndRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 1000); + DALI_TEST_EQUALS(true, data.functorCalled, TEST_LOCATION); + data.Reset(); + + // Set layer to consume all touch + layer.SetTouchConsumed( true ); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit the same signals again, should not receive + TestStartRotation( application, Vector2( 2.0f, 20.0f ), Vector2( 38.0f, 20.0f ), + Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), 1500 ); + TestEndRotation( application, Vector2( 10.0f, 20.0f ), Vector2( 30.0f, 20.0f ), + Vector2( 19.0f, 20.0f ), Vector2( 21.0f, 20.0f ), 2000); + DALI_TEST_EQUALS(false, data.functorCalled, TEST_LOCATION); + data.Reset(); + + END_TEST; +} + diff --git a/automated-tests/src/dali/utc-Dali-TypeRegistry.cpp b/automated-tests/src/dali/utc-Dali-TypeRegistry.cpp index fe65d5e..fd6d4cb 100644 --- a/automated-tests/src/dali/utc-Dali-TypeRegistry.cpp +++ b/automated-tests/src/dali/utc-Dali-TypeRegistry.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using namespace Dali; @@ -2170,6 +2171,43 @@ int UtcDaliPinchGestureDetectorTypeRegistry(void) END_TEST; } +int UtcDaliRotationGestureDetectorTypeRegistry(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetSize(100.0f, 100.0f); + actor.SetAnchorPoint(AnchorPoint::TOP_LEFT); + Stage::GetCurrent().Add(actor); + + // Register Type + TypeInfo type; + type = TypeRegistry::Get().GetTypeInfo( "RotationGestureDetector" ); + DALI_TEST_CHECK( type ); + BaseHandle handle = type.CreateInstance(); + DALI_TEST_CHECK( handle ); + RotationGestureDetector detector = RotationGestureDetector::DownCast( handle ); + DALI_TEST_CHECK( detector ); + + // Attach actor to detector + SignalData data; + GestureReceivedFunctor functor( data ); + detector.Attach(actor); + + // Connect to signal through type + handle.ConnectSignal( &application, "rotationDetected", functor ); + + // Render and notify + application.SendNotification(); + application.Render(); + + // Emit gesture + TestGenerateRotation( application ); + + DALI_TEST_EQUALS(true, data.voidFunctorCalled, TEST_LOCATION); + END_TEST; +} + int UtcDaliTapGestureDetectorTypeRegistry(void) { TestApplication application; diff --git a/dali/devel-api/events/gesture-devel.h b/dali/devel-api/events/gesture-devel.h new file mode 100644 index 0000000..45ca3df --- /dev/null +++ b/dali/devel-api/events/gesture-devel.h @@ -0,0 +1,47 @@ +#ifndef DALI_GESTURE_DEVEL_H +#define DALI_GESTURE_DEVEL_H + +/* + * Copyright (c) 2019 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 + +namespace Dali +{ + +namespace DevelGesture +{ + +/** + * @copydoc Dali::Gesture::Type + */ +enum Type +{ + Pinch = Gesture::Pinch, + Pan = Gesture::Pan, + Tap = Gesture::Tap, + LongPress = Gesture::LongPress, + + // Devel Gesture Types + Rotation = 1 << 4, ///< When the user rotates two fingers around a particular ares of the screen. +}; + +} // namespace DevelGesture + +} // namespace Dali + +#endif // DALI_GESTURE_DEVEL_H diff --git a/dali/devel-api/events/rotation-gesture-detector.cpp b/dali/devel-api/events/rotation-gesture-detector.cpp new file mode 100644 index 0000000..16f007e --- /dev/null +++ b/dali/devel-api/events/rotation-gesture-detector.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +RotationGestureDetector::RotationGestureDetector( Internal::RotationGestureDetector* internal ) +: GestureDetector( internal ) +{ +} + +RotationGestureDetector::RotationGestureDetector() +{ +} + +RotationGestureDetector RotationGestureDetector::New() +{ + Internal::RotationGestureDetectorPtr internal = Internal::RotationGestureDetector::New(); + + return RotationGestureDetector( internal.Get() ); +} + +RotationGestureDetector RotationGestureDetector::DownCast( BaseHandle handle ) +{ + return RotationGestureDetector( dynamic_cast( handle.GetObjectPtr() ) ); +} + +RotationGestureDetector::~RotationGestureDetector() +{ +} + +RotationGestureDetector::RotationGestureDetector( const RotationGestureDetector& handle ) +: GestureDetector( handle ) +{ +} + +RotationGestureDetector& RotationGestureDetector::operator=( const RotationGestureDetector& rhs ) +{ + BaseHandle::operator=( rhs ); + return *this; +} + +RotationGestureDetector::DetectedSignalType& RotationGestureDetector::DetectedSignal() +{ + return GetImplementation( *this ).DetectedSignal(); +} + +} // namespace Dali diff --git a/dali/devel-api/events/rotation-gesture-detector.h b/dali/devel-api/events/rotation-gesture-detector.h new file mode 100644 index 0000000..837110a --- /dev/null +++ b/dali/devel-api/events/rotation-gesture-detector.h @@ -0,0 +1,141 @@ +#ifndef DALI_ROTATION_GESTURE_DETECTOR_H +#define DALI_ROTATION_GESTURE_DETECTOR_H + +/* + * Copyright (c) 2019 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. + * + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Internal DALI_INTERNAL +{ +class RotationGestureDetector; +} + +struct RotationGesture; + +/** + * @brief This class looks for a rotation gesture involving two touches. + * + * It measures the relative rotation of two touch points & emits a signal + * when this changes. Please see RotationGesture for more information. + * + * The application programmer can use this gesture detector as follows: + * @code + * RotationGestureDetector detector = RotationGestureDetector::New(); + * detector.Attach( myActor ); + * detector.DetectedSignal().Connect( this, &MyApplication::OnRotation ); + * @endcode + * + * @see RotationGesture + * + * Signals + * | %Signal Name | Method | + * |-------------------|-----------------------| + * | rotationDetected | @ref DetectedSignal() | + */ +class DALI_CORE_API RotationGestureDetector : public GestureDetector +{ +public: // Typedefs + + /** + * @brief Signal type. + */ + typedef Signal< void ( Actor, const RotationGesture& ) > DetectedSignalType; + +public: // Creation & Destruction + + /** + * @brief Creates an uninitialized RotationGestureDetector; this can be initialized with RotationGestureDetector::New(). + * + * Calling member functions with an uninitialized RotationGestureDetector handle is not allowed. + */ + RotationGestureDetector(); + + /** + * @brief Creates an initialized RotationGestureDetector. + * + * @return A handle to a newly allocated Dali resource + */ + static RotationGestureDetector New(); + + /** + * @brief Downcasts a handle to RotationGestureDetector handle. + * + * If handle points to a RotationGestureDetector object, the + * downcast produces valid handle. If not, the returned handle is left uninitialized. + * @param[in] handle Handle to an object + * @return Handle to a RotationGestureDetector object or an uninitialized handle + */ + static RotationGestureDetector DownCast( BaseHandle handle ); + + /** + * @brief Destructor. + * + * This is non-virtual since derived Handle types must not contain data or virtual methods. + */ + ~RotationGestureDetector(); + + /** + * @brief This copy constructor is required for (smart) pointer semantics. + * + * @param[in] handle A reference to the copied handle + */ + RotationGestureDetector(const RotationGestureDetector& handle); + + /** + * @brief This assignment operator is required for (smart) pointer semantics. + * + * @param[in] rhs A reference to the copied handle + * @return A reference to this + */ + RotationGestureDetector& operator=(const RotationGestureDetector& rhs); + +public: // Signals + + /** + * @brief This signal is emitted when the rotation gesture is detected on the attached actor. + * + * A callback of the following type may be connected: + * @code + * void YourCallbackName( Actor actor, const RotationGesture& gesture ); + * @endcode + * @return The signal to connect to + * @pre The gesture detector has been initialized. + */ + DetectedSignalType& DetectedSignal(); + +public: // Not intended for Application developers + + /// @cond internal + /** + * @brief This constructor is used by RotationGestureDetector::New() methods. + * + * @param[in] internal A pointer to a newly allocated Dali resource + */ + explicit DALI_INTERNAL RotationGestureDetector(Internal::RotationGestureDetector* internal); + /// @endcond + +}; + +} // namespace Dali + +#endif // DALI_ROTATION_GESTURE_DETECTOR_H diff --git a/dali/devel-api/events/rotation-gesture.cpp b/dali/devel-api/events/rotation-gesture.cpp new file mode 100644 index 0000000..05b1258 --- /dev/null +++ b/dali/devel-api/events/rotation-gesture.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +RotationGesture::RotationGesture( Gesture::State state ) +: Gesture( static_cast< Gesture::Type >( DevelGesture::Rotation ), state ) +{ +} + +RotationGesture::RotationGesture( const RotationGesture& rhs ) +: Gesture( rhs ), + rotation( rhs.rotation ), + screenCenterPoint( rhs.screenCenterPoint ), + localCenterPoint( rhs.localCenterPoint ) +{ +} + +RotationGesture& RotationGesture::operator=( const RotationGesture& rhs ) +{ + if( this != &rhs ) + { + Gesture::operator=( rhs ); + rotation = rhs.rotation; + screenCenterPoint = rhs.screenCenterPoint; + localCenterPoint = rhs.localCenterPoint; + } + + return *this; +} + +RotationGesture::~RotationGesture() +{ +} + +} // namespace Dali diff --git a/dali/devel-api/events/rotation-gesture.h b/dali/devel-api/events/rotation-gesture.h new file mode 100644 index 0000000..5f3e2f8 --- /dev/null +++ b/dali/devel-api/events/rotation-gesture.h @@ -0,0 +1,90 @@ +#ifndef DALI_ROTATION_GESTURE_H +#define DALI_ROTATION_GESTURE_H + +/* + * Copyright (c) 2019 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. + * + */ + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +/** + * @brief A RotationGesture is emitted when the user moves two fingers that are opposite each other + * in a rotational/circular gesture. + * + * This gesture can be in one of three states; when the rotation gesture is first detected, its + * state is set to Gesture::Started. After this, if there is change in the gesture, the state will + * be Gesture::Continuing. Finally, when the gesture ends, the state of the gesture changes to + * Gesture::Finished. + * + * A rotation gesture will continue to be sent to the actor under the center point of the rotation + * until the rotation ends. + */ +struct DALI_CORE_API RotationGesture: public Gesture +{ + // Construction & Destruction + + /** + * @brief Default Constructor. + * + * @param[in] state The state of the gesture + */ + RotationGesture( Gesture::State state ); + + /** + * @brief Copy constructor. + * @param[in] rhs A reference to the copied handle + */ + RotationGesture( const RotationGesture& rhs ); + + /** + * @brief Assignment operator. + * @param[in] rhs A reference to the copied handle + * @return A reference to this + */ + RotationGesture& operator=( const RotationGesture& rhs ); + + /** + * @brief Virtual destructor. + */ + virtual ~RotationGesture(); + + // Data + + /** + * @brief The overall rotation from the start of the rotation gesture till the latest rotation gesture. + */ + Radian rotation; + + /** + * @brief The center point of the two points that caused the rotation gesture in screen coordinates. + */ + Vector2 screenCenterPoint; + + /** + * @brief The center point of the two points that caused the rotation gesture in local actor coordinates. + */ + Vector2 localCenterPoint; +}; + +} // namespace Dali + +#endif // DALI_ROTATION_GESTURE_H diff --git a/dali/devel-api/file.list b/dali/devel-api/file.list index 2ed30a5..8efb7a9 100644 --- a/dali/devel-api/file.list +++ b/dali/devel-api/file.list @@ -10,6 +10,8 @@ SET( devel_api_src_files ${devel_api_src_dir}/animation/path-constrainer.cpp ${devel_api_src_dir}/common/hash.cpp ${devel_api_src_dir}/common/stage-devel.cpp + ${devel_api_src_dir}/events/rotation-gesture.cpp + ${devel_api_src_dir}/events/rotation-gesture-detector.cpp ${devel_api_src_dir}/events/hit-test-algorithm.cpp ${devel_api_src_dir}/events/touch-data-devel.cpp ${devel_api_src_dir}/events/key-event-devel.cpp @@ -57,6 +59,8 @@ SET( devel_api_core_common_header_files SET( devel_api_core_events_header_files + ${devel_api_src_dir}/events/rotation-gesture.h + ${devel_api_src_dir}/events/rotation-gesture-detector.h ${devel_api_src_dir}/events/hit-test-algorithm.h ${devel_api_src_dir}/events/touch-data-devel.h ${devel_api_src_dir}/events/key-event-devel.h diff --git a/dali/internal/event/actors/actor-impl.cpp b/dali/internal/event/actors/actor-impl.cpp index 876182f..44a9e89 100644 --- a/dali/internal/event/actors/actor-impl.cpp +++ b/dali/internal/event/actors/actor-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 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. @@ -1782,7 +1782,7 @@ ActorGestureData& Actor::GetGestureData() return *mGestureData; } -bool Actor::IsGestureRequred( Gesture::Type type ) const +bool Actor::IsGestureRequred( DevelGesture::Type type ) const { return mGestureData && mGestureData->IsGestureRequred( type ); } diff --git a/dali/internal/event/actors/actor-impl.h b/dali/internal/event/actors/actor-impl.h index 53fe06a..92e6ab8 100755 --- a/dali/internal/event/actors/actor-impl.h +++ b/dali/internal/event/actors/actor-impl.h @@ -2,7 +2,7 @@ #define DALI_INTERNAL_ACTOR_H /* - * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 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. @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -1388,8 +1389,9 @@ public: /** * Queries whether the actor requires the gesture type. * @param[in] type The gesture type. + * @return True if the gesture is required, false otherwise. */ - bool IsGestureRequred( Gesture::Type type ) const; + bool IsGestureRequred( DevelGesture::Type type ) const; // Signals diff --git a/dali/internal/event/events/actor-gesture-data.cpp b/dali/internal/event/events/actor-gesture-data.cpp index c331c56..a6efbf0 100644 --- a/dali/internal/event/events/actor-gesture-data.cpp +++ b/dali/internal/event/events/actor-gesture-data.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Copyright (c) 2019 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. @@ -25,11 +25,12 @@ namespace Internal { ActorGestureData::ActorGestureData() -: gesturesRequired( Gesture::Type( 0 ) ), - panDetectors( NULL ), - pinchDetectors( NULL ), - longPressDetectors( NULL ), - tapDetectors( NULL ) +: gesturesRequired( DevelGesture::Type( 0 ) ), + panDetectors( nullptr ), + pinchDetectors( nullptr ), + longPressDetectors( nullptr ), + tapDetectors( nullptr ), + rotationDetectors( nullptr ) { } @@ -39,11 +40,12 @@ ActorGestureData::~ActorGestureData() delete pinchDetectors; delete longPressDetectors; delete tapDetectors; + delete rotationDetectors; } void ActorGestureData::AddGestureDetector( GestureDetector& detector ) { - const Gesture::Type type( detector.GetType() ); + const DevelGesture::Type type( detector.GetType() ); GestureDetectorContainer*& containerPtr( GetContainerPtr( type ) ); if ( NULL == containerPtr ) @@ -52,12 +54,12 @@ void ActorGestureData::AddGestureDetector( GestureDetector& detector ) } containerPtr->push_back( &detector ); - gesturesRequired = Gesture::Type( gesturesRequired | type ); + gesturesRequired = DevelGesture::Type( gesturesRequired | type ); } void ActorGestureData::RemoveGestureDetector( GestureDetector& detector ) { - const Gesture::Type type( detector.GetType() ); + const DevelGesture::Type type( detector.GetType() ); GestureDetectorContainer*& containerPtr( GetContainerPtr( type ) ); DALI_ASSERT_DEBUG( containerPtr && "Container had not been created" ); @@ -69,40 +71,45 @@ void ActorGestureData::RemoveGestureDetector( GestureDetector& detector ) if ( container.empty() ) { - gesturesRequired = Gesture::Type( gesturesRequired & ~type ); + gesturesRequired = DevelGesture::Type( gesturesRequired & ~type ); delete containerPtr; containerPtr = NULL; } } -GestureDetectorContainer& ActorGestureData::GetGestureDetectorContainer( Gesture::Type type ) +GestureDetectorContainer& ActorGestureData::GetGestureDetectorContainer( DevelGesture::Type type ) { return *GetContainerPtr( type ); } -GestureDetectorContainer*& ActorGestureData::GetContainerPtr( Gesture::Type type ) +GestureDetectorContainer*& ActorGestureData::GetContainerPtr( DevelGesture::Type type ) { switch ( type ) { - case Gesture::Pan: + case DevelGesture::Pan: { return panDetectors; } - case Gesture::Pinch: + case DevelGesture::Pinch: { return pinchDetectors; } - case Gesture::LongPress: + case DevelGesture::LongPress: { return longPressDetectors; } - case Gesture::Tap: + case DevelGesture::Tap: { return tapDetectors; } + + case DevelGesture::Rotation: + { + return rotationDetectors; + } } DALI_ASSERT_DEBUG( ! "Invalid Type" ); diff --git a/dali/internal/event/events/actor-gesture-data.h b/dali/internal/event/events/actor-gesture-data.h index 47eb9ed..8412e54 100644 --- a/dali/internal/event/events/actor-gesture-data.h +++ b/dali/internal/event/events/actor-gesture-data.h @@ -19,6 +19,7 @@ */ // INTERNAL INCLUDES +#include #include namespace Dali @@ -66,7 +67,7 @@ public: * @param[in] type The gesture type. * @return true if the gesture is required, false otherwise. */ - inline bool IsGestureRequred( Gesture::Type type ) const + inline bool IsGestureRequred( DevelGesture::Type type ) const { return type & gesturesRequired; } @@ -76,7 +77,7 @@ public: * @param[in] type The container type required * @pre Ensure IsGestureRequired() is used to check if the container is actually available. */ - GestureDetectorContainer& GetGestureDetectorContainer( Gesture::Type type ); + GestureDetectorContainer& GetGestureDetectorContainer( DevelGesture::Type type ); private: @@ -84,16 +85,17 @@ private: * Helper to retrieve the appropriate container type. * @param[in] type The container type required. */ - inline GestureDetectorContainer*& GetContainerPtr( Gesture::Type type ); + inline GestureDetectorContainer*& GetContainerPtr( DevelGesture::Type type ); private: - Gesture::Type gesturesRequired; ///< Stores which gestures are required + DevelGesture::Type gesturesRequired; ///< Stores which gestures are required GestureDetectorContainer* panDetectors; ///< Pointer to a container of pan-detectors GestureDetectorContainer* pinchDetectors; ///< Pointer to a container of pinch-detectors GestureDetectorContainer* longPressDetectors; ///< Pointer to a container of long-press-detectors GestureDetectorContainer* tapDetectors; ///< Pointer to a container of tap-detectors + GestureDetectorContainer* rotationDetectors; ///< Pointer to a container of tap-detectors }; } // namespace Internal diff --git a/dali/internal/event/events/gesture-detector-impl.cpp b/dali/internal/event/events/gesture-detector-impl.cpp index c541a7a..7748d36 100644 --- a/dali/internal/event/events/gesture-detector-impl.cpp +++ b/dali/internal/event/events/gesture-detector-impl.cpp @@ -34,13 +34,18 @@ namespace Dali namespace Internal { -GestureDetector::GestureDetector(Gesture::Type type, const SceneGraph::PropertyOwner* sceneObject ) +GestureDetector::GestureDetector( DevelGesture::Type type, const SceneGraph::PropertyOwner* sceneObject ) : Object( sceneObject ), mType( type ), mGestureEventProcessor( ThreadLocalStorage::Get().GetGestureEventProcessor() ) { } +GestureDetector::GestureDetector( Gesture::Type type, const SceneGraph::PropertyOwner* sceneObject ) +: GestureDetector( static_cast< DevelGesture::Type >( type ), sceneObject ) +{ +} + GestureDetector::~GestureDetector() { if ( !mPendingAttachActors.empty() ) diff --git a/dali/internal/event/events/gesture-detector-impl.h b/dali/internal/event/events/gesture-detector-impl.h index 54976d6..7cb0ffe 100644 --- a/dali/internal/event/events/gesture-detector-impl.h +++ b/dali/internal/event/events/gesture-detector-impl.h @@ -98,7 +98,7 @@ public: * Retrieves the type of GestureDetector * @return The GestureDetector Type */ - Gesture::Type GetType() const + DevelGesture::Type GetType() const { return mType; } @@ -118,7 +118,12 @@ protected: // Creation & Destruction * @param pointer to the scene object, nullptr if none * by default GestureDetectors don't have our own scene object */ - GestureDetector(Gesture::Type type, const SceneGraph::PropertyOwner* sceneObject = nullptr ); + GestureDetector( DevelGesture::Type type, const SceneGraph::PropertyOwner* sceneObject = nullptr ); + + /** + * @copydoc GestureDetector( DevelGesture::Type, const SceneGraph::PropertyOwner* ) + */ + GestureDetector( Gesture::Type type, const SceneGraph::PropertyOwner* sceneObject = nullptr ); /** * A reference counted object may only be deleted by calling Unreference() @@ -169,7 +174,7 @@ private: protected: - Gesture::Type mType; ///< The gesture detector will detect this type of gesture. + DevelGesture::Type mType; ///< The gesture detector will detect this type of gesture. GestureDetectorActorContainer mAttachedActors; ///< Object::Observer is used to provide weak-pointer behaviour GestureDetectorActorContainer mPendingAttachActors; ///< Object::Observer is used to provide weak-pointer behaviour GestureEventProcessor& mGestureEventProcessor; ///< A reference to the gesture event processor. diff --git a/dali/internal/event/events/gesture-event-processor.cpp b/dali/internal/event/events/gesture-event-processor.cpp index efe0ace..fbfad1b 100644 --- a/dali/internal/event/events/gesture-event-processor.cpp +++ b/dali/internal/event/events/gesture-event-processor.cpp @@ -41,6 +41,7 @@ GestureEventProcessor::GestureEventProcessor( SceneGraph::UpdateManager& updateM mPanGestureProcessor( updateManager ), mPinchGestureProcessor(), mTapGestureProcessor(), + mRotationGestureProcessor(), mRenderController( renderController ), envOptionMinimumPanDistance(-1), envOptionMinimumPanEvents(-1) @@ -57,39 +58,47 @@ void GestureEventProcessor::ProcessTouchEvent( Scene& scene, const Integration:: mPanGestureProcessor.ProcessTouch(scene, event); mPinchGestureProcessor.ProcessTouch(scene, event); mTapGestureProcessor.ProcessTouch(scene, event); + mRotationGestureProcessor.ProcessTouch(scene, event); } void GestureEventProcessor::AddGestureDetector(GestureDetector* gestureDetector, Scene& scene) { switch (gestureDetector->GetType()) { - case Gesture::LongPress: + case DevelGesture::LongPress: { LongPressGestureDetector* longPress = static_cast(gestureDetector); mLongPressGestureProcessor.AddGestureDetector(longPress, scene); break; } - case Gesture::Pan: + case DevelGesture::Pan: { PanGestureDetector* pan = static_cast(gestureDetector); mPanGestureProcessor.AddGestureDetector(pan, scene, envOptionMinimumPanDistance, envOptionMinimumPanEvents); break; } - case Gesture::Pinch: + case DevelGesture::Pinch: { PinchGestureDetector* pinch = static_cast(gestureDetector); mPinchGestureProcessor.AddGestureDetector(pinch, scene); break; } - case Gesture::Tap: + case DevelGesture::Tap: { TapGestureDetector* tap = static_cast(gestureDetector); mTapGestureProcessor.AddGestureDetector(tap, scene); break; } + + case DevelGesture::Rotation: + { + RotationGestureDetector* rotation = static_cast(gestureDetector); + mRotationGestureProcessor.AddGestureDetector(rotation, scene); + break; + } } } @@ -97,33 +106,40 @@ void GestureEventProcessor::RemoveGestureDetector(GestureDetector* gestureDetect { switch (gestureDetector->GetType()) { - case Gesture::LongPress: + case DevelGesture::LongPress: { LongPressGestureDetector* longPress = static_cast(gestureDetector); mLongPressGestureProcessor.RemoveGestureDetector(longPress); break; } - case Gesture::Pan: + case DevelGesture::Pan: { PanGestureDetector* pan = static_cast(gestureDetector); mPanGestureProcessor.RemoveGestureDetector(pan); break; } - case Gesture::Pinch: + case DevelGesture::Pinch: { PinchGestureDetector* pinch = static_cast(gestureDetector); mPinchGestureProcessor.RemoveGestureDetector(pinch); break; } - case Gesture::Tap: + case DevelGesture::Tap: { TapGestureDetector* tap = static_cast(gestureDetector); mTapGestureProcessor.RemoveGestureDetector(tap); break; } + + case DevelGesture::Rotation: + { + RotationGestureDetector* rotation = static_cast(gestureDetector); + mRotationGestureProcessor.RemoveGestureDetector(rotation); + break; + } } } @@ -131,33 +147,39 @@ void GestureEventProcessor::GestureDetectorUpdated(GestureDetector* gestureDetec { switch (gestureDetector->GetType()) { - case Gesture::LongPress: + case DevelGesture::LongPress: { LongPressGestureDetector* longPress = static_cast(gestureDetector); mLongPressGestureProcessor.GestureDetectorUpdated(longPress); break; } - case Gesture::Pan: + case DevelGesture::Pan: { PanGestureDetector* pan = static_cast(gestureDetector); mPanGestureProcessor.GestureDetectorUpdated(pan); break; } - case Gesture::Pinch: + case DevelGesture::Pinch: { PinchGestureDetector* pinch = static_cast(gestureDetector); mPinchGestureProcessor.GestureDetectorUpdated(pinch); break; } - case Gesture::Tap: + case DevelGesture::Tap: { TapGestureDetector* tap = static_cast(gestureDetector); mTapGestureProcessor.GestureDetectorUpdated(tap); break; } + + case DevelGesture::Rotation: + { + // Nothing to do + break; + } } } @@ -165,18 +187,19 @@ void GestureEventProcessor::SetGestureProperties( const Gesture& gesture ) { bool requestUpdate = false; - switch ( gesture.type ) + switch ( static_cast< DevelGesture::Type >( gesture.type ) ) { - case Gesture::Pan: + case DevelGesture::Pan: { const PanGesture& pan = static_cast< const PanGesture& >( gesture ); requestUpdate = mPanGestureProcessor.SetPanGestureProperties( pan ); break; } - case Gesture::LongPress: - case Gesture::Pinch: - case Gesture::Tap: + case DevelGesture::LongPress: + case DevelGesture::Pinch: + case DevelGesture::Tap: + case DevelGesture::Rotation: { DALI_ASSERT_DEBUG( false && "Gesture type does not have scene object\n" ); break; @@ -198,6 +221,7 @@ bool GestureEventProcessor::NeedsUpdate() updateRequired |= mPanGestureProcessor.NeedsUpdate(); updateRequired |= mPinchGestureProcessor.NeedsUpdate(); updateRequired |= mTapGestureProcessor.NeedsUpdate(); + updateRequired |= mRotationGestureProcessor.NeedsUpdate(); return updateRequired; } diff --git a/dali/internal/event/events/gesture-event-processor.h b/dali/internal/event/events/gesture-event-processor.h index e848f44..a181588 100644 --- a/dali/internal/event/events/gesture-event-processor.h +++ b/dali/internal/event/events/gesture-event-processor.h @@ -24,6 +24,7 @@ #include #include #include +#include #include namespace Dali @@ -270,6 +271,7 @@ private: PanGestureProcessor mPanGestureProcessor; PinchGestureProcessor mPinchGestureProcessor; TapGestureProcessor mTapGestureProcessor; + RotationGestureProcessor mRotationGestureProcessor; Integration::RenderController& mRenderController; int32_t envOptionMinimumPanDistance; diff --git a/dali/internal/event/events/gesture-event.cpp b/dali/internal/event/events/gesture-event.cpp index 3466004..f42e514 100644 --- a/dali/internal/event/events/gesture-event.cpp +++ b/dali/internal/event/events/gesture-event.cpp @@ -28,13 +28,17 @@ GestureEvent::~GestureEvent() { } -GestureEvent::GestureEvent(Gesture::Type gesture, Gesture::State gestureState) -: gestureType(gesture), - state(gestureState), - time(0) +GestureEvent::GestureEvent( DevelGesture::Type gesture, Gesture::State gestureState ) +: gestureType( gesture ), + state( gestureState ), + time( 0 ) { } +GestureEvent::GestureEvent(Gesture::Type gesture, Gesture::State gestureState) +: GestureEvent( static_cast< DevelGesture::Type >( gesture ), gestureState ) +{ +} } // namespace Internal } // namespace Dali diff --git a/dali/internal/event/events/gesture-event.h b/dali/internal/event/events/gesture-event.h index 8d3e0a3..cb4dd77 100644 --- a/dali/internal/event/events/gesture-event.h +++ b/dali/internal/event/events/gesture-event.h @@ -20,6 +20,7 @@ // INTERNAL INCLUDES #include +#include #include namespace Dali @@ -46,7 +47,7 @@ struct GestureEvent /** * Gesture Type. */ - Gesture::Type gestureType; + DevelGesture::Type gestureType; /** * The state of the gesture. @@ -65,7 +66,12 @@ protected: // Constructors only to be used by derived structures. * @param[in] gesture The type of gesture event. * @param[in] gestureState The state of the gesture event. */ - GestureEvent(Gesture::Type gesture, Gesture::State gestureState); + GestureEvent( DevelGesture::Type gesture, Gesture::State gestureState); + + /** + * @copydoc GestureEvent( DevelGesture::Type, Gesture::State ) + */ + GestureEvent( Gesture::Type gesture, Gesture::State gestureState ); }; } // namespace Internal diff --git a/dali/internal/event/events/gesture-processor.cpp b/dali/internal/event/events/gesture-processor.cpp index 773cadb..c294c54 100644 --- a/dali/internal/event/events/gesture-processor.cpp +++ b/dali/internal/event/events/gesture-processor.cpp @@ -41,11 +41,16 @@ namespace */ struct GestureHitTestCheck : public HitTestAlgorithm::HitTestInterface { - GestureHitTestCheck( Gesture::Type type ) + GestureHitTestCheck( DevelGesture::Type type ) : mType( type ) { } + GestureHitTestCheck( Gesture::Type type ) + : GestureHitTestCheck( static_cast< DevelGesture::Type >( type ) ) + { + } + virtual bool IsActorHittable( Actor* actor ) { return actor->IsGestureRequred( mType ) && // Does the Application or derived actor type require the gesture? @@ -63,13 +68,13 @@ struct GestureHitTestCheck : public HitTestAlgorithm::HitTestInterface return layer->IsTouchConsumed(); } - Gesture::Type mType; + DevelGesture::Type mType; }; } // unnamed namespace -GestureProcessor::GestureProcessor( Gesture::Type type ) +GestureProcessor::GestureProcessor( DevelGesture::Type type ) : mGestureRecognizer(), mNeedsUpdate( false ), mType( type ), @@ -78,6 +83,12 @@ GestureProcessor::GestureProcessor( Gesture::Type type ) { } +GestureProcessor::GestureProcessor( Gesture::Type type ) +: GestureProcessor( static_cast< DevelGesture::Type >( type ) ) +{ +} + + GestureProcessor::~GestureProcessor() { ResetActor(); diff --git a/dali/internal/event/events/gesture-processor.h b/dali/internal/event/events/gesture-processor.h index af39967..ed430df 100644 --- a/dali/internal/event/events/gesture-processor.h +++ b/dali/internal/event/events/gesture-processor.h @@ -19,6 +19,7 @@ */ // INTERNAL INCLUDES +#include #include #include #include @@ -64,6 +65,11 @@ protected: /** * Protected constructor. Cannot create an instance of GestureProcessor */ + GestureProcessor( DevelGesture::Type type ); + + /** + * Protected constructor. Cannot create an instance of GestureProcessor + */ GestureProcessor( Gesture::Type type ); /** @@ -190,7 +196,7 @@ protected: //Data private: // Data - Gesture::Type mType; ///< Type of GestureProcessor + DevelGesture::Type mType; ///< Type of GestureProcessor Actor* mCurrentGesturedActor; ///< The current actor that has been gestured. bool mGesturedActorDisconnected:1; ///< Indicates whether the gestured actor has been disconnected from the scene }; diff --git a/dali/internal/event/events/gesture-recognizer.h b/dali/internal/event/events/gesture-recognizer.h index 6efb254..3c99c3e 100644 --- a/dali/internal/event/events/gesture-recognizer.h +++ b/dali/internal/event/events/gesture-recognizer.h @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace Dali @@ -56,6 +57,7 @@ public: class GestureRecognizer : public RefObject { public: + /** * Called when it gets a touch event. The gesture recognizer should * evaluate this event along with previously received events to determine @@ -74,25 +76,49 @@ public: * Returns the type of gesture detector. * @return Type of gesture detector. */ - Gesture::Type GetType() const { return mType; } + DevelGesture::Type GetType() const { return mType; } - void SendEvent(Scene& scene, const Integration::TouchEvent& event) + /** + * Called when we get a touch event. + * @param[in] scene The scene the touch event has occurred on + * @param[in] event The latest touch event + */ + void SendEvent( Scene& scene, const Integration::TouchEvent& event ) { mScene = &scene; - SendEvent(event); + SendEvent( event ); } protected: /** - * Protected Constructor. Should only be able to create derived class objects. + * Protected Constructor. Should only be able to create derived class objects. * @param[in] screenSize The size of the screen. * @param[in] detectorType The type of gesture detector. */ + GestureRecognizer( Vector2 screenSize, DevelGesture::Type detectorType ) + : mScreenSize( screenSize ), + mType( detectorType ), + mScene( nullptr ) + { + } + + /** + * copydoc GestureRecognizer( Vector2, DevelGesture::Type ) + */ GestureRecognizer( Vector2 screenSize, Gesture::Type detectorType ) - : mScreenSize(screenSize), - mType(detectorType), - mScene(nullptr) + : GestureRecognizer( screenSize, static_cast< DevelGesture::Type >( detectorType ) ) + { + } + + /** + * Protected Constructor. Should only be able to create derived class objects. + * + * Use this constructor with the screen size is not used in the dereived class. + * @param[in] detectorType The type of gesture detector. + */ + GestureRecognizer( DevelGesture::Type detectorType ) + : GestureRecognizer( Vector2::ZERO, detectorType ) { } @@ -103,7 +129,7 @@ protected: protected: Vector2 mScreenSize; - Gesture::Type mType; + DevelGesture::Type mType; Scene* mScene; }; diff --git a/dali/internal/event/events/gesture-requests.h b/dali/internal/event/events/gesture-requests.h index a40e553..06a41bc 100644 --- a/dali/internal/event/events/gesture-requests.h +++ b/dali/internal/event/events/gesture-requests.h @@ -20,6 +20,7 @@ // INTERNAL INCLUDES #include +#include namespace Dali { @@ -38,20 +39,27 @@ struct GestureRequest * Default Constructor * @param[in] typeRequired The gesture type required */ - GestureRequest(Gesture::Type typeRequired) : type(typeRequired) + GestureRequest( DevelGesture::Type typeRequired ) + : type( typeRequired ) { } /** - * Virtual destructor + * @copydoc GestureRequest( DevelGesture::Type ) */ - virtual ~GestureRequest() + GestureRequest( Gesture::Type typeRequired ) + : GestureRequest( static_cast< DevelGesture::Type >( typeRequired ) ) { } + /** + * Virtual destructor + */ + virtual ~GestureRequest() = default; + // Data Members - Gesture::Type type; ///< The type of gesture required. + DevelGesture::Type type; ///< The type of gesture required. }; /** @@ -85,29 +93,6 @@ struct PanGestureRequest : public GestureRequest }; /** - * This is used by Core when a pinch gesture is required. - */ -struct PinchGestureRequest : public GestureRequest -{ - // Creation & Destruction - - /** - * Default Constructor - */ - PinchGestureRequest() - : GestureRequest(Gesture::Pinch) - { - } - - /** - * Virtual destructor - */ - virtual ~PinchGestureRequest() - { - } -}; - -/** * This is used by Core when a tap gesture is required. */ struct TapGestureRequest : public GestureRequest diff --git a/dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.cpp b/dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.cpp new file mode 100644 index 0000000..6b75613 --- /dev/null +++ b/dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include // for strcmp + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace +{ + +// Signals + +const char* const SIGNAL_ROTATION_DETECTED = "rotationDetected"; + +BaseHandle Create() +{ + return Dali::RotationGestureDetector::New(); +} + +TypeRegistration mType( typeid( Dali::RotationGestureDetector ), typeid( Dali::GestureDetector ), Create ); + +SignalConnectorType signalConnector1( mType, SIGNAL_ROTATION_DETECTED, &RotationGestureDetector::DoConnectSignal ); + +} + + +RotationGestureDetectorPtr RotationGestureDetector::New() +{ + return new RotationGestureDetector; +} + +RotationGestureDetector::RotationGestureDetector() +: GestureDetector( DevelGesture::Rotation ) +{ +} + +void RotationGestureDetector::EmitRotationGestureSignal( Dali::Actor actor, const RotationGesture& rotation ) +{ + // Guard against destruction during signal emission + Dali::RotationGestureDetector handle( this ); + + mDetectedSignal.Emit( actor, rotation ); +} + +bool RotationGestureDetector::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) +{ + bool connected( true ); + RotationGestureDetector* gesture = static_cast< RotationGestureDetector* >( object ); // TypeRegistry guarantees that this is the correct type. + + if ( 0 == strcmp( signalName.c_str(), SIGNAL_ROTATION_DETECTED ) ) + { + gesture->DetectedSignal().Connect( tracker, functor ); + } + else + { + // signalName does not match any signal + connected = false; + } + + return connected; +} + +} // namespace Internal + +} // namespace Dali diff --git a/dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.h b/dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.h new file mode 100644 index 0000000..0b8f17b --- /dev/null +++ b/dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.h @@ -0,0 +1,145 @@ +#ifndef DALI_INTERNAL_ROTATION_GESTURE_DETECTOR_H +#define DALI_INTERNAL_ROTATION_GESTURE_DETECTOR_H + +/* + * Copyright (c) 2019 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. + * + */ + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +class RotationGesture; + +namespace Internal +{ + +class RotationGestureDetector; + +typedef IntrusivePtr RotationGestureDetectorPtr; +typedef DerivedGestureDetectorContainer::type RotationGestureDetectorContainer; + +/** + * @copydoc Dali::RotationGestureDetector + */ +class RotationGestureDetector : public GestureDetector +{ +public: // Creation + + /** + * Create a new gesture detector. + * @return A smart-pointer to the newly allocated detector. + */ + static RotationGestureDetectorPtr New(); + + /** + * Construct a new GestureDetector. + */ + RotationGestureDetector(); + + // Not copyable + + RotationGestureDetector( const RotationGestureDetector& ) = delete; ///< Deleted copy constructor + RotationGestureDetector& operator=(const RotationGestureDetector& rhs) = delete; ///< Deleted copy assignment operator + +public: + + /** + * Called by the RotationGestureProcessor when a rotation gesture event occurs within the bounds of our + * attached actor. + * @param[in] actor The rotated actor + * @param[in] rotation The rotation gesture + */ + void EmitRotationGestureSignal( Dali::Actor actor, const RotationGesture& rotation ); + +public: // Signals + + /** + * @copydoc Dali::RotationGestureDetector::DetectedSignal() + */ + Dali::RotationGestureDetector::DetectedSignalType& DetectedSignal() + { + return mDetectedSignal; + } + + /** + * Connects a callback function with the object's signals. + * @param[in] object The object providing the signal. + * @param[in] tracker Used to disconnect the signal. + * @param[in] signalName The signal to connect to. + * @param[in] functor A newly allocated FunctorDelegate. + * @return True if the signal was connected. + * @post If a signal was connected, ownership of functor was passed to CallbackBase. Otherwise the caller is responsible for deleting the unused functor. + */ + static bool DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ); + +protected: + + /** + * A reference counted object may only be deleted by calling Unreference() + */ + virtual ~RotationGestureDetector() = default; + +private: // GestureDetector overrides + + /** + * @copydoc Dali::Internal::GestureDetector::OnActorAttach(Actor&) + */ + virtual void OnActorAttach( Actor& actor ) { /* Nothing to do */ } + + /** + * @copydoc Dali::Internal::GestureDetector::OnActorDetach(Actor&) + */ + virtual void OnActorDetach( Actor& actor ) { /* Nothing to do */ } + + /** + * @copydoc Dali::Internal::GestureDetector::OnActorDestroyed(Object&) + */ + virtual void OnActorDestroyed( Object& object ) { /* Nothing to do */ } + +private: + + Dali::RotationGestureDetector::DetectedSignalType mDetectedSignal; +}; + +} // namespace Internal + +// Helpers for public-api forwarding methods + +inline Internal::RotationGestureDetector& GetImplementation( Dali::RotationGestureDetector& detector ) +{ + DALI_ASSERT_ALWAYS( detector && "RotationGestureDetector handle is empty" ); + + BaseObject& handle = detector.GetBaseObject(); + + return static_cast( handle ); +} + +inline const Internal::RotationGestureDetector& GetImplementation( const Dali::RotationGestureDetector& detector ) +{ + DALI_ASSERT_ALWAYS( detector && "RotationGestureDetector handle is empty" ); + + const BaseObject& handle = detector.GetBaseObject(); + + return static_cast( handle ); +} + +} // namespace Dali + +#endif // DALI_INTERNAL_ROTATION_GESTURE_DETECTOR_H diff --git a/dali/internal/event/events/rotation-gesture/rotation-gesture-event.h b/dali/internal/event/events/rotation-gesture/rotation-gesture-event.h new file mode 100644 index 0000000..38b83cd --- /dev/null +++ b/dali/internal/event/events/rotation-gesture/rotation-gesture-event.h @@ -0,0 +1,77 @@ +#ifndef DALI_INTERNAL_EVENT_ROTATION_GESTURE_EVENT_H +#define DALI_INTERNAL_EVENT_ROTATION_GESTURE_EVENT_H + +/* + * Copyright (c) 2019 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. + * + */ + +// INTERNAL INCLUDES +#include +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +/** + * When a rotation gesture is detected, this structure holds information regarding the gesture. + * + * A Rotation Gesture event should be in one of four states: + * - Started: If a rotation is detected. + * - Continuing: If after a rotation is detected, it continues. + * - Finished: If after a rotation, the user lifts their finger(s). + * - Cancelled: If there is a system interruption. + */ +struct RotationGestureEvent : public GestureEvent +{ + // Construction & Destruction + + /** + * Default Constructor + * @param[in] state The state of the gesture + */ + RotationGestureEvent( Gesture::State state ) + : GestureEvent( DevelGesture::Rotation, state ) + { + } + + /** + * Virtual destructor + */ + virtual ~RotationGestureEvent() = default; + + // Data + + /** + * @copydoc Dali::RotationGesture::rotation + */ + Radian rotation; + + /** + * @copydoc Dali::RotationGesture::screenCenterPoint + */ + Vector2 centerPoint; +}; + +} // namespace Integration + +} // namespace Dali + +#endif // DALI_INTERNAL_EVENT_ROTATION_GESTURE_EVENT_H diff --git a/dali/internal/event/events/rotation-gesture/rotation-gesture-processor.cpp b/dali/internal/event/events/rotation-gesture/rotation-gesture-processor.cpp new file mode 100644 index 0000000..f65642e --- /dev/null +++ b/dali/internal/event/events/rotation-gesture/rotation-gesture-processor.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace +{ + +/** + * Creates a RotationGesture and asks the specified detector to emit its detected signal. + * @param[in] actor The actor that has been rotationed. + * @param[in] gestureDetectors The gesture detector container that should emit the signal. + * @param[in] rotationEvent The rotationEvent received from the adaptor. + * @param[in] localCenter Relative to the actor attached to the detector. + */ +void EmitRotationSignal( + Actor* actor, + const GestureDetectorContainer& gestureDetectors, + const RotationGestureEvent& rotationEvent, + Vector2 localCenter) +{ + RotationGesture rotation(rotationEvent.state); + rotation.time = rotationEvent.time; + rotation.rotation = rotationEvent.rotation; + rotation.screenCenterPoint = rotationEvent.centerPoint; + rotation.localCenterPoint = localCenter; + + Dali::Actor actorHandle( actor ); + const GestureDetectorContainer::const_iterator endIter = gestureDetectors.end(); + for ( GestureDetectorContainer::const_iterator iter = gestureDetectors.begin(); iter != endIter; ++iter ) + { + static_cast< RotationGestureDetector* >( *iter )->EmitRotationGestureSignal( actorHandle, rotation ); + } +} + +/** + * Functor which checks whether the specified actor is attached to the gesture detector. + * It returns true if it is no longer attached. This can be used in remove_if functions. + */ +struct IsNotAttachedFunctor +{ + /** + * Constructor + * @param[in] actor The actor to check whether it is attached. + */ + IsNotAttachedFunctor(Actor* actor) + : actorToCheck(actor) + { + } + + /** + * Returns true if not attached, false if it is still attached. + * @param[in] detector The detector to check. + * @return true, if not attached, false otherwise. + */ + bool operator()(const GestureDetector* detector) const + { + return !detector->IsAttached(*actorToCheck); + } + + Actor* actorToCheck; ///< The actor to check whether it is attached or not. +}; + +} // unnamed namespace + +RotationGestureProcessor::RotationGestureProcessor() +: GestureProcessor( DevelGesture::Rotation ), + mRotationGestureDetectors(), + mCurrentRotationEmitters(), + mCurrentRotationEvent( nullptr ) +{ +} + +void RotationGestureProcessor::Process( Scene& scene, const RotationGestureEvent& rotationEvent ) +{ + switch ( rotationEvent.state ) + { + case Gesture::Started: + { + // The rotation gesture should only be sent to the gesture detector which first received it so that + // it can be told when the gesture ends as well. + + mCurrentRotationEmitters.clear(); + ResetActor(); + + HitTestAlgorithm::Results hitTestResults; + if( HitTest( scene, rotationEvent.centerPoint, hitTestResults ) ) + { + // Record the current render-task for Screen->Actor coordinate conversions + mCurrentRenderTask = hitTestResults.renderTask; + + // Set mCurrentRotationEvent to use inside overridden methods called from ProcessAndEmit() + mCurrentRotationEvent = &rotationEvent; + ProcessAndEmit( hitTestResults ); + mCurrentRotationEvent = NULL; + } + break; + } + + case Gesture::Continuing: + case Gesture::Finished: + case Gesture::Cancelled: + { + // Only send subsequent rotation gesture signals if we processed the rotation gesture when it started. + // Check if actor is still touchable. + + Actor* currentGesturedActor = GetCurrentGesturedActor(); + if ( currentGesturedActor ) + { + if ( currentGesturedActor->IsHittable() && !mCurrentRotationEmitters.empty() && mCurrentRenderTask ) + { + // Ensure actor is still attached to the emitters, if it is not then remove the emitter. + GestureDetectorContainer::iterator endIter = std::remove_if( mCurrentRotationEmitters.begin(), mCurrentRotationEmitters.end(), IsNotAttachedFunctor(currentGesturedActor) ); + mCurrentRotationEmitters.erase( endIter, mCurrentRotationEmitters.end() ); + + if ( !mCurrentRotationEmitters.empty() ) + { + Vector2 actorCoords; + RenderTask& renderTaskImpl( *mCurrentRenderTask.Get() ); + currentGesturedActor->ScreenToLocal( renderTaskImpl, actorCoords.x, actorCoords.y, rotationEvent.centerPoint.x, rotationEvent.centerPoint.y ); + + EmitRotationSignal( currentGesturedActor, mCurrentRotationEmitters, rotationEvent, actorCoords ); + } + else + { + // If we have no current emitters then clear rotated actor as well. + ResetActor(); + } + + // Clear current emitters if rotation gesture has ended or been cancelled. + if ( rotationEvent.state == Gesture::Finished || rotationEvent.state == Gesture::Cancelled ) + { + mCurrentRotationEmitters.clear(); + ResetActor(); + } + } + else + { + mCurrentRotationEmitters.clear(); + ResetActor(); + } + } + break; + } + + case Gesture::Clear: + case Gesture::Possible: + { + // Nothing to do + break; + } + } +} + +void RotationGestureProcessor::AddGestureDetector( RotationGestureDetector* gestureDetector, Scene& /* scene */ ) +{ + bool createRecognizer(mRotationGestureDetectors.empty()); + + mRotationGestureDetectors.push_back(gestureDetector); + + if (createRecognizer) + { + mGestureRecognizer = new RotationGestureRecognizer( *this ); + } +} + +void RotationGestureProcessor::RemoveGestureDetector( RotationGestureDetector* gestureDetector ) +{ + if ( !mCurrentRotationEmitters.empty() ) + { + // Check if the removed detector was one that is currently being rotated and remove it from emitters. + GestureDetectorContainer::iterator endIter = std::remove( mCurrentRotationEmitters.begin(), mCurrentRotationEmitters.end(), gestureDetector ); + mCurrentRotationEmitters.erase( endIter, mCurrentRotationEmitters.end() ); + + // If we no longer have any emitters, then we should clear mCurrentGesturedActor as well + if ( mCurrentRotationEmitters.empty() ) + { + ResetActor(); + } + } + + // Find the detector... + RotationGestureDetectorContainer::iterator endIter = std::remove( mRotationGestureDetectors.begin(), mRotationGestureDetectors.end(), gestureDetector ); + DALI_ASSERT_DEBUG( endIter != mRotationGestureDetectors.end() ); + + // ...and remove it + mRotationGestureDetectors.erase(endIter, mRotationGestureDetectors.end()); + + if (mRotationGestureDetectors.empty()) + { + mGestureRecognizer.Detach(); + } +} + +void RotationGestureProcessor::OnGesturedActorStageDisconnection() +{ + mCurrentRotationEmitters.clear(); +} + +bool RotationGestureProcessor::CheckGestureDetector( GestureDetector* detector, Actor* actor ) +{ + // No special case required for rotation. + return true; +} + +void RotationGestureProcessor::EmitGestureSignal( Actor* actor, const GestureDetectorContainer& gestureDetectors, Vector2 actorCoordinates ) +{ + DALI_ASSERT_DEBUG( mCurrentRotationEvent ); + + EmitRotationSignal( actor, gestureDetectors, *mCurrentRotationEvent, actorCoordinates ); + + if ( actor->OnStage() ) + { + mCurrentRotationEmitters = gestureDetectors; + SetActor( actor ); + } +} + +} // namespace Internal + +} // namespace Dali diff --git a/dali/internal/event/events/rotation-gesture/rotation-gesture-processor.h b/dali/internal/event/events/rotation-gesture/rotation-gesture-processor.h new file mode 100644 index 0000000..72484c0 --- /dev/null +++ b/dali/internal/event/events/rotation-gesture/rotation-gesture-processor.h @@ -0,0 +1,122 @@ +#ifndef DALI_INTERNAL_ROTATION_GESTURE_EVENT_PROCESSOR_H +#define DALI_INTERNAL_ROTATION_GESTURE_EVENT_PROCESSOR_H + +/* + * Copyright (c) 2019 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. + * + */ + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +class Scene; +class Stage; + +struct RotationGestureEvent; + +/** + * Rotation Gesture Event Processing: + * + * When we receive a rotation gesture event, we do the following: + * - Find the hit actor that requires a rotation underneath the center-point of the rotation. + * - Emit the gesture if the event satisfies the detector conditions. + * + * The above is only checked when our gesture starts. We continue sending the rotation gesture to this + * detector until the rotation ends or is cancelled. + */ +class RotationGestureProcessor : public GestureProcessor, public RecognizerObserver +{ +public: + + /** + * Create a rotation gesture processor. + */ + RotationGestureProcessor(); + + /** + * Non-virtual destructor; RotationGestureProcessor is not a base class + */ + ~RotationGestureProcessor() = default; + + RotationGestureProcessor( const RotationGestureProcessor& ) = delete; ///< Deleted copy constructor. + RotationGestureProcessor& operator=( const RotationGestureProcessor& rhs ) = delete; ///< Deleted copy assignment operator. + +public: // To be called by GestureEventProcessor + + /** + * This method is called whenever a rotation gesture event occurs. + * @param[in] scene The scene the rotation gesture event occurs in. + * @param[in] rotationEvent The event that has occurred. + */ + void Process( Scene& scene, const RotationGestureEvent& rotationEvent ); + + /** + * Adds a gesture detector to this gesture processor. + * If this is the first gesture detector being added, then this method registers the required + * gesture with the adaptor. + * @param[in] gestureDetector The gesture detector being added + * @param[in] scene The scene the rotation gesture occurred in + */ + void AddGestureDetector( RotationGestureDetector* gestureDetector, Scene& scene ); + + /** + * Removes the specified gesture detector from this gesture processor. If, after removing this + * gesture detector, there are no more gesture detectors registered, then this method unregisters + * the gesture from the adaptor. + * @param[in] gestureDetector The gesture detector being removed. + */ + void RemoveGestureDetector( RotationGestureDetector* gestureDetector ); + +private: + + // GestureProcessor overrides + + /** + * @copydoc GestureProcessor::OnGesturedActorStageDisconnection() + */ + void OnGesturedActorStageDisconnection(); + + /** + * @copydoc GestureProcessor::CheckGestureDetector() + */ + bool CheckGestureDetector( GestureDetector* detector, Actor* actor ); + + /** + * @copydoc GestureProcessor::EmitGestureSignal() + */ + void EmitGestureSignal( Actor* actor, const GestureDetectorContainer& gestureDetectors, Vector2 actorCoordinates ); + +private: + + RotationGestureDetectorContainer mRotationGestureDetectors; + GestureDetectorContainer mCurrentRotationEmitters; + RenderTaskPtr mCurrentRenderTask; + + const RotationGestureEvent* mCurrentRotationEvent; ///< Pointer to current RotationEvent, used when calling ProcessAndEmit() +}; + +} // namespace Internal + +} // namespace Dali + +#endif // DALI_INTERNAL_ROTATION_GESTURE_EVENT_PROCESSOR_H diff --git a/dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.cpp b/dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.cpp new file mode 100644 index 0000000..308fc46 --- /dev/null +++ b/dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include + +// INTERNAL INCLUDES + + +namespace Dali +{ + +namespace Internal +{ + +namespace +{ +const unsigned int MINIMUM_TOUCH_EVENTS_REQUIRED = 4; +const unsigned int MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START = 4; + +inline float GetAngle( const Integration::Point& point1, const Integration::Point& point2 ) +{ + const Vector2& point1ScreenPosition = point1.GetScreenPosition(); + const Vector2& point2ScreenPosition = point2.GetScreenPosition(); + return atan2( point2ScreenPosition.y - point1ScreenPosition.y, point2ScreenPosition.x - point1ScreenPosition.x ); +} + +inline Vector2 GetCenterPoint( const Integration::Point& point1, const Integration::Point& point2 ) +{ + return Vector2( point1.GetScreenPosition() + point2.GetScreenPosition() ) * 0.5f; +} + +} // unnamed namespace + +RotationGestureRecognizer::RotationGestureRecognizer( Observer& observer ) +: GestureRecognizer( DevelGesture::Rotation ), + mObserver( observer ), + mState( Clear ), + mTouchEvents(), + mStartingAngle( 0.0f ) +{ +} + +void RotationGestureRecognizer::SendEvent( const Integration::TouchEvent& event ) +{ + int pointCount = event.GetPointCount(); + + switch( mState ) + { + case Clear: + { + if( pointCount == 2 ) + { + // Change state to possible as we have two touch points. + mState = Possible; + mTouchEvents.push_back( event ); + } + break; + } + + case Possible: + { + if ( pointCount != 2 ) + { + // We no longer have two touch points so change state back to Clear. + mState = Clear; + mTouchEvents.clear(); + } + else + { + const Integration::Point& currentPoint1 = event.points[0]; + const Integration::Point& currentPoint2 = event.points[1]; + + if( currentPoint1.GetState() == PointState::UP || currentPoint2.GetState() == PointState::UP ) + { + // One of our touch points has an Up event so change our state back to Clear. + mState = Clear; + mTouchEvents.clear(); + } + else + { + mTouchEvents.push_back( event ); + + // We can only determine a rotation after a certain number of touch points have been collected. + if( mTouchEvents.size() >= MINIMUM_TOUCH_EVENTS_REQUIRED ) + { + // Remove the first few events from the vector otherwise values are exaggerated + mTouchEvents.erase( mTouchEvents.begin(), mTouchEvents.end() - MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START ); + + if( !mTouchEvents.empty() ) + { + mStartingAngle = GetAngle( mTouchEvents.begin()->points[0], mTouchEvents.begin()->points[1] ); + + // Send rotation started + SendRotation( Gesture::Started, event ); + + mState = Started; + } + + mTouchEvents.clear(); + + if( mState == Possible ) + { + // No rotation, so restart detection + mState = Clear; + mTouchEvents.clear(); + } + } + } + } + break; + } + + case Started: + { + if( pointCount != 2 ) + { + // Send rotation finished event + SendRotation( Gesture::Finished, event ); + + mState = Clear; + mTouchEvents.clear(); + } + else + { + const Integration::Point& currentPoint1 = event.points[0]; + const Integration::Point& currentPoint2 = event.points[1]; + + if( ( currentPoint1.GetState() == PointState::UP ) || + ( currentPoint2.GetState() == PointState::UP ) ) + { + mTouchEvents.push_back( event ); + // Send rotation finished event + SendRotation( Gesture::Finished, event ); + + mState = Clear; + mTouchEvents.clear(); + } + else + { + mTouchEvents.push_back( event ); + + if( mTouchEvents.size() >= MINIMUM_TOUCH_EVENTS_REQUIRED_AFTER_START ) + { + // Send rotation continuing + SendRotation( Gesture::Continuing, event ); + + mTouchEvents.clear(); + } + } + } + break; + } + } +} + +void RotationGestureRecognizer::SendRotation( Gesture::State state, const Integration::TouchEvent& currentEvent ) +{ + RotationGestureEvent gesture( state ); + + if( !mTouchEvents.empty() ) + { + // Assert if we have been holding TouchEvents that do not have 2 points + DALI_ASSERT_DEBUG( mTouchEvents[0].GetPointCount() == 2 ); + + // We should use the current event in our calculations unless it does not have two points. + // If it does not have two points, then we should use the last point in mTouchEvents. + Integration::TouchEvent event( currentEvent ); + if( event.GetPointCount() != 2 ) + { + event = *mTouchEvents.rbegin(); + } + + const Integration::Point& currentPoint1( event.points[0] ); + const Integration::Point& currentPoint2( event.points[1] ); + + gesture.rotation = GetAngle( currentPoint1, currentPoint2 ) - mStartingAngle; + gesture.centerPoint = GetCenterPoint( currentPoint1, currentPoint2 ); + } + else + { + // Something has gone wrong, just cancel the gesture. + gesture.state = Gesture::Cancelled; + } + + gesture.time = currentEvent.time; + + if( mScene ) + { + // Create another handle so the recognizer cannot be destroyed during process function + GestureRecognizerPtr recognizerHandle = this; + + mObserver.Process( *mScene, gesture ); + } +} + +} // namespace Internal + +} // namespace Dali diff --git a/dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.h b/dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.h new file mode 100644 index 0000000..2413356 --- /dev/null +++ b/dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.h @@ -0,0 +1,105 @@ +#ifndef DALI_INTERNAL_EVENT_ROTATION_GESTURE_RECOGNIZER_H +#define DALI_INTERNAL_EVENT_ROTATION_GESTURE_RECOGNIZER_H + +/* + * Copyright (c) 2019 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. + * + */ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Integration +{ +struct TouchEvent; +} + +namespace Internal +{ + +/** + * When given a set of touch events, this detector attempts to determine if a rotation gesture has taken place. + */ +class RotationGestureRecognizer : public GestureRecognizer +{ +public: + + using Observer = RecognizerObserver< RotationGestureEvent >; + + /** + * Constructor + * @param[in] observer The observer to send gesture too when it's detected + */ + RotationGestureRecognizer( Observer& observer ); + + /** + * Virtual destructor. + */ + virtual ~RotationGestureRecognizer() = default; + +public: + + /** + * @copydoc Dali::Internal::GestureDetector::SendEvent(const Integration::TouchEvent&) + */ + virtual void SendEvent( const Integration::TouchEvent& event ); + + /** + * @copydoc Dali::Internal::GestureDetector::Update(const Integration::GestureRequest&) + */ + virtual void Update( const GestureRequest& request ) { /* Nothing to do */ } + +private: + + /** + * Emits the rotation gesture event to the core. + * @param[in] state The state of the rotation (whether it's starting, continuing or finished). + * @param[in] currentEvent The latest touch event. + */ + void SendRotation( Gesture::State state, const Integration::TouchEvent& currentEvent ); + +private: + + // Reference to the gesture processor for this recognizer + Observer& mObserver; + + /** + * Internal state machine. + */ + enum State + { + Clear, ///< No gesture detected. + Possible, ///< The current touch event data suggests that a gesture is possible. + Started, ///< A gesture has been detected. + }; + + State mState; ///< The current state of the detector. + std::vector< Integration::TouchEvent > mTouchEvents; ///< The touch events since initial touch down. + + float mStartingAngle; ///< The angle between the two touch points when the rotation is first detected. +}; + +} // namespace Internal + +} // namespace Dali + +#endif // DALI_INTERNAL_EVENT_ROTATION_GESTURE_RECOGNIZER_H diff --git a/dali/internal/file.list b/dali/internal/file.list index fcbb447..9bb1751 100644 --- a/dali/internal/file.list +++ b/dali/internal/file.list @@ -71,6 +71,9 @@ SET( internal_src_files ${internal_src_dir}/event/events/pinch-gesture/pinch-gesture-event.cpp ${internal_src_dir}/event/events/pinch-gesture/pinch-gesture-processor.cpp ${internal_src_dir}/event/events/pinch-gesture/pinch-gesture-recognizer.cpp + ${internal_src_dir}/event/events/rotation-gesture/rotation-gesture-detector-impl.cpp + ${internal_src_dir}/event/events/rotation-gesture/rotation-gesture-processor.cpp + ${internal_src_dir}/event/events/rotation-gesture/rotation-gesture-recognizer.cpp ${internal_src_dir}/event/events/tap-gesture/tap-gesture-detector-impl.cpp ${internal_src_dir}/event/events/tap-gesture/tap-gesture-event.cpp ${internal_src_dir}/event/events/tap-gesture/tap-gesture-processor.cpp -- 2.7.4