Revert "[Tizen] Revert "Changes after touch consumed behaviour change"
[platform/core/uifw/dali-demo.git] / examples / gestures / gesture-example.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 // EXTERNAL INCLUDES
19 #include <dali-toolkit/dali-toolkit.h>
20 #include <string>
21 #include <dali/devel-api/actors/actor-devel.h>
22 #include <dali/devel-api/events/rotation-gesture.h>
23 #include <dali/devel-api/events/rotation-gesture-detector.h>
24
25 using namespace Dali;
26 using namespace Dali::Toolkit;
27 using namespace std;
28
29 namespace
30 {
31 const Property::Value BACKGROUND
32 {
33   { Toolkit::Visual::Property::TYPE, Visual::GRADIENT },
34   { GradientVisual::Property::STOP_COLOR,  Property::Array{ Vector4( 167.0f, 207.0f, 223.0f, 255.0f ) / 255.0f,
35                                                             Vector4(   0.0f,  64.0f, 137.0f, 255.0f ) / 255.0f } },
36   { GradientVisual::Property::START_POSITION, Vector2( 0.0f, -0.5f ) },
37   { GradientVisual::Property::END_POSITION,   Vector2( 0.0f,  0.5f ) }
38 };
39
40 const Property::Value CONTROL_BACKGROUND
41 {
42   { Toolkit::Visual::Property::TYPE, Visual::GRADIENT },
43   { GradientVisual::Property::STOP_COLOR, Property::Array{ Vector4( 234.0f, 185.0f,  45.0f, 255.0f ) / 255.0f,
44                                                            Vector4( 199.0f, 152.0f,  16.0f, 255.0f ) / 255.0f } },
45   { GradientVisual::Property::CENTER, Vector2::ZERO },
46   { GradientVisual::Property::RADIUS, 0.5f }
47 };
48
49 const float HELP_ANIMATION_DURATION( 25.0f );
50 const float HELP_ANIMATION_SEGMENT_TIME( 5.0f );
51 const float HELP_ANIMATION_TRANSITION_DURATION( 0.75f );
52 const Vector2 HELP_TEXT_POSITION_MULTIPLIER( 0.25f, 0.13f );
53
54 const float SHAKY_ANIMATION_DURATION( 0.1f );
55 const float SHAKY_ANIMATION_SEGMENT_TIME( 0.05f );
56 const float SHAKY_ANIMATION_ANGLE( 1.0f );
57
58 const float TOUCH_MODE_ANIMATION_DURATION( 0.1f );
59 const Vector4 TOUCH_MODE_COLOR( 1.0f, 0.7f, 0.7f, 1.0f );
60
61 const float PAN_MODE_CHANGE_ANIMATION_DURATION( 0.25f );
62 const Vector3 PAN_MODE_START_ANIMATION_SCALE( 1.2f, 1.2f, 1.0f );
63 const Vector3 PAN_MODE_END_ANIMATION_SCALE( 0.8f, 0.8f, 1.0f );
64
65 const float TAP_ANIMATION_DURATION( 0.5f );
66 const Vector4 TAP_ANIMATION_COLOR( 0.8f, 0.5, 0.2f, 0.6f );
67
68 const Vector3 MINIMUM_SCALE( Vector3::ONE );
69 const Vector3 MAXIMUM_SCALE( Vector3::ONE * 2.0f );
70 const float SCALE_BACK_ANIMATION_DURATION( 0.25f );
71 const float ROTATE_BACK_ANIMATION_DURATION( 0.25f );
72
73 /**
74  * @brief Shows the given string between the given start and end times.
75  *
76  * Appropriately animates the string into and out of the scene.
77  *
78  * @param[in]  string     The label to display, this is an rvalue reference & will be moved
79  * @param[in]  parent     The parent to add the label to
80  * @param[in]  animation  The animation to add the animators created in this function
81  * @param[in]  startTime  When to start the animators
82  * @param[in]  endTime    When to end the animators
83  */
84 void AddHelpInfo( const std::string&& string, const Vector2& windowSize, Actor parent, Animation animation, float startTime, float endTime )
85 {
86   Actor text = TextLabel::New( std::move( string ) );
87   Vector3 position( windowSize * HELP_TEXT_POSITION_MULTIPLIER );
88
89   text.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
90   text.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_CENTER );
91   text.SetProperty( Actor::Property::POSITION, position );
92   text.SetProperty( Actor::Property::OPACITY, 0.0f );
93   text.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, Text::HorizontalAlignment::CENTER );
94   text.SetProperty( TextLabel::Property::MULTI_LINE, true );
95   parent.Add( text );
96
97   // Animate IN
98   TimePeriod timePeriod( startTime, HELP_ANIMATION_TRANSITION_DURATION );
99   animation.AnimateTo( Property( text, Actor::Property::COLOR_ALPHA ),  1.0f,       timePeriod );
100   animation.AnimateBy( Property( text, Actor::Property::POSITION_X ),  -position.x, timePeriod );
101
102   // Animate OUT
103   timePeriod.delaySeconds = endTime;
104   animation.AnimateTo( Property( text, Actor::Property::COLOR_ALPHA ),  0.0f,       timePeriod );
105   animation.AnimateBy( Property( text, Actor::Property::POSITION_X ),  -position.x, timePeriod );
106 }
107
108 } // unnamed namespace
109
110 /**
111  * @brief This example shows how to use the different gesture detectors available.
112  *
113  * - Tapping on the control shows a small rotation animation.
114  * - A Long press on the control puts it into Pan Mode.
115  * - When in Pan mode, the control can be panned (moved).
116  * - When the pan ends, we exit the Pan mode via an animation.
117  * - Pinching the control changes the scale of the control.
118  */
119 class GestureExample : public ConnectionTracker
120 {
121 public:
122
123   /**
124    * @brief Constructor.
125    *
126    * @param[in]  application  Reference to the application
127    */
128   GestureExample( Application &application )
129   : mApplication( application )
130   {
131     // Connect to the Application's Init signal
132     application.InitSignal().Connect( this, &GestureExample::Create );
133   }
134
135 private:
136
137   /**
138    * @brief Creates the scene as described in this class' description.
139    *
140    * @param[in]  application  Reference to the application class
141    */
142   void Create( Application& application )
143   {
144     // Get a handle to the window & connect to the key event signal
145     auto window = application.GetWindow();
146     Vector2 windowSize = window.GetSize();
147     window.KeyEventSignal().Connect(this, &GestureExample::OnKeyEvent);
148
149     // Create a background with a linear gradient which matches parent size & is placed in the center.
150     Actor background = Control::New();
151     background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
152     background.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
153     background.SetProperty( Control::Property::BACKGROUND, BACKGROUND );
154     window.Add( background );
155
156     // Create a control with a circular gradient that we'll use for the gestures and be a quarter of the size of the window.
157     Actor touchControl = Control::New();
158     touchControl.SetProperty( Actor::Property::SIZE, windowSize * 0.25f );
159     touchControl.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
160     touchControl.SetProperty( Control::Property::BACKGROUND, CONTROL_BACKGROUND );
161     background.Add( touchControl );
162
163     // Connect to the touch signal
164     touchControl.TouchSignal().Connect( this, &GestureExample::OnTouch );
165     touchControl.SetProperty( Actor::Property::LEAVE_REQUIRED, true );
166
167     // Create a long press gesture detector, attach the actor & connect
168     mLongPressDetector = LongPressGestureDetector::New();
169     mLongPressDetector.Attach( touchControl );
170     mLongPressDetector.DetectedSignal().Connect( this, &GestureExample::OnLongPress );
171
172     // Create a pan gesture detector & connect, don't attach the actor as we'll attach it when we detect a long-press
173     mPanDetector = PanGestureDetector::New();
174     mPanDetector.DetectedSignal().Connect( this, &GestureExample::OnPan );
175
176     // Create a tap gesture detector, attach the actor & connect
177     mTapDetector = TapGestureDetector::New();
178     mTapDetector.Attach( touchControl );
179     mTapDetector.DetectedSignal().Connect( this, &GestureExample::OnTap );
180
181     // Create a pinch gesture detector, attach the actor & connect
182     mPinchDetector = PinchGestureDetector::New();
183     mPinchDetector.Attach( touchControl );
184     mPinchDetector.DetectedSignal().Connect( this, &GestureExample::OnPinch );
185
186     // Create a rotation gesture detector, attach the actor & connect
187     mRotationDetector = RotationGestureDetector::New();
188     mRotationDetector.Attach( touchControl );
189     mRotationDetector.DetectedSignal().Connect( this, &GestureExample::OnRotation );
190
191     // Create an animation which shakes the actor when in Pan mode
192     mShakeAnimation = Animation::New( SHAKY_ANIMATION_DURATION );
193     mShakeAnimation.AnimateBy( Property( touchControl, Actor::Property::ORIENTATION ),
194                                Quaternion( Degree(  SHAKY_ANIMATION_ANGLE ), Vector3::ZAXIS ),
195                                AlphaFunction::BOUNCE,
196                                TimePeriod( 0.0f, SHAKY_ANIMATION_SEGMENT_TIME ) );
197     mShakeAnimation.AnimateBy( Property( touchControl, Actor::Property::ORIENTATION ),
198                                Quaternion( Degree( -SHAKY_ANIMATION_ANGLE ), Vector3::ZAXIS ),
199                                AlphaFunction::BOUNCE,
200                                TimePeriod( SHAKY_ANIMATION_SEGMENT_TIME, SHAKY_ANIMATION_SEGMENT_TIME ) );
201
202     // Animate help information
203     // Here we just animate some text on the screen to show tips on how to use this example
204     // The help animation loops
205     Animation helpAnimation = Animation::New( HELP_ANIMATION_DURATION );
206
207     float startTime( 0.0f );
208     float endTime( startTime + HELP_ANIMATION_SEGMENT_TIME );
209
210     AddHelpInfo( "Tap image for animation",                              windowSize, background, helpAnimation, startTime, endTime );
211     AddHelpInfo( "Press & Hold image to drag",                           windowSize, background, helpAnimation, startTime += HELP_ANIMATION_SEGMENT_TIME, endTime += HELP_ANIMATION_SEGMENT_TIME );
212     AddHelpInfo( "Pinch image to resize",                                windowSize, background, helpAnimation, startTime += HELP_ANIMATION_SEGMENT_TIME, endTime += HELP_ANIMATION_SEGMENT_TIME );
213     AddHelpInfo( "Move fingers in a circular motion on image to rotate", windowSize, background, helpAnimation, startTime += HELP_ANIMATION_SEGMENT_TIME, endTime += HELP_ANIMATION_SEGMENT_TIME );
214     helpAnimation.SetLooping( true );
215     helpAnimation.Play();
216   }
217
218   /**
219    * @brief Called when our actor is touched.
220    *
221    * @param[in]  actor  The touched actor
222    * @param[in]  touch  The touch event
223    */
224   bool OnTouch( Actor actor, const TouchEvent& touch )
225   {
226     switch( touch.GetState( 0 ) )
227     {
228       case PointState::DOWN:
229       {
230         // When we get a touch point, change the color of the actor.
231
232         Animation anim = Animation::New( TOUCH_MODE_ANIMATION_DURATION );
233         anim.AnimateTo( Property( actor, Actor::Property::COLOR ), TOUCH_MODE_COLOR );
234         anim.Play();
235         break;
236       }
237
238       case PointState::LEAVE:
239       case PointState::UP:
240       case PointState::INTERRUPTED:
241       {
242         if( ! mPanStarted )
243         {
244           // If we're not panning, change the color back to normal.
245
246           Animation anim = Animation::New( TOUCH_MODE_ANIMATION_DURATION );
247           anim.AnimateTo( Property( actor, Actor::Property::COLOR ), Vector4::ONE );
248           anim.Play();
249
250           // Stop the shake animation from looping.
251           mShakeAnimation.SetLooping( false );
252         }
253         break;
254       }
255
256       default:
257       {
258         break;
259       }
260     }
261     return false;
262   }
263
264   /**
265    * @brief Called when a long-press gesture is detected on our control.
266    *
267    * @param[in]  actor      The actor that's been long-pressed
268    * @param[in]  longPress  The long-press gesture information
269    */
270   void OnLongPress( Actor actor, const LongPressGesture& longPress )
271   {
272     if( longPress.state == Gesture::Started )
273     {
274       // When we first receive a long press, attach the actor to the pan detector.
275       mPanDetector.Attach( actor );
276
277       // Do a small animation to indicate to the user that we are in pan mode.
278       Animation anim = Animation::New( PAN_MODE_CHANGE_ANIMATION_DURATION );
279       anim.AnimateTo( Property( actor, Actor::Property::SCALE ), actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE ) * PAN_MODE_START_ANIMATION_SCALE, AlphaFunction::BOUNCE );
280       anim.Play();
281
282       // Start the shake animation so the user knows when they are in pan mode.
283       mShakeAnimation.SetLooping( true );
284       mShakeAnimation.Play();
285     }
286   }
287
288   /**
289    * @brief Called when a pan gesture is detected on our control.
290    *
291    * @param[in]  actor  The actor that's been panned
292    * @param[in]  pan    The pan gesture information
293    */
294   void OnPan( Actor actor, const PanGesture& pan )
295   {
296     // Just move the actor by the displacement.
297
298     // As the displacement is in local actor coords, we will have to multiply the displacement by the
299     // actor's scale so that it moves the correct amount in the parent's coordinate system.
300     Vector3 scaledDisplacement( pan.displacement );
301     scaledDisplacement *= actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE );
302
303     Vector3 currentPosition;
304     actor.GetProperty( Actor::Property::POSITION ).Get( currentPosition );
305
306     Vector3 newPosition = currentPosition + scaledDisplacement;
307     actor.SetProperty( Actor::Property::POSITION, newPosition );
308
309     switch( pan.state )
310     {
311       case Gesture::Started:
312       {
313         mPanStarted = true;
314         break;
315       }
316
317       case Gesture::Finished:
318       case Gesture::Cancelled:
319       {
320         // If we cancel or finish the pan, do an animation to indicate this and stop the shake animation.
321
322         Animation anim = Animation::New( PAN_MODE_CHANGE_ANIMATION_DURATION );
323         anim.AnimateTo( Property( actor, Actor::Property::COLOR ), Vector4::ONE );
324         anim.AnimateTo( Property( actor, Actor::Property::SCALE ), actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE ) * PAN_MODE_END_ANIMATION_SCALE, AlphaFunction::BOUNCE );
325
326         // Move actor back to center if we're out of bounds
327         Vector2 halfWindowSize = Vector2(mApplication.GetWindow().GetSize()) * 0.5f;
328         if( ( abs( newPosition.x ) > halfWindowSize.width  ) ||
329             ( abs( newPosition.y ) > halfWindowSize.height ) )
330         {
331           anim.AnimateTo( Property( actor, Actor::Property::POSITION ), Vector3::ZERO, AlphaFunction::EASE_IN );
332         }
333         anim.Play();
334
335         // Set end of pan configuration and disconnect the actor from the pan detector
336         mShakeAnimation.SetLooping( false );
337         mPanStarted = false;
338         mPanDetector.Detach( actor );
339         break;
340       }
341
342       default:
343       {
344         break;
345       }
346     }
347   }
348
349   /**
350    * @brief Called when a tap gesture is detected on our control.
351    *
352    * @param[in]  actor The actor that's been tapped
353    * @param[in]  tap   The tap gesture information
354    */
355   void OnTap( Actor actor, const TapGesture& tap )
356   {
357     // Do a short animation to show a tap has happened.
358
359     Animation anim = Animation::New( TAP_ANIMATION_DURATION );
360     anim.AnimateBy( Property( actor, Actor::Property::ORIENTATION ), Quaternion( ANGLE_360, Vector3::ZAXIS ) );
361     anim.AnimateTo( Property( actor, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::LINEAR );
362     anim.AnimateTo( Property( actor, Actor::Property::COLOR ), TAP_ANIMATION_COLOR, AlphaFunction::BOUNCE );
363     anim.AnimateTo( Property( actor, Actor::Property::POSITION ), Vector3::ZERO, AlphaFunction::EASE_OUT_SQUARE );
364     anim.Play();
365   }
366
367   /**
368    * @brief Called when a pinch gesture is detected on our control.
369    *
370    * @param[in]  actor  The actor that's been pinched
371    * @param[in]  pinch  The pinch gesture information
372    */
373   void OnPinch( Actor actor, const PinchGesture& pinch )
374   {
375     switch( pinch.state )
376     {
377       case Gesture::Started:
378       {
379         // Starting scale is required so that we know what to multiply the pinch.scale by.
380         mStartingScale = actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE );
381         break;
382       }
383
384       case Gesture::Finished:
385       case Gesture::Cancelled:
386       {
387         Vector3 scale( actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE ) );
388
389         // Ensure the actor sizes itself to be within the limits defined.
390         if ( scale.x < MINIMUM_SCALE.x )
391         {
392           scale = MINIMUM_SCALE;
393         }
394         else if ( scale.x > MAXIMUM_SCALE.x )
395         {
396           scale = MAXIMUM_SCALE;
397         }
398
399         // Do an animation to come back to go back to the limits.
400         Animation anim = Animation::New( SCALE_BACK_ANIMATION_DURATION );
401         anim.AnimateTo( Property( actor, Actor::Property::SCALE ), scale, AlphaFunction::LINEAR );
402         anim.Play();
403         break;
404       }
405
406       default:
407       {
408         break;
409       }
410     }
411
412     actor.SetProperty( Actor::Property::SCALE, mStartingScale * pinch.scale );
413   }
414
415   /**
416    * @brief Called when a rotation gesture is detected on our control.
417    *
418    * @param[in]  actor     The actor that's been pinched
419    * @param[in]  rotation  The rotation gesture information
420    */
421   void OnRotation( Actor actor, const RotationGesture& rotation )
422   {
423     switch( rotation.state )
424     {
425       case Gesture::Started:
426       {
427         // Starting orientation is required so that we know what to multiply the rotation.rotation by.
428         mStartingOrientation = actor.GetCurrentProperty< Quaternion >( Actor::Property::ORIENTATION );
429         break;
430       }
431
432       case Gesture::Finished:
433       case Gesture::Cancelled:
434       {
435         // Do an animation to come back to go back to the original orientation.
436         Animation anim = Animation::New( ROTATE_BACK_ANIMATION_DURATION );
437         anim.AnimateTo( Property( actor, Actor::Property::ORIENTATION ), Quaternion::IDENTITY, AlphaFunction::LINEAR );
438         anim.Play();
439         break;
440       }
441
442       default:
443       {
444         break;
445       }
446     }
447
448     actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( mStartingOrientation * Quaternion( rotation.rotation, Vector3::ZAXIS ) ) );
449   }
450
451   /**
452    * @brief Called when any key event is received.
453    *
454    * Will use this to quit the application if Back or the Escape key is received.
455    * @param[in] event The key event information
456    */
457   void OnKeyEvent( const KeyEvent& event )
458   {
459     if( event.state == KeyEvent::Down )
460     {
461       if( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
462       {
463         mApplication.Quit();
464       }
465     }
466   }
467
468 private:
469   Application&  mApplication;
470
471   PanGestureDetector mPanDetector;
472   LongPressGestureDetector mLongPressDetector;
473   TapGestureDetector mTapDetector;
474   PinchGestureDetector mPinchDetector;
475   RotationGestureDetector mRotationDetector;
476
477   Vector3 mStartingScale; ///< Set to the scale of the control when pinch starts.
478   Quaternion mStartingOrientation; ///< Set to the orientation of the control when the rotation starts.
479   Animation mShakeAnimation; ///< "Shake" animation to show when we are in panning mode.
480   bool mPanStarted = false; ///< Set to true to state that panning has started.
481 };
482
483 int DALI_EXPORT_API main( int argc, char **argv )
484 {
485   Application application = Application::New( &argc, &argv );
486   GestureExample controller( application );
487   application.MainLoop();
488   return 0;
489 }