6e0b24d2b3265277e61e03d5b90f4c0ab4cd48cc
[platform/core/uifw/dali-core.git] / dali / internal / event / events / long-press-gesture / long-press-gesture-recognizer.cpp
1 /*
2  * Copyright (c) 2019 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/long-press-gesture/long-press-gesture-recognizer.h>
20
21 // EXTERNAL INCLUDES
22 #include <cmath>
23
24 #include <dali/public-api/events/touch-point.h>
25 #include <dali/public-api/math/vector2.h>
26
27 #include <dali/internal/event/common/thread-local-storage.h>
28 #include <dali/internal/event/events/gesture-requests.h>
29 #include <dali/integration-api/events/touch-event-integ.h>
30 #include <dali/integration-api/platform-abstraction.h>
31
32 namespace Dali
33 {
34
35 namespace Internal
36 {
37
38 namespace
39 {
40 // TODO: Set these according to DPI
41 const float MAXIMUM_MOTION_ALLOWED = 60.0f;
42 // TODO: Set this time according to system setting (vconf)
43 const unsigned long LONG_PRESS_TIME = 500u;
44 } // unnamed namespace
45
46 LongPressGestureRecognizer::LongPressGestureRecognizer(Observer& observer, Vector2 screenSize, const LongPressGestureRequest& request )
47 : GestureRecognizer( screenSize, Gesture::LongPress ),
48   mObserver( observer ),
49   mState( Clear ),
50   mMinimumTouchesRequired( request.minTouches ),
51   mMaximumTouchesRequired( request.maxTouches ),
52   mTouchTime( 0 ),
53   mTimerId( 0 )
54 {
55 }
56
57 LongPressGestureRecognizer::~LongPressGestureRecognizer()
58 {
59 }
60
61 void LongPressGestureRecognizer::SendEvent(const Integration::TouchEvent& event)
62 {
63   unsigned int pointCount( event.GetPointCount() );
64   Dali::Integration::PlatformAbstraction& platformAbstraction = ThreadLocalStorage::Get().GetPlatformAbstraction();
65
66   switch (mState)
67   {
68     // Clear: Wait till one point touches the screen before starting timer.
69     case Clear:
70     {
71       const Integration::Point& point = event.points[0];
72
73       if ( point.GetState() == PointState::DOWN )
74       {
75         mTouchPositions.clear();
76         mTouchPositions[point.GetDeviceId()] = point.GetScreenPosition();
77
78         mTouchTime = event.time;
79
80         mTimerId = platformAbstraction.StartTimer(GetSystemValue(), MakeCallback( this, &LongPressGestureRecognizer::TimerCallback));
81
82         // A long press gesture may be possible, tell Core about this and change state to Touched.
83         mState = Touched;
84         EmitGesture( Gesture::Possible );
85       }
86
87       break;
88     }
89
90     // Touched: Monitor movement and addition/removal of points.
91     case Touched:
92     {
93       if (pointCount > mMaximumTouchesRequired)
94       {
95         // A long press did not occur, tell Core that it was cancelled and change state to Failed.
96         EmitGesture( Gesture::Cancelled );
97         mTouchPositions.clear();
98         platformAbstraction.CancelTimer(mTimerId);
99         mState = Failed;
100         break;
101       }
102
103       bool endLoop(false);
104
105       for ( Integration::PointContainerConstIterator iter = event.points.begin(), endIter = event.points.end();
106            iter != endIter && !endLoop; ++iter)
107       {
108         switch( iter->GetState() )
109         {
110           // add point.
111           case PointState::DOWN:
112           {
113             mTouchPositions[iter->GetDeviceId()] = iter->GetScreenPosition();
114             break;
115           }
116
117           // remove point.
118           case PointState::UP:
119           case PointState::INTERRUPTED:
120           {
121             // System has interrupted us, long press is not possible, inform Core
122             EmitGesture( Gesture::Cancelled );
123             mTouchPositions.clear();
124             platformAbstraction.CancelTimer(mTimerId);
125             mState = ( pointCount == 1 ) ? Clear : Failed; // Change state to Clear if only one point, Failed otherwise.
126             endLoop = true;
127             break;
128           }
129
130           case PointState::MOTION:
131           {
132             const Vector2 touchPosition( mTouchPositions[iter->GetDeviceId()] - iter->GetScreenPosition() );
133             float distanceSquared = touchPosition.LengthSquared();
134
135             if (distanceSquared > ( MAXIMUM_MOTION_ALLOWED * MAXIMUM_MOTION_ALLOWED ) )
136             {
137               // We have moved more than the allowable motion for a long press gesture. Inform Core and change state to Failed.
138               EmitGesture( Gesture::Cancelled );
139               platformAbstraction.CancelTimer(mTimerId);
140               mState = Failed;
141               endLoop = true;
142             }
143             break;
144           }
145
146           case PointState::STATIONARY:
147           case PointState::LEAVE:
148           {
149             break;
150           }
151         }
152       }
153       break;
154     }
155
156     // Failed/Finished: Monitor the touches, waiting for all touches to be released.
157     case Failed:
158     case Finished:
159     {
160       // eventually the final touch point will be removed, marking the end of this gesture.
161       if ( pointCount == 1 )
162       {
163         PointState::Type primaryPointState = event.points[0].GetState();
164
165         if ( (primaryPointState == PointState::UP) || (primaryPointState == PointState::INTERRUPTED) )
166         {
167           if(mState == Finished)
168           {
169             // When the last touch point is lifted, we should inform the Core that the Long press has finished.
170             EmitGesture(Gesture::Finished);
171           }
172           mTouchPositions.clear();
173           mState = Clear; // Reset state to clear when last touch point is lifted.
174         }
175       }
176       break;
177     }
178   }
179 }
180
181 void LongPressGestureRecognizer::Update(const GestureRequest& request)
182 {
183   const LongPressGestureRequest& longPress = static_cast<const LongPressGestureRequest&>(request);
184
185   mMinimumTouchesRequired = longPress.minTouches;
186   mMaximumTouchesRequired = longPress.maxTouches;
187 }
188
189 bool LongPressGestureRecognizer::TimerCallback()
190 {
191   EmitGesture(Gesture::Started);
192
193   mState = Finished;
194
195   return false;
196 }
197
198 void LongPressGestureRecognizer::EmitGesture(Gesture::State state)
199 {
200   unsigned int touchPoints ( static_cast<unsigned int>( mTouchPositions.size() ) );
201
202   // We should tell Core about the Possible and Cancelled states regardless of whether we have satisfied long press requirements.
203   if ( (state == Gesture::Possible) ||
204        (state == Gesture::Cancelled) ||
205        (touchPoints >= mMinimumTouchesRequired) )
206   {
207     LongPressGestureEvent longPress( state );
208     longPress.numberOfTouches = touchPoints;
209
210     for (std::map<int, Vector2>::iterator iter = mTouchPositions.begin(), endIter = mTouchPositions.end();
211          iter != endIter; ++iter)
212     {
213       longPress.point += iter->second;
214     }
215     longPress.point /= static_cast<float>( touchPoints );
216
217     longPress.time = mTouchTime;
218     if ( state != Gesture::Possible )
219     {
220       longPress.time += GetSystemValue();
221     }
222
223     if( mScene )
224     {
225       // Create another handle so the recognizer cannot be destroyed during process function
226       GestureRecognizerPtr recognizerHandle = this;
227
228       mObserver.Process(*mScene, longPress);
229     }
230   }
231 }
232
233 int LongPressGestureRecognizer::GetSystemValue()
234 {
235   return LONG_PRESS_TIME;
236 }
237
238 } // namespace Internal
239
240 } // namespace Dali