Merge branch 'devel/master' into tizen
[platform/core/uifw/dali-demo.git] / examples / scroll-view / scroll-view-example.cpp
1 /*
2  * Copyright (c) 2017 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 <sstream>
20
21 // INTERNAL INCLUDES
22 #include "shared/view.h"
23 #include <dali/dali.h>
24 #include <dali-toolkit/dali-toolkit.h>
25 #include <dali-toolkit/devel-api/controls/buttons/button-devel.h>
26
27 using namespace Dali;
28 using namespace Dali::Toolkit;
29
30 namespace
31 {
32 const char * const BACKGROUND_IMAGE( DEMO_IMAGE_DIR "background-default.png" );
33 const char * const TOOLBAR_IMAGE( DEMO_IMAGE_DIR "top-bar.png" );
34 const char * const APPLICATION_TITLE( "ScrollView" );
35 const char * const EFFECT_CAROUSEL_IMAGE( DEMO_IMAGE_DIR "icon-scroll-view-carousel.png" );
36 const char * const EFFECT_CAROUSEL_IMAGE_SELECTED( DEMO_IMAGE_DIR "icon-scroll-view-carousel-selected.png" );
37 const char * const EFFECT_CUBE_IMAGE( DEMO_IMAGE_DIR "icon-scroll-view-inner-cube.png" );
38 const char * const EFFECT_CUBE_IMAGE_SELECTED( DEMO_IMAGE_DIR "icon-scroll-view-inner-cube-selected.png" );
39 const char * const EFFECT_SPIRAL_IMAGE( DEMO_IMAGE_DIR "icon-scroll-view-spiral.png" );
40 const char * const EFFECT_SPIRAL_IMAGE_SELECTED( DEMO_IMAGE_DIR "icon-scroll-view-spiral-selected.png" );
41 const char * const EFFECT_WAVE_IMAGE( DEMO_IMAGE_DIR "icon-effect-wave.png" );
42 const char * const EFFECT_WAVE_IMAGE_SELECTED( DEMO_IMAGE_DIR "icon-effect-wave-selected.png" );
43
44 const Vector3 ICON_SIZE(100.0f, 100.0f, 0.0f);
45
46 const char* EFFECT_MODE_NAME[] = {
47     "PageCarousel",
48     "PageCube",
49     "PageSpiral",
50     "PageWave"
51 };
52
53 const char * const IMAGE_PATHS[] = {
54     DEMO_IMAGE_DIR "gallery-medium-1.jpg",
55     DEMO_IMAGE_DIR "gallery-medium-2.jpg",
56     DEMO_IMAGE_DIR "gallery-medium-3.jpg",
57     DEMO_IMAGE_DIR "gallery-medium-4.jpg",
58     DEMO_IMAGE_DIR "gallery-medium-5.jpg",
59     DEMO_IMAGE_DIR "gallery-medium-6.jpg",
60     DEMO_IMAGE_DIR "gallery-medium-7.jpg",
61     DEMO_IMAGE_DIR "gallery-medium-8.jpg",
62     DEMO_IMAGE_DIR "gallery-medium-9.jpg",
63     DEMO_IMAGE_DIR "gallery-medium-10.jpg",
64     DEMO_IMAGE_DIR "gallery-medium-11.jpg",
65     DEMO_IMAGE_DIR "gallery-medium-12.jpg",
66     DEMO_IMAGE_DIR "gallery-medium-13.jpg",
67     DEMO_IMAGE_DIR "gallery-medium-14.jpg",
68     DEMO_IMAGE_DIR "gallery-medium-15.jpg",
69     DEMO_IMAGE_DIR "gallery-medium-16.jpg",
70     DEMO_IMAGE_DIR "gallery-medium-17.jpg",
71     DEMO_IMAGE_DIR "gallery-medium-18.jpg",
72     DEMO_IMAGE_DIR "gallery-medium-19.jpg",
73     DEMO_IMAGE_DIR "gallery-medium-20.jpg",
74     DEMO_IMAGE_DIR "gallery-medium-21.jpg",
75     DEMO_IMAGE_DIR "gallery-medium-22.jpg",
76     DEMO_IMAGE_DIR "gallery-medium-23.jpg",
77     DEMO_IMAGE_DIR "gallery-medium-24.jpg",
78     DEMO_IMAGE_DIR "gallery-medium-25.jpg",
79     DEMO_IMAGE_DIR "gallery-medium-26.jpg",
80     DEMO_IMAGE_DIR "gallery-medium-27.jpg",
81     DEMO_IMAGE_DIR "gallery-medium-28.jpg",
82     DEMO_IMAGE_DIR "gallery-medium-29.jpg",
83     DEMO_IMAGE_DIR "gallery-medium-30.jpg",
84     DEMO_IMAGE_DIR "gallery-medium-31.jpg",
85     DEMO_IMAGE_DIR "gallery-medium-32.jpg",
86     DEMO_IMAGE_DIR "gallery-medium-33.jpg",
87     DEMO_IMAGE_DIR "gallery-medium-34.jpg",
88     DEMO_IMAGE_DIR "gallery-medium-35.jpg",
89     DEMO_IMAGE_DIR "gallery-medium-36.jpg",
90     DEMO_IMAGE_DIR "gallery-medium-37.jpg",
91     DEMO_IMAGE_DIR "gallery-medium-38.jpg",
92     DEMO_IMAGE_DIR "gallery-medium-39.jpg",
93     DEMO_IMAGE_DIR "gallery-medium-40.jpg",
94     DEMO_IMAGE_DIR "gallery-medium-41.jpg",
95     DEMO_IMAGE_DIR "gallery-medium-42.jpg",
96     DEMO_IMAGE_DIR "gallery-medium-43.jpg",
97     DEMO_IMAGE_DIR "gallery-medium-44.jpg",
98     DEMO_IMAGE_DIR "gallery-medium-45.jpg",
99     DEMO_IMAGE_DIR "gallery-medium-46.jpg",
100     DEMO_IMAGE_DIR "gallery-medium-47.jpg",
101     DEMO_IMAGE_DIR "gallery-medium-48.jpg",
102     DEMO_IMAGE_DIR "gallery-medium-49.jpg",
103     DEMO_IMAGE_DIR "gallery-medium-50.jpg",
104     DEMO_IMAGE_DIR "gallery-medium-51.jpg",
105     DEMO_IMAGE_DIR "gallery-medium-52.jpg",
106     DEMO_IMAGE_DIR "gallery-medium-53.jpg",
107
108     NULL
109 };
110
111 const char * const GetNextImagePath()
112 {
113   static const char * const * imagePtr = &IMAGE_PATHS[0];
114
115   if ( *(++imagePtr) == NULL )
116   {
117     imagePtr = &IMAGE_PATHS[0];
118   }
119
120   return *imagePtr;
121 }
122
123 const int PAGE_COLUMNS = 10;                                                ///< Number of Pages going across (columns)
124 const int PAGE_ROWS = 1;                                                    ///< Number of Pages going down (rows)
125 const int IMAGE_ROWS = 5;                                                   ///< Number of Images going down (rows) with a Page
126
127 const unsigned int IMAGE_THUMBNAIL_WIDTH  = 256;                            ///< Width of Thumbnail Image in texels
128 const unsigned int IMAGE_THUMBNAIL_HEIGHT = 256;                            ///< Height of Thumbnail Image in texels
129
130 const float SPIN_DURATION = 1.0f;                                           ///< Times to spin an Image by upon touching, each spin taking a second.
131
132 const float EFFECT_SNAP_DURATION(0.66f);                                    ///< Scroll Snap Duration for Effects
133 const float EFFECT_FLICK_DURATION(0.5f);                                    ///< Scroll Flick Duration for Effects
134
135 } // unnamed namespace
136
137 /**
138  * This example shows how to do custom Scroll Effects
139  */
140 class ExampleController : public ConnectionTracker
141 {
142 public:
143
144   /**
145    * Constructor
146    * @param application class, stored as reference
147    */
148   ExampleController( Application& application )
149   : mApplication( application ),
150     mView(),
151     mScrolling(false),
152     mEffectMode(PageCarouselEffect)
153   {
154     // Connect to the Application's Init and orientation changed signal
155     mApplication.InitSignal().Connect(this, &ExampleController::OnInit);
156   }
157
158   ~ExampleController()
159   {
160     // Nothing to do here; everything gets deleted automatically
161   }
162
163   /**
164    * This method gets called once the main loop of application is up and running
165    */
166   void OnInit(Application& app)
167   {
168     Stage stage = Dali::Stage::GetCurrent();
169     stage.KeyEventSignal().Connect(this, &ExampleController::OnKeyEvent);
170
171     // Hide the indicator bar
172     mApplication.GetWindow().ShowIndicator(Dali::Window::INVISIBLE);
173
174     // Creates a default view with a default tool bar.
175     // The view is added to the stage.
176     mContentLayer = DemoHelper::CreateView( app,
177                                             mView,
178                                             mToolBar,
179                                             BACKGROUND_IMAGE,
180                                             TOOLBAR_IMAGE,
181                                             "" );
182
183     mEffectIcon[ PageCarouselEffect ] = EFFECT_CAROUSEL_IMAGE;
184     mEffectIconSelected[ PageCarouselEffect ] = EFFECT_CAROUSEL_IMAGE_SELECTED;
185     mEffectIcon[ PageCubeEffect ]     = EFFECT_CUBE_IMAGE;
186     mEffectIconSelected[ PageCubeEffect ]     = EFFECT_CUBE_IMAGE_SELECTED;
187     mEffectIcon[ PageSpiralEffect ]   = EFFECT_SPIRAL_IMAGE;
188     mEffectIconSelected[ PageSpiralEffect ]   = EFFECT_SPIRAL_IMAGE_SELECTED;
189     mEffectIcon[ PageWaveEffect ]     = EFFECT_WAVE_IMAGE;
190     mEffectIconSelected[ PageWaveEffect ]     = EFFECT_WAVE_IMAGE_SELECTED;
191
192     // Create a effect change button. (right of toolbar)
193     mEffectChangeButton = Toolkit::PushButton::New();
194     mEffectChangeButton.ClickedSignal().Connect( this, &ExampleController::OnEffectTouched );
195     mToolBar.AddControl( mEffectChangeButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalRight, DemoHelper::DEFAULT_MODE_SWITCH_PADDING  );
196
197     // Create the content layer.
198     AddContentLayer();
199
200     // Hack to force screen refresh.
201     Animation animation = Animation::New(1.0f);
202     animation.AnimateTo(Property(mContentLayer, Actor::Property::POSITION), Vector3::ZERO );
203     animation.Play();
204   }
205
206 private:
207
208   /**
209    * Adds content to the ContentLayer. This is everything we see
210    * excluding the toolbar at the top.
211    */
212   void AddContentLayer()
213   {
214     Stage stage = Stage::GetCurrent();
215     Vector2 stageSize = stage.GetSize();
216
217     mScrollView = ScrollView::New();
218     mScrollView.SetAnchorPoint(AnchorPoint::CENTER);
219     mScrollView.SetParentOrigin(ParentOrigin::CENTER);
220     mContentLayer.Add( mScrollView );
221     mScrollView.SetSize( stageSize );
222     mScrollView.SetAxisAutoLock( true );
223     mScrollView.SetAxisAutoLockGradient( 1.0f );
224
225     mScrollView.ScrollStartedSignal().Connect( this, &ExampleController::OnScrollStarted );
226     mScrollView.ScrollCompletedSignal().Connect( this, &ExampleController::OnScrollCompleted );
227
228     for(int row = 0;row<PAGE_ROWS;row++)
229     {
230       for(int column = 0;column<PAGE_COLUMNS;column++)
231       {
232         Actor page = CreatePage();
233
234         page.SetPosition( column * stageSize.x, row * stageSize.y );
235         mScrollView.Add( page );
236
237         mPages.push_back(page);
238       }
239     }
240
241     Update();
242   }
243
244   /**
245    * Updates the ScrollView and it's children based
246    * on the current effect.
247    */
248   void Update()
249   {
250     std::stringstream ss(APPLICATION_TITLE);
251     ss << APPLICATION_TITLE << ": " << EFFECT_MODE_NAME[mEffectMode];
252     SetTitle(ss.str());
253
254     mEffectChangeButton.SetProperty( Toolkit::DevelButton::Property::UNSELECTED_BACKGROUND_VISUAL, mEffectIcon[ mEffectMode ] );
255     mEffectChangeButton.SetProperty( Toolkit::DevelButton::Property::SELECTED_BACKGROUND_VISUAL, mEffectIconSelected[ mEffectMode ] );
256
257     // remove old Effect if exists.
258     if(mScrollViewEffect)
259     {
260       mScrollView.RemoveEffect(mScrollViewEffect);
261     }
262
263     // apply new Effect to ScrollView
264     ApplyEffectToScrollView();
265     unsigned int pageCount(0);
266     for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter)
267     {
268       Actor page = *pageIter;
269       ApplyEffectToPage( page, pageCount++ );
270     }
271   }
272
273   /**
274    * Creates a page using a source of images.
275    */
276   Actor CreatePage()
277   {
278     Actor page = Actor::New();
279     page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
280     page.SetParentOrigin( ParentOrigin::CENTER );
281     page.SetAnchorPoint( AnchorPoint::CENTER );
282
283     Stage stage = Stage::GetCurrent();
284     Vector2 stageSize = stage.GetSize();
285
286     const float margin = 10.0f;
287
288     // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
289     int imageColumns = round(IMAGE_ROWS * (stageSize.x / stage.GetDpi().x) / (stageSize.y / stage.GetDpi().y));
290     const Vector3 imageSize((stageSize.x / imageColumns) - margin, (stageSize.y / IMAGE_ROWS) - margin, 0.0f);
291
292     for(int row = 0;row<IMAGE_ROWS;row++)
293     {
294       for(int column = 0;column<imageColumns;column++)
295       {
296         ImageView image = CreateImage( GetNextImagePath(), imageSize.x, imageSize.y );
297
298         image.SetParentOrigin( ParentOrigin::CENTER );
299         image.SetAnchorPoint( AnchorPoint::CENTER );
300
301         Vector3 position( margin * 0.5f + (imageSize.x + margin) * column - stageSize.width * 0.5f,
302                          margin * 0.5f + (imageSize.y + margin) * row - stageSize.height * 0.5f,
303                           0.0f);
304         image.SetPosition( position + imageSize * 0.5f );
305         image.SetSize( imageSize );
306         page.Add(image);
307       }
308     }
309
310     return page;
311   }
312
313   /**
314    * [ScrollView]
315    * Applies effect to scrollView
316    */
317   void ApplyEffectToScrollView()
318   {
319     bool snap(true);
320
321     Stage stage = Stage::GetCurrent();
322     Vector2 stageSize = stage.GetSize();
323
324     RulerPtr rulerX = CreateRuler(snap ? stageSize.width : 0.0f);
325     RulerPtr rulerY = new DefaultRuler;
326     rulerX->SetDomain(RulerDomain(0.0f, stageSize.x * PAGE_COLUMNS, false));
327     rulerY->Disable();
328
329     Dali::Path path = Dali::Path::New();
330     Dali::Property::Array points;
331         points.Resize(3);
332     Dali::Property::Array controlPoints;
333         controlPoints.Resize(4);
334     Vector3 forward;
335     if( mEffectMode == PageCarouselEffect)
336     {
337
338       points[0] = Vector3( stageSize.x*0.75, 0.0f,  -stageSize.x*0.75f);
339       points[1] = Vector3( 0.0f, 0.0f, 0.0f );
340       points[2] = Vector3( -stageSize.x*0.75f, 0.0f,  -stageSize.x*0.75f);
341       path.SetProperty( Path::Property::POINTS, points );
342
343       controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, 0.0f );
344       controlPoints[1] = Vector3( stageSize.x*0.5f, 0.0f, 0.0f );
345       controlPoints[2] = Vector3(-stageSize.x*0.5f, 0.0f, 0.0f );
346       controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f, 0.0f );
347       path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
348
349       forward = Vector3::ZERO;
350     }
351     else if( mEffectMode == PageCubeEffect)
352     {
353       points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
354       points[1] = Vector3( 0.0f, 0.0f, 0.0f );
355       points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
356       path.SetProperty( Path::Property::POINTS, points );
357
358       controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
359       controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
360       controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
361       controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
362       path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
363
364       forward = Vector3(-1.0f,0.0f,0.0f);
365     }
366     else if( mEffectMode == PageSpiralEffect)
367     {
368       points[0] = Vector3( stageSize.x*0.5, 0.0f,  -stageSize.x*0.5f);
369       points[1] = Vector3( 0.0f, 0.0f, 0.0f );
370       points[2] = Vector3( -stageSize.x*0.5f, 0.0f, -stageSize.x*0.5f);
371       path.SetProperty( Path::Property::POINTS, points );
372
373       controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, 0.0f );
374       controlPoints[1] = Vector3( stageSize.x*0.5f, 0.0f, 0.0f );
375       controlPoints[2] = Vector3(-stageSize.x*0.5f, 0.0f, 0.0f );
376       controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f, 0.0f );
377       path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
378
379       forward = Vector3(-1.0f,0.0f,0.0f);
380     }
381     else if( mEffectMode == PageWaveEffect)
382     {
383       points[0] = Vector3( stageSize.x, 0.0f,  -stageSize.x);
384       points[1] = Vector3( 0.0f, 0.0f, 0.0f );
385       points[2] = Vector3( -stageSize.x, 0.0f, -stageSize.x);
386       path.SetProperty( Path::Property::POINTS, points );
387
388       controlPoints[0] = Vector3( 0.0f, 0.0f, -stageSize.x );
389       controlPoints[1] = Vector3( stageSize.x*0.5f, 0.0f, 0.0f );
390       controlPoints[2] = Vector3( -stageSize.x*0.5f, 0.0f, 0.0f);
391       controlPoints[3] = Vector3(0.0f, 0.0f,-stageSize.x  );
392       path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
393
394       forward = Vector3(-1.0f,0.0f,0.0f);
395     }
396
397     mScrollViewEffect = ScrollViewPagePathEffect::New(path, forward,Toolkit::ScrollView::Property::SCROLL_FINAL_X, Vector3(stageSize.x,stageSize.y,0.0f),PAGE_COLUMNS);
398     mScrollView.SetScrollSnapDuration(EFFECT_SNAP_DURATION);
399     mScrollView.SetScrollFlickDuration(EFFECT_FLICK_DURATION);
400     mScrollView.SetScrollSnapAlphaFunction(AlphaFunction::EASE_OUT);
401     mScrollView.SetScrollFlickAlphaFunction(AlphaFunction::EASE_OUT);
402     mScrollView.RemoveConstraintsFromChildren();
403
404     rulerX = CreateRuler(snap ? stageSize.width * 0.5f : 0.0f);
405     rulerX->SetDomain( RulerDomain( 0.0f, stageSize.x * 0.5f * PAGE_COLUMNS, false ) );
406
407     unsigned int currentPage = mScrollView.GetCurrentPage();
408     if( mScrollViewEffect )
409     {
410       mScrollView.ApplyEffect(mScrollViewEffect);
411     }
412
413     mScrollView.SetWrapMode( true );
414     mScrollView.SetRulerX( rulerX );
415     mScrollView.SetRulerY( rulerY );
416
417     mScrollView.ScrollTo( currentPage, 0.0f );
418   }
419
420   /**
421    * Creates a Ruler that snaps to a specified grid size.
422    * If that grid size is 0.0 then this ruler does not
423    * snap.
424    *
425    * @param[in] gridSize (optional) The grid size for the ruler,
426    * (Default = 0.0 i.e. no snapping)
427    * @return The ruler is returned.
428    */
429   RulerPtr CreateRuler(float gridSize = 0.0f)
430   {
431     if(gridSize <= Math::MACHINE_EPSILON_0)
432     {
433         return new DefaultRuler();
434     }
435     return new FixedRuler(gridSize);
436   }
437   // end switch
438   /**
439     * [Page]
440     * Applies effect to the pages within scroll view.
441     *
442     * @param[in] page The page Actor to apply effect to.
443     */
444    void ApplyEffectToPage(Actor page, unsigned int pageOrder )
445    {
446      page.RemoveConstraints();
447      page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
448
449      ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
450      effect.ApplyToPage( page, pageOrder );
451    }
452
453   /**
454    * Creates an Image (Helper)
455    *
456    * @param[in] filename the path of the image.
457    * @param[in] width the width of the image in texels
458    * @param[in] height the height of the image in texels.
459    */
460   ImageView CreateImage( const std::string& filename, int width = IMAGE_THUMBNAIL_WIDTH, int height = IMAGE_THUMBNAIL_HEIGHT )
461   {
462     ImageView actor = ImageView::New();
463     Property::Map map;
464     map[Visual::Property::TYPE] = Visual::IMAGE;
465     map[ImageVisual::Property::URL] = filename;
466     map[ImageVisual::Property::DESIRED_WIDTH] = width;
467     map[ImageVisual::Property::DESIRED_HEIGHT] = height;
468     map[ImageVisual::Property::FITTING_MODE] = FittingMode::SCALE_TO_FILL;
469     map[ImageVisual::Property::SAMPLING_MODE] = SamplingMode::BOX_THEN_LINEAR;
470     actor.SetProperty( ImageView::Property::IMAGE, map );
471
472     actor.SetName( filename );
473     actor.SetParentOrigin(ParentOrigin::CENTER);
474     actor.SetAnchorPoint(AnchorPoint::CENTER);
475
476     actor.TouchSignal().Connect( this, &ExampleController::OnTouchImage );
477     return actor;
478   }
479
480   /**
481    * When scroll starts (i.e. user starts to drag scrollview),
482    * note this state (mScrolling = true)
483    * @param[in] position Current Scroll Position
484    */
485   void OnScrollStarted( const Vector2& position )
486   {
487     mScrolling = true;
488   }
489
490   /**
491    * When scroll starts (i.e. user stops dragging scrollview, and scrollview has snapped to destination),
492    * note this state (mScrolling = false)
493    * @param[in] position Current Scroll Position
494    */
495   void OnScrollCompleted( const Vector2& position )
496   {
497     mScrolling = false;
498   }
499
500   /**
501    * Upon Touching an image (Release), make it spin
502    * (provided we're not scrolling).
503    * @param[in] actor The actor touched
504    * @param[in] event The touch information.
505    */
506   bool OnTouchImage( Actor actor, const TouchData& event )
507   {
508     if( (event.GetPointCount() > 0) && (!mScrolling) )
509     {
510       if( event.GetState( 0 ) == PointState::UP )
511       {
512         // Spin the Image a few times.
513         Animation animation = Animation::New(SPIN_DURATION);
514         animation.AnimateBy( Property( actor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree(360.0f * SPIN_DURATION) ), Vector3::XAXIS ), AlphaFunction::EASE_OUT );
515         animation.Play();
516       }
517     }
518     return false;
519   }
520
521   /**
522    * Signal handler, called when the 'Effect' button has been touched.
523    *
524    * @param[in] button The button that was pressed.
525    */
526   bool OnEffectTouched(Button button)
527   {
528     mEffectMode = static_cast<EffectMode>((static_cast<int>(mEffectMode) + 1) % static_cast<int>(Total));
529     Update();
530     return true;
531   }
532
533   /**
534    * Sets/Updates the title of the View
535    * @param[in] title The new title for the view.
536    */
537   void SetTitle(const std::string& title)
538   {
539     if(!mTitleActor)
540     {
541       mTitleActor = DemoHelper::CreateToolBarLabel( "" );
542       // Add title to the tool bar.
543       mToolBar.AddControl( mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Alignment::HorizontalCenter );
544     }
545
546     mTitleActor.SetProperty( Toolkit::TextLabel::Property::TEXT, title );
547   }
548
549   /**
550    * Main key event handler
551    */
552   void OnKeyEvent(const KeyEvent& event)
553   {
554     if(event.state == KeyEvent::Down)
555     {
556       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
557       {
558         mApplication.Quit();
559       }
560     }
561   }
562
563 private:
564
565   Application& mApplication;                            ///< Application instance
566   Toolkit::Control mView;                               ///< The View instance.
567   Toolkit::ToolBar mToolBar;                            ///< The View's Toolbar.
568   TextLabel mTitleActor;                                ///< The Toolbar's Title.
569   Layer mContentLayer;                                  ///< The content layer (contains game actors)
570   ScrollView mScrollView;                               ///< ScrollView UI Component
571   bool mScrolling;                                      ///< ScrollView scrolling state (true = scrolling, false = stationary)
572   ScrollViewEffect mScrollViewEffect;                   ///< ScrollView Effect instance.
573   std::vector< Actor > mPages;                          ///< Keeps track of all the pages for applying effects.
574
575   /**
576    * Enumeration of different effects this scrollview can operate under.
577    */
578   enum EffectMode
579   {
580     PageCarouselEffect,                                 ///< Page carousel effect
581     PageCubeEffect,                                     ///< Page cube effect
582     PageSpiralEffect,                                   ///< Page spiral effect
583     PageWaveEffect,                                     ///< Page wave effect
584
585     Total
586   };
587
588   EffectMode mEffectMode;                               ///< Current Effect mode
589
590   std::string mEffectIcon[Total];                       ///< Icons for the effect button
591   std::string mEffectIconSelected[Total];               ///< Icons for the effect button when its selected
592   Toolkit::PushButton mEffectChangeButton;              ///< Effect Change Button
593 };
594
595 int DALI_EXPORT_API main(int argc, char **argv)
596 {
597   Application app = Application::New(&argc, &argv, DEMO_THEME_PATH);
598   ExampleController test(app);
599   app.MainLoop();
600   return 0;
601 }