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