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