Further Setter/Getter public API removal from Dali::Actor
[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 #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, Actor parent, Animation animation, float startTime, float endTime )
85 {
86   Actor text = TextLabel::New( std::move( string ) );
87   Vector3 position( Stage::GetCurrent().GetSize() * 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( DevelActor::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 stage & connect to the key event signal
145     Stage stage = Stage::GetCurrent();
146     stage.KeyEventSignal().Connect(this, &GestureExample::OnKeyEvent);
147
148     // Create a background with a linear gradient which matches parent size & is placed in the center.
149     Actor background = Control::New();
150     background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
151     background.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
152     background.SetProperty( Control::Property::BACKGROUND, BACKGROUND );
153     stage.Add( background );
154
155     // Create a control with a circular gradient that we'll use for the gestures and be a quarter of the size of the stage.
156     Actor touchControl = Control::New();
157     touchControl.SetProperty( Actor::Property::SIZE, stage.GetSize() * 0.25f );
158     touchControl.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
159     touchControl.SetProperty( Control::Property::BACKGROUND, CONTROL_BACKGROUND );
160     background.Add( touchControl );
161
162     // Connect to the touch signal
163     touchControl.TouchSignal().Connect( this, &GestureExample::OnTouch );
164     touchControl.SetProperty( Actor::Property::LEAVE_REQUIRED, true );
165
166     // Create a long press gesture detector, attach the actor & connect
167     mLongPressDetector = LongPressGestureDetector::New();
168     mLongPressDetector.Attach( touchControl );
169     mLongPressDetector.DetectedSignal().Connect( this, &GestureExample::OnLongPress );
170
171     // Create a pan gesture detector & connect, don't attach the actor as we'll attach it when we detect a long-press
172     mPanDetector = PanGestureDetector::New();
173     mPanDetector.DetectedSignal().Connect( this, &GestureExample::OnPan );
174
175     // Create a tap gesture detector, attach the actor & connect
176     mTapDetector = TapGestureDetector::New();
177     mTapDetector.Attach( touchControl );
178     mTapDetector.DetectedSignal().Connect( this, &GestureExample::OnTap );
179
180     // Create a pinch gesture detector, attach the actor & connect
181     mPinchDetector = PinchGestureDetector::New();
182     mPinchDetector.Attach( touchControl );
183     mPinchDetector.DetectedSignal().Connect( this, &GestureExample::OnPinch );
184
185     // Create a rotation gesture detector, attach the actor & connect
186     mRotationDetector = RotationGestureDetector::New();
187     mRotationDetector.Attach( touchControl );
188     mRotationDetector.DetectedSignal().Connect( this, &GestureExample::OnRotation );
189
190     // Create an animation which shakes the actor when in Pan mode
191     mShakeAnimation = Animation::New( SHAKY_ANIMATION_DURATION );
192     mShakeAnimation.AnimateBy( Property( touchControl, Actor::Property::ORIENTATION ),
193                                Quaternion( Degree(  SHAKY_ANIMATION_ANGLE ), Vector3::ZAXIS ),
194                                AlphaFunction::BOUNCE,
195                                TimePeriod( 0.0f, SHAKY_ANIMATION_SEGMENT_TIME ) );
196     mShakeAnimation.AnimateBy( Property( touchControl, Actor::Property::ORIENTATION ),
197                                Quaternion( Degree( -SHAKY_ANIMATION_ANGLE ), Vector3::ZAXIS ),
198                                AlphaFunction::BOUNCE,
199                                TimePeriod( SHAKY_ANIMATION_SEGMENT_TIME, SHAKY_ANIMATION_SEGMENT_TIME ) );
200
201     // Animate help information
202     // Here we just animate some text on the screen to show tips on how to use this example
203     // The help animation loops
204     Animation helpAnimation = Animation::New( HELP_ANIMATION_DURATION );
205
206     float startTime( 0.0f );
207     float endTime( startTime + HELP_ANIMATION_SEGMENT_TIME );
208
209     AddHelpInfo( "Tap image for animation",                              background, helpAnimation, startTime, endTime );
210     AddHelpInfo( "Press & Hold image to drag",                           background, helpAnimation, startTime += HELP_ANIMATION_SEGMENT_TIME, endTime += HELP_ANIMATION_SEGMENT_TIME );
211     AddHelpInfo( "Pinch image to resize",                                background, helpAnimation, startTime += HELP_ANIMATION_SEGMENT_TIME, endTime += HELP_ANIMATION_SEGMENT_TIME );
212     AddHelpInfo( "Move fingers in a circular motion on image to rotate", background, helpAnimation, startTime += HELP_ANIMATION_SEGMENT_TIME, endTime += HELP_ANIMATION_SEGMENT_TIME );
213     helpAnimation.SetLooping( true );
214     helpAnimation.Play();
215   }
216
217   /**
218    * @brief Called when our actor is touched.
219    *
220    * @param[in]  actor  The touched actor
221    * @param[in]  touch  The touch event
222    */
223   bool OnTouch( Actor actor, const TouchData& touch )
224   {
225     switch( touch.GetState( 0 ) )
226     {
227       case PointState::DOWN:
228       {
229         // When we get a touch point, change the color of the actor.
230
231         Animation anim = Animation::New( TOUCH_MODE_ANIMATION_DURATION );
232         anim.AnimateTo( Property( actor, Actor::Property::COLOR ), TOUCH_MODE_COLOR );
233         anim.Play();
234         break;
235       }
236
237       case PointState::LEAVE:
238       case PointState::UP:
239       case PointState::INTERRUPTED:
240       {
241         if( ! mPanStarted )
242         {
243           // If we're not panning, change the color back to normal.
244
245           Animation anim = Animation::New( TOUCH_MODE_ANIMATION_DURATION );
246           anim.AnimateTo( Property( actor, Actor::Property::COLOR ), Vector4::ONE );
247           anim.Play();
248
249           // Stop the shake animation from looping.
250           mShakeAnimation.SetLooping( false );
251         }
252         break;
253       }
254
255       default:
256       {
257         break;
258       }
259     }
260     return true;
261   }
262
263   /**
264    * @brief Called when a long-press gesture is detected on our control.
265    *
266    * @param[in]  actor      The actor that's been long-pressed
267    * @param[in]  longPress  The long-press gesture information
268    */
269   void OnLongPress( Actor actor, const LongPressGesture& longPress )
270   {
271     if( longPress.state == Gesture::Started )
272     {
273       // When we first receive a long press, attach the actor to the pan detector.
274       mPanDetector.Attach( actor );
275
276       // Do a small animation to indicate to the user that we are in pan mode.
277       Animation anim = Animation::New( PAN_MODE_CHANGE_ANIMATION_DURATION );
278       anim.AnimateTo( Property( actor, Actor::Property::SCALE ), actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE ) * PAN_MODE_START_ANIMATION_SCALE, AlphaFunction::BOUNCE );
279       anim.Play();
280
281       // Start the shake animation so the user knows when they are in pan mode.
282       mShakeAnimation.SetLooping( true );
283       mShakeAnimation.Play();
284     }
285   }
286
287   /**
288    * @brief Called when a pan gesture is detected on our control.
289    *
290    * @param[in]  actor  The actor that's been panned
291    * @param[in]  pan    The pan gesture information
292    */
293   void OnPan( Actor actor, const PanGesture& pan )
294   {
295     // Just move the actor by the displacement.
296
297     // As the displacement is in local actor coords, we will have to multiply the displacement by the
298     // actor's scale so that it moves the correct amount in the parent's coordinate system.
299     Vector3 scaledDisplacement( pan.displacement );
300     scaledDisplacement *= actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE );
301
302     Vector3 currentPosition;
303     actor.GetProperty( Actor::Property::POSITION ).Get( currentPosition );
304
305     Vector3 newPosition = currentPosition + scaledDisplacement;
306     actor.SetProperty( Actor::Property::POSITION, newPosition );
307
308     switch( pan.state )
309     {
310       case Gesture::Started:
311       {
312         mPanStarted = true;
313         break;
314       }
315
316       case Gesture::Finished:
317       case Gesture::Cancelled:
318       {
319         // If we cancel or finish the pan, do an animation to indicate this and stop the shake animation.
320
321         Animation anim = Animation::New( PAN_MODE_CHANGE_ANIMATION_DURATION );
322         anim.AnimateTo( Property( actor, Actor::Property::COLOR ), Vector4::ONE );
323         anim.AnimateTo( Property( actor, Actor::Property::SCALE ), actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE ) * PAN_MODE_END_ANIMATION_SCALE, AlphaFunction::BOUNCE );
324
325         // Move actor back to center if we're out of bounds
326         Vector2 halfStageSize = Stage::GetCurrent().GetSize() * 0.5f;
327         if( ( abs( newPosition.x ) > halfStageSize.width  ) ||
328             ( abs( newPosition.y ) > halfStageSize.height ) )
329         {
330           anim.AnimateTo( Property( actor, Actor::Property::POSITION ), Vector3::ZERO, AlphaFunction::EASE_IN );
331         }
332         anim.Play();
333
334         // Set end of pan configuration and disconnect the actor from the pan detector
335         mShakeAnimation.SetLooping( false );
336         mPanStarted = false;
337         mPanDetector.Detach( actor );
338         break;
339       }
340
341       default:
342       {
343         break;
344       }
345     }
346   }
347
348   /**
349    * @brief Called when a tap gesture is detected on our control.
350    *
351    * @param[in]  actor The actor that's been tapped
352    * @param[in]  tap   The tap gesture information
353    */
354   void OnTap( Actor actor, const TapGesture& tap )
355   {
356     // Do a short animation to show a tap has happened.
357
358     Animation anim = Animation::New( TAP_ANIMATION_DURATION );
359     anim.AnimateBy( Property( actor, Actor::Property::ORIENTATION ), Quaternion( ANGLE_360, Vector3::ZAXIS ) );
360     anim.AnimateTo( Property( actor, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::LINEAR );
361     anim.AnimateTo( Property( actor, Actor::Property::COLOR ), TAP_ANIMATION_COLOR, AlphaFunction::BOUNCE );
362     anim.AnimateTo( Property( actor, Actor::Property::POSITION ), Vector3::ZERO, AlphaFunction::EASE_OUT_SQUARE );
363     anim.Play();
364   }
365
366   /**
367    * @brief Called when a pinch gesture is detected on our control.
368    *
369    * @param[in]  actor  The actor that's been pinched
370    * @param[in]  pinch  The pinch gesture information
371    */
372   void OnPinch( Actor actor, const PinchGesture& pinch )
373   {
374     switch( pinch.state )
375     {
376       case Gesture::Started:
377       {
378         // Starting scale is required so that we know what to multiply the pinch.scale by.
379         mStartingScale = actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE );
380         break;
381       }
382
383       case Gesture::Finished:
384       case Gesture::Cancelled:
385       {
386         Vector3 scale( actor.GetCurrentProperty< Vector3 >( Actor::Property::SCALE ) );
387
388         // Ensure the actor sizes itself to be within the limits defined.
389         if ( scale.x < MINIMUM_SCALE.x )
390         {
391           scale = MINIMUM_SCALE;
392         }
393         else if ( scale.x > MAXIMUM_SCALE.x )
394         {
395           scale = MAXIMUM_SCALE;
396         }
397
398         // Do an animation to come back to go back to the limits.
399         Animation anim = Animation::New( SCALE_BACK_ANIMATION_DURATION );
400         anim.AnimateTo( Property( actor, Actor::Property::SCALE ), scale, AlphaFunction::LINEAR );
401         anim.Play();
402         break;
403       }
404
405       default:
406       {
407         break;
408       }
409     }
410
411     actor.SetProperty( Actor::Property::SCALE, mStartingScale * pinch.scale );
412   }
413
414   /**
415    * @brief Called when a rotation gesture is detected on our control.
416    *
417    * @param[in]  actor     The actor that's been pinched
418    * @param[in]  rotation  The rotation gesture information
419    */
420   void OnRotation( Actor actor, const RotationGesture& rotation )
421   {
422     switch( rotation.state )
423     {
424       case Gesture::Started:
425       {
426         // Starting orientation is required so that we know what to multiply the rotation.rotation by.
427         mStartingOrientation = actor.GetCurrentProperty< Quaternion >( Actor::Property::ORIENTATION );
428         break;
429       }
430
431       case Gesture::Finished:
432       case Gesture::Cancelled:
433       {
434         // Do an animation to come back to go back to the original orientation.
435         Animation anim = Animation::New( ROTATE_BACK_ANIMATION_DURATION );
436         anim.AnimateTo( Property( actor, Actor::Property::ORIENTATION ), Quaternion::IDENTITY, AlphaFunction::LINEAR );
437         anim.Play();
438         break;
439       }
440
441       default:
442       {
443         break;
444       }
445     }
446
447     actor.SetProperty( Actor::Property::ORIENTATION, Quaternion( mStartingOrientation * Quaternion( rotation.rotation, Vector3::ZAXIS ) ) );
448   }
449
450   /**
451    * @brief Called when any key event is received.
452    *
453    * Will use this to quit the application if Back or the Escape key is received.
454    * @param[in] event The key event information
455    */
456   void OnKeyEvent( const KeyEvent& event )
457   {
458     if( event.state == KeyEvent::Down )
459     {
460       if( IsKey( event, Dali::DALI_KEY_ESCAPE ) || IsKey( event, Dali::DALI_KEY_BACK ) )
461       {
462         mApplication.Quit();
463       }
464     }
465   }
466
467 private:
468   Application&  mApplication;
469
470   PanGestureDetector mPanDetector;
471   LongPressGestureDetector mLongPressDetector;
472   TapGestureDetector mTapDetector;
473   PinchGestureDetector mPinchDetector;
474   RotationGestureDetector mRotationDetector;
475
476   Vector3 mStartingScale; ///< Set to the scale of the control when pinch starts.
477   Quaternion mStartingOrientation; ///< Set to the orientation of the control when the rotation starts.
478   Animation mShakeAnimation; ///< "Shake" animation to show when we are in panning mode.
479   bool mPanStarted = false; ///< Set to true to state that panning has started.
480 };
481
482 int DALI_EXPORT_API main( int argc, char **argv )
483 {
484   Application application = Application::New( &argc, &argv );
485   GestureExample controller( application );
486   application.MainLoop();
487   return 0;
488 }