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