Added support for Rotation Gestures 93/215293/8
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 4 Oct 2019 18:57:44 +0000 (19:57 +0100)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 11 Oct 2019 10:38:08 +0000 (11:38 +0100)
Change-Id: Ib824b72119425f7329e9514295e83ade767d5117

34 files changed:
automated-tests/src/dali/CMakeLists.txt
automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.cpp
automated-tests/src/dali/dali-test-suite-utils/test-gesture-generator.h
automated-tests/src/dali/utc-Dali-RotationGesture.cpp [new file with mode: 0644]
automated-tests/src/dali/utc-Dali-RotationGestureDetector.cpp [new file with mode: 0644]
automated-tests/src/dali/utc-Dali-TypeRegistry.cpp
dali/devel-api/events/gesture-devel.h [new file with mode: 0644]
dali/devel-api/events/rotation-gesture-detector.cpp [new file with mode: 0644]
dali/devel-api/events/rotation-gesture-detector.h [new file with mode: 0644]
dali/devel-api/events/rotation-gesture.cpp [new file with mode: 0644]
dali/devel-api/events/rotation-gesture.h [new file with mode: 0644]
dali/devel-api/file.list
dali/internal/event/actors/actor-impl.cpp
dali/internal/event/actors/actor-impl.h
dali/internal/event/events/actor-gesture-data.cpp
dali/internal/event/events/actor-gesture-data.h
dali/internal/event/events/gesture-detector-impl.cpp
dali/internal/event/events/gesture-detector-impl.h
dali/internal/event/events/gesture-event-processor.cpp
dali/internal/event/events/gesture-event-processor.h
dali/internal/event/events/gesture-event.cpp
dali/internal/event/events/gesture-event.h
dali/internal/event/events/gesture-processor.cpp
dali/internal/event/events/gesture-processor.h
dali/internal/event/events/gesture-recognizer.h
dali/internal/event/events/gesture-requests.h
dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.cpp [new file with mode: 0644]
dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.h [new file with mode: 0644]
dali/internal/event/events/rotation-gesture/rotation-gesture-event.h [new file with mode: 0644]
dali/internal/event/events/rotation-gesture/rotation-gesture-processor.cpp [new file with mode: 0644]
dali/internal/event/events/rotation-gesture/rotation-gesture-processor.h [new file with mode: 0644]
dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.cpp [new file with mode: 0644]
dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.h [new file with mode: 0644]
dali/internal/file.list

index 17c4767..c501853 100644 (file)
@@ -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
index a0ac206..c0b55ce 100644 (file)
@@ -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
index 80611a0..18051cf 100644 (file)
@@ -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 (file)
index 0000000..4287ebf
--- /dev/null
@@ -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 <iostream>
+
+#include <stdlib.h>
+#include <dali/public-api/dali-core.h>
+#include <dali/devel-api/events/gesture-devel.h>
+#include <dali/devel-api/events/rotation-gesture.h>
+#include <dali-test-suite-utils.h>
+
+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 (file)
index 0000000..8d27655
--- /dev/null
@@ -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 <iostream>
+
+#include <stdlib.h>
+#include <dali/public-api/dali-core.h>
+#include <dali/integration-api/events/touch-event-integ.h>
+#include <dali/integration-api/render-task-list-integ.h>
+#include <dali/devel-api/events/gesture-devel.h>
+#include <dali/devel-api/events/rotation-gesture.h>
+#include <dali/devel-api/events/rotation-gesture-detector.h>
+#include <dali-test-suite-utils.h>
+#include <test-touch-utils.h>
+
+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;
+}
+
index fe65d5e..fd6d4cb 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali/internal/event/common/type-info-impl.h>
 #include <dali/integration-api/events/touch-event-integ.h>
 #include <dali/integration-api/events/hover-event-integ.h>
+#include <dali/devel-api/events/rotation-gesture-detector.h>
 
 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 (file)
index 0000000..45ca3df
--- /dev/null
@@ -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 <dali/public-api/events/gesture.h>
+
+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 (file)
index 0000000..16f007e
--- /dev/null
@@ -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 <dali/devel-api/events/rotation-gesture-detector.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.h>
+
+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<Dali::Internal::RotationGestureDetector*>( 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 (file)
index 0000000..837110a
--- /dev/null
@@ -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 <dali/public-api/events/gesture-detector.h>
+#include <dali/public-api/signals/dali-signal.h>
+
+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 (file)
index 0000000..05b1258
--- /dev/null
@@ -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 <dali/devel-api/events/rotation-gesture.h>
+
+// INTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+#include <dali/devel-api/events/gesture-devel.h>
+
+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 (file)
index 0000000..5f3e2f8
--- /dev/null
@@ -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 <dali/public-api/events/gesture.h>
+#include <dali/public-api/math/radian.h>
+#include <dali/public-api/math/vector2.h>
+
+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
index 2ed30a5..8efb7a9 100644 (file)
@@ -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
index 876182f..44a9e89 100644 (file)
@@ -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 );
 }
index 53fe06a..92e6ab8 100755 (executable)
@@ -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 <dali/public-api/math/viewport.h>
 #include <dali/public-api/object/ref-object.h>
 #include <dali/public-api/size-negotiation/relayout-container.h>
+#include <dali/devel-api/events/gesture-devel.h>
 #include <dali/internal/common/memory-pool-object-allocator.h>
 #include <dali/internal/event/actors/actor-declarations.h>
 #include <dali/internal/event/common/object-impl.h>
@@ -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
 
index c331c56..a6efbf0 100644 (file)
@@ -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" );
index 47eb9ed..8412e54 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/events/gesture-devel.h>
 #include <dali/internal/event/events/gesture-detector-impl.h>
 
 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
index c541a7a..7748d36 100644 (file)
@@ -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() )
index 54976d6..7cb0ffe 100644 (file)
@@ -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.
index efe0ace..fbfad1b 100644 (file)
@@ -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<LongPressGestureDetector*>(gestureDetector);
       mLongPressGestureProcessor.AddGestureDetector(longPress, scene);
       break;
     }
 
-    case Gesture::Pan:
+    case DevelGesture::Pan:
     {
       PanGestureDetector* pan = static_cast<PanGestureDetector*>(gestureDetector);
       mPanGestureProcessor.AddGestureDetector(pan, scene, envOptionMinimumPanDistance, envOptionMinimumPanEvents);
       break;
     }
 
-    case Gesture::Pinch:
+    case DevelGesture::Pinch:
     {
       PinchGestureDetector* pinch = static_cast<PinchGestureDetector*>(gestureDetector);
       mPinchGestureProcessor.AddGestureDetector(pinch, scene);
       break;
     }
 
-    case Gesture::Tap:
+    case DevelGesture::Tap:
     {
       TapGestureDetector* tap = static_cast<TapGestureDetector*>(gestureDetector);
       mTapGestureProcessor.AddGestureDetector(tap, scene);
       break;
     }
+
+    case DevelGesture::Rotation:
+    {
+      RotationGestureDetector* rotation = static_cast<RotationGestureDetector*>(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<LongPressGestureDetector*>(gestureDetector);
       mLongPressGestureProcessor.RemoveGestureDetector(longPress);
       break;
     }
 
-    case Gesture::Pan:
+    case DevelGesture::Pan:
     {
       PanGestureDetector* pan = static_cast<PanGestureDetector*>(gestureDetector);
       mPanGestureProcessor.RemoveGestureDetector(pan);
       break;
     }
 
-    case Gesture::Pinch:
+    case DevelGesture::Pinch:
     {
       PinchGestureDetector* pinch = static_cast<PinchGestureDetector*>(gestureDetector);
       mPinchGestureProcessor.RemoveGestureDetector(pinch);
       break;
     }
 
-    case Gesture::Tap:
+    case DevelGesture::Tap:
     {
       TapGestureDetector* tap = static_cast<TapGestureDetector*>(gestureDetector);
       mTapGestureProcessor.RemoveGestureDetector(tap);
       break;
     }
+
+    case DevelGesture::Rotation:
+    {
+      RotationGestureDetector* rotation = static_cast<RotationGestureDetector*>(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<LongPressGestureDetector*>(gestureDetector);
       mLongPressGestureProcessor.GestureDetectorUpdated(longPress);
       break;
     }
 
-    case Gesture::Pan:
+    case DevelGesture::Pan:
     {
       PanGestureDetector* pan = static_cast<PanGestureDetector*>(gestureDetector);
       mPanGestureProcessor.GestureDetectorUpdated(pan);
       break;
     }
 
-    case Gesture::Pinch:
+    case DevelGesture::Pinch:
     {
       PinchGestureDetector* pinch = static_cast<PinchGestureDetector*>(gestureDetector);
       mPinchGestureProcessor.GestureDetectorUpdated(pinch);
       break;
     }
 
-    case Gesture::Tap:
+    case DevelGesture::Tap:
     {
       TapGestureDetector* tap = static_cast<TapGestureDetector*>(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;
 }
index e848f44..a181588 100644 (file)
@@ -24,6 +24,7 @@
 #include <dali/internal/event/events/long-press-gesture/long-press-gesture-processor.h>
 #include <dali/internal/event/events/pan-gesture/pan-gesture-processor.h>
 #include <dali/internal/event/events/pinch-gesture/pinch-gesture-processor.h>
+#include <dali/internal/event/events/rotation-gesture/rotation-gesture-processor.h>
 #include <dali/internal/event/events/tap-gesture/tap-gesture-processor.h>
 
 namespace Dali
@@ -270,6 +271,7 @@ private:
   PanGestureProcessor mPanGestureProcessor;
   PinchGestureProcessor mPinchGestureProcessor;
   TapGestureProcessor mTapGestureProcessor;
+  RotationGestureProcessor mRotationGestureProcessor;
   Integration::RenderController& mRenderController;
 
   int32_t envOptionMinimumPanDistance;
index 3466004..f42e514 100644 (file)
@@ -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
index 8d3e0a3..cb4dd77 100644 (file)
@@ -20,6 +20,7 @@
 
 // INTERNAL INCLUDES
 #include <dali/public-api/events/gesture.h>
+#include <dali/devel-api/events/gesture-devel.h>
 #include <dali/integration-api/events/event.h>
 
 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
index 773cadb..c294c54 100644 (file)
@@ -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();
index af39967..ed430df 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/events/gesture-devel.h>
 #include <dali/internal/event/events/gesture-detector-impl.h>
 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
 #include <dali/internal/event/events/gesture-recognizer.h>
@@ -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
 };
index 6efb254..3c99c3e 100644 (file)
@@ -23,6 +23,7 @@
 #include <dali/public-api/events/gesture.h>
 #include <dali/public-api/math/vector2.h>
 #include <dali/public-api/object/ref-object.h>
+#include <dali/devel-api/events/gesture-devel.h>
 #include <dali/internal/event/events/gesture-event.h>
 
 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;
 };
 
index a40e553..06a41bc 100644 (file)
@@ -20,6 +20,7 @@
 
 // INTERNAL INCLUDES
 #include <dali/public-api/events/gesture.h>
+#include <dali/devel-api/events/gesture-devel.h>
 
 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 (file)
index 0000000..6b75613
--- /dev/null
@@ -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 <dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.h>
+
+// EXTERNAL INCLUDES
+#include <cstring> // for strcmp
+
+// INTERNAL INCLUDES
+#include <dali/public-api/object/type-registry.h>
+#include <dali/devel-api/events/gesture-devel.h>
+#include <dali/internal/event/events/gesture-event-processor.h>
+#include <dali/integration-api/debug.h>
+#include <dali/devel-api/events/rotation-gesture.h>
+
+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 (file)
index 0000000..0b8f17b
--- /dev/null
@@ -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 <dali/internal/event/events/gesture-detector-impl.h>
+#include <dali/devel-api/events/rotation-gesture-detector.h>
+
+namespace Dali
+{
+
+class RotationGesture;
+
+namespace Internal
+{
+
+class RotationGestureDetector;
+
+typedef IntrusivePtr<RotationGestureDetector> RotationGestureDetectorPtr;
+typedef DerivedGestureDetectorContainer<RotationGestureDetector>::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<Internal::RotationGestureDetector&>( 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<const Internal::RotationGestureDetector&>( 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 (file)
index 0000000..38b83cd
--- /dev/null
@@ -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 <dali/public-api/math/radian.h>
+#include <dali/public-api/math/vector2.h>
+#include <dali/devel-api/events/gesture-devel.h>
+#include <dali/internal/event/events/gesture-event.h>
+
+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 (file)
index 0000000..f65642e
--- /dev/null
@@ -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 <dali/internal/event/events/rotation-gesture/rotation-gesture-processor.h>
+
+// EXTERNAL INCLUDES
+#include <algorithm>
+
+// INTERNAL INCLUDES
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/math/vector2.h>
+#include <dali/devel-api/events/gesture-devel.h>
+#include <dali/internal/event/events/rotation-gesture/rotation-gesture-event.h>
+#include <dali/integration-api/debug.h>
+#include <dali/internal/event/common/scene-impl.h>
+#include <dali/internal/event/render-tasks/render-task-impl.h>
+#include <dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.h>
+#include <dali/internal/event/events/gesture-requests.h>
+#include <dali/devel-api/events/rotation-gesture.h>
+
+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 (file)
index 0000000..72484c0
--- /dev/null
@@ -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 <dali/internal/event/events/rotation-gesture/rotation-gesture-detector-impl.h>
+#include <dali/internal/event/events/gesture-processor.h>
+#include <dali/internal/event/render-tasks/render-task-impl.h>
+
+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<RotationGestureEvent>
+{
+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 (file)
index 0000000..308fc46
--- /dev/null
@@ -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 <dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.h>
+
+// EXTERNAL INCLUDES
+#include <cmath>
+
+// INTERNAL INCLUDES
+#include <dali/public-api/events/touch-point.h>
+#include <dali/public-api/math/vector2.h>
+#include <dali/devel-api/events/gesture-devel.h>
+#include <dali/integration-api/events/touch-event-integ.h>
+#include <dali/internal/event/events/rotation-gesture/rotation-gesture-event.h>
+#include <dali/internal/event/common/scene-impl.h>
+
+// 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 (file)
index 0000000..2413356
--- /dev/null
@@ -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 <dali/public-api/common/vector-wrapper.h>
+
+// INTERNAL INCLUDES
+#include <dali/internal/event/events/gesture-recognizer.h>
+#include <dali/internal/event/events/rotation-gesture/rotation-gesture-event.h>
+
+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
index fcbb447..9bb1751 100644 (file)
@@ -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