Ensure we do not destroy GestureRecognizers while we are still processing them
[platform/core/uifw/dali-core.git] / dali / internal / event / events / rotation-gesture / rotation-gesture-recognizer.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/events/rotation-gesture/rotation-gesture-recognizer.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/events/touch-point.h>
26 #include <dali/public-api/math/vector2.h>
27 #include <dali/devel-api/events/gesture-devel.h>
28 #include <dali/integration-api/events/touch-event-integ.h>
29 #include <dali/internal/event/events/rotation-gesture/rotation-gesture-event.h>
30 #include <dali/internal/event/common/scene-impl.h>
31
32 // INTERNAL INCLUDES
33
34
35 namespace Dali
36 {
37
38 namespace Internal
39 {
40
41 namespace
42 {
43
44 inline float GetAngle( const Integration::Point& point1, const Integration::Point& point2 )
45 {
46   const Vector2& point1ScreenPosition = point1.GetScreenPosition();
47   const Vector2& point2ScreenPosition = point2.GetScreenPosition();
48   return atan2( point2ScreenPosition.y - point1ScreenPosition.y, point2ScreenPosition.x - point1ScreenPosition.x );
49 }
50
51 inline Vector2 GetCenterPoint( const Integration::Point& point1, const Integration::Point& point2 )
52 {
53   return Vector2( point1.GetScreenPosition() + point2.GetScreenPosition() ) * 0.5f;
54 }
55
56 } // unnamed namespace
57
58 RotationGestureRecognizer::RotationGestureRecognizer( Observer& observer, uint32_t minimumTouchEvents, uint32_t minimumTouchEventsAfterStart )
59 : GestureRecognizer( DevelGesture::Rotation ),
60   mObserver( observer ),
61   mState( Clear ),
62   mTouchEvents(),
63   mStartingAngle( 0.0f ),
64   mMinimumTouchEvents( minimumTouchEvents ),
65   mMinimumTouchEventsAfterStart( minimumTouchEventsAfterStart )
66 {
67 }
68
69 void RotationGestureRecognizer::SendEvent( const Integration::TouchEvent& event )
70 {
71   int pointCount = event.GetPointCount();
72   GestureRecognizerPtr ptr(this); // To keep us from being destroyed during the life-time of this method
73
74   switch( mState )
75   {
76     case Clear:
77     {
78       if( pointCount == 2 )
79       {
80         // Change state to possible as we have two touch points.
81         mState = Possible;
82         mTouchEvents.push_back( event );
83       }
84       break;
85     }
86
87     case Possible:
88     {
89       if ( pointCount != 2 )
90       {
91         // We no longer have two touch points so change state back to Clear.
92         mState = Clear;
93         mTouchEvents.clear();
94       }
95       else
96       {
97         const Integration::Point& currentPoint1 = event.points[0];
98         const Integration::Point& currentPoint2 = event.points[1];
99
100         if( currentPoint1.GetState() == PointState::UP || currentPoint2.GetState() == PointState::UP || currentPoint1.GetState() == PointState::INTERRUPTED )
101         {
102           // One of our touch points has an Up event so change our state back to Clear.
103           mState = Clear;
104           mTouchEvents.clear();
105         }
106         else
107         {
108           mTouchEvents.push_back( event );
109
110           // We can only determine a rotation after a certain number of touch points have been collected.
111           if( mTouchEvents.size() >= mMinimumTouchEvents )
112           {
113             // Remove the first few events from the vector otherwise values are exaggerated
114             mTouchEvents.erase( mTouchEvents.begin(), mTouchEvents.end() - mMinimumTouchEvents );
115
116             if( !mTouchEvents.empty() )
117             {
118               mStartingAngle = GetAngle( mTouchEvents.begin()->points[0], mTouchEvents.begin()->points[1] );
119
120               // Send rotation started
121               SendRotation( Gesture::Started, event );
122
123               mState = Started;
124             }
125
126             mTouchEvents.clear();
127
128             if( mState == Possible )
129             {
130               // No rotation, so restart detection
131               mState = Clear;
132               mTouchEvents.clear();
133             }
134           }
135         }
136       }
137       break;
138     }
139
140     case Started:
141     {
142       if(event.points[0].GetState() == PointState::INTERRUPTED)
143       {
144         // System interruption occurred, rotation should be cancelled
145         mTouchEvents.clear();
146         SendRotation(Gesture::Cancelled, event);
147         mState = Clear;
148         mTouchEvents.clear();
149       }
150       else if( pointCount != 2 )
151       {
152         // Send rotation finished event
153         SendRotation( Gesture::Finished, event );
154
155         mState = Clear;
156         mTouchEvents.clear();
157       }
158       else
159       {
160         const Integration::Point& currentPoint1 = event.points[0];
161         const Integration::Point& currentPoint2 = event.points[1];
162
163         if( ( currentPoint1.GetState() == PointState::UP ) ||
164             ( currentPoint2.GetState() == PointState::UP ) )
165         {
166           mTouchEvents.push_back( event );
167           // Send rotation finished event
168           SendRotation( Gesture::Finished, event );
169
170           mState = Clear;
171           mTouchEvents.clear();
172         }
173         else
174         {
175           mTouchEvents.push_back( event );
176
177           if( mTouchEvents.size() >= mMinimumTouchEventsAfterStart )
178           {
179             // Send rotation continuing
180             SendRotation( Gesture::Continuing, event );
181
182             mTouchEvents.clear();
183           }
184         }
185       }
186       break;
187     }
188   }
189 }
190
191 void RotationGestureRecognizer::SetMinimumTouchEvents( uint32_t value )
192 {
193   mMinimumTouchEvents = value;
194 }
195
196 void RotationGestureRecognizer::SetMinimumTouchEventsAfterStart( uint32_t value )
197 {
198   mMinimumTouchEventsAfterStart = value;
199 }
200
201 void RotationGestureRecognizer::SendRotation( Gesture::State state, const Integration::TouchEvent& currentEvent )
202 {
203   RotationGestureEvent gesture( state );
204
205   if( !mTouchEvents.empty() )
206   {
207     // Assert if we have been holding TouchEvents that do not have 2 points
208     DALI_ASSERT_DEBUG( mTouchEvents[0].GetPointCount() == 2 );
209
210     // We should use the current event in our calculations unless it does not have two points.
211     // If it does not have two points, then we should use the last point in mTouchEvents.
212     Integration::TouchEvent event( currentEvent );
213     if( event.GetPointCount() != 2 )
214     {
215       event = *mTouchEvents.rbegin();
216     }
217
218     const Integration::Point& currentPoint1( event.points[0] );
219     const Integration::Point& currentPoint2( event.points[1] );
220
221     gesture.rotation = GetAngle( currentPoint1, currentPoint2 ) - mStartingAngle;
222     gesture.centerPoint = GetCenterPoint( currentPoint1, currentPoint2 );
223   }
224   else
225   {
226     // Something has gone wrong, just cancel the gesture.
227     gesture.state = Gesture::Cancelled;
228   }
229
230   gesture.time = currentEvent.time;
231
232   if( mScene )
233   {
234     // Create another handle so the recognizer cannot be destroyed during process function
235     GestureRecognizerPtr recognizerHandle = this;
236
237     mObserver.Process( *mScene, gesture );
238   }
239 }
240
241 } // namespace Internal
242
243 } // namespace Dali