Supply stylesheet using Application constructor
[platform/core/uifw/dali-demo.git] / examples / cluster / cluster-example.cpp
1 /*
2  * Copyright (c) 2014 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 #include <sstream>
19 #include <iomanip>
20
21 #include "shared/view.h"
22 #include <dali/dali.h>
23 #include <dali-toolkit/dali-toolkit.h>
24 #include <dali-toolkit/devel-api/shader-effects/motion-blur-effect.h>
25 #include <dali-toolkit/devel-api/controls/popup/popup.h>
26 #include <dali-toolkit/devel-api/shader-effects/carousel-effect.h>
27
28 #include "cluster.h"
29 #include "cluster-style.h"
30
31
32 using namespace Dali;
33 using namespace Dali::Demo;
34 using namespace Dali::Toolkit;
35 using namespace DemoHelper;
36
37 namespace // unnamed namespace
38 {
39
40 const char * const BACKGROUND_IMAGE( DALI_IMAGE_DIR "background-default.png" );
41 const char * const TOOLBAR_IMAGE( DALI_IMAGE_DIR "top-bar.png" );
42 const char * const APPLICATION_TITLE( "Clusters" );
43 const char * const LAYOUT_NONE_IMAGE( DALI_IMAGE_DIR "icon-cluster-none.png" );
44 const char * const LAYOUT_MOTION_BLUR_IMAGE( DALI_IMAGE_DIR "icon-cluster-wobble.png" );
45 const char * const LAYOUT_CAROUSEL_IMAGE( DALI_IMAGE_DIR "icon-cluster-carousel.png" );
46 const char * const LAYOUT_SPHERE_IMAGE( DALI_IMAGE_DIR "icon-cluster-sphere.png" );
47
48 enum ClusterType
49 {
50   PEOPLE,
51   TODAY,
52   PHONE,
53   PICTURES,
54   MUSIC,
55   MAGAZINE,
56
57   CLUSTER_COUNT
58 };
59
60 const char* PEOPLE_IMAGE_PATHS[] =   { DALI_IMAGE_DIR "people-medium-1.jpg",
61                                        DALI_IMAGE_DIR "people-medium-2.jpg",
62                                        DALI_IMAGE_DIR "people-medium-3.jpg",
63                                        DALI_IMAGE_DIR "people-medium-4.jpg",
64                                        DALI_IMAGE_DIR "people-medium-5.jpg",
65                                        DALI_IMAGE_DIR "people-medium-6.jpg",
66                                        DALI_IMAGE_DIR "people-medium-7.jpg",
67                                        DALI_IMAGE_DIR "people-medium-8.jpg",
68                                        NULL };
69
70 const char* TODAY_IMAGE_PATHS[] =    { DALI_IMAGE_DIR "gallery-medium-1.jpg",
71                                        DALI_IMAGE_DIR "gallery-medium-2.jpg",
72                                        DALI_IMAGE_DIR "gallery-medium-3.jpg",
73                                        DALI_IMAGE_DIR "gallery-medium-4.jpg",
74                                        DALI_IMAGE_DIR "gallery-medium-5.jpg",
75                                        DALI_IMAGE_DIR "gallery-medium-6.jpg",
76                                        NULL };
77
78 const char* PHONE_IMAGE_PATHS[] =    { DALI_IMAGE_DIR "gallery-medium-7.jpg",
79                                        DALI_IMAGE_DIR "gallery-medium-8.jpg",
80                                        DALI_IMAGE_DIR "gallery-medium-9.jpg",
81                                        DALI_IMAGE_DIR "gallery-medium-10.jpg",
82                                        DALI_IMAGE_DIR "gallery-medium-11.jpg",
83                                        DALI_IMAGE_DIR "gallery-medium-12.jpg",
84                                        NULL };
85
86 const char* PICTURES_IMAGE_PATHS[] = { DALI_IMAGE_DIR "gallery-medium-13.jpg",
87                                        DALI_IMAGE_DIR "gallery-medium-14.jpg",
88                                        DALI_IMAGE_DIR "gallery-medium-15.jpg",
89                                        DALI_IMAGE_DIR "gallery-medium-16.jpg",
90                                        DALI_IMAGE_DIR "gallery-medium-17.jpg",
91                                        DALI_IMAGE_DIR "gallery-medium-18.jpg",
92                                        NULL };
93
94 const char* MUSIC_IMAGE_PATHS[] =    { DALI_IMAGE_DIR "gallery-medium-19.jpg",
95                                        DALI_IMAGE_DIR "gallery-medium-20.jpg",
96                                        DALI_IMAGE_DIR "gallery-medium-21.jpg",
97                                        DALI_IMAGE_DIR "gallery-medium-22.jpg",
98                                        DALI_IMAGE_DIR "gallery-medium-23.jpg",
99                                        DALI_IMAGE_DIR "gallery-medium-24.jpg",
100                                        NULL };
101
102 const char* MAGAZINE_IMAGE_PATHS[] = { DALI_IMAGE_DIR "gallery-medium-25.jpg",
103                                        DALI_IMAGE_DIR "gallery-medium-26.jpg",
104                                        DALI_IMAGE_DIR "gallery-medium-27.jpg",
105                                        DALI_IMAGE_DIR "gallery-medium-28.jpg",
106                                        DALI_IMAGE_DIR "gallery-medium-29.jpg",
107                                        DALI_IMAGE_DIR "gallery-medium-30.jpg",
108                                        NULL };
109
110 const char **IMAGE_GROUPS[] = {PEOPLE_IMAGE_PATHS,
111                                TODAY_IMAGE_PATHS,
112                                PHONE_IMAGE_PATHS,
113                                PICTURES_IMAGE_PATHS,
114                                MUSIC_IMAGE_PATHS,
115                                MAGAZINE_IMAGE_PATHS,
116                                NULL};
117
118 const float CLUSTER_IMAGE_THUMBNAIL_WIDTH  = 256.0f;
119 const float CLUSTER_IMAGE_THUMBNAIL_HEIGHT = 256.0f;
120
121 const char* CLUSTER_SHADOW_IMAGE_PATH = DALI_IMAGE_DIR "cluster-image-shadow.png";
122 const char* CLUSTER_BORDER_IMAGE_PATH = DALI_IMAGE_DIR "cluster-image-frame.png";
123 const char* CLUSTER_BACKGROUND_IMAGE_PATH = DALI_IMAGE_DIR "cluster-background.png";
124
125 const float CLUSTER_IMAGE_BORDER_INDENT = 14.0f;            ///< Indent of border in pixels.
126 const float CLUSTER_IMAGE_BORDER_WIDTH = 128;               ///< Width of border in pixels.
127 const float CLUSTER_IMAGE_BORDER_HEIGHT = 128;              ///< Height of border in pixels.
128
129 const Vector4 CLUSTER_IMAGE_BORDER_ABSOLUTE( 16.0f, 16.0f, 16.0f, 16.0f ); // Border dimensions in absolute pixel coordinates.
130
131 // These values depend on the border image
132 const float CLUSTER_RELATIVE_SIZE = 0.65f;                  ///< Cluster size relative to screen width
133
134 const float CLUSTER_GROUP_DELAY_TOP = 0.25f;                ///< Delay for top Clusters in seconds.
135 const float CLUSTER_GROUP_DELAY_BOTTOM = 0.0f;              ///< Delay for bottom Clusters in seconds.
136
137 const float CLUSTER_COLUMN_INDENT = 0.1f;                   ///< Left Indentation in screen coordinates.
138 const float CLUSTER_ROW_INDENT = 0.13f;                     ///< Top Indentation in screen coordinates.
139
140 const float UI_MARGIN = 10.0f;                              ///< Screen Margin for placement of UI buttons
141
142 const float CAROUSEL_EFFECT_RADIUS = 500.0f;                ///< In Carousel Effect mode: Radius of carousel (Z peak depth)
143 const float CAROUSEL_EFFECT_ANGLE_SWEEP = 90.0f;            ///< In Carousel Effect mode: Angle sweep from left to right of screen
144 const float SPHERE_EFFECT_RADIUS = 1000.0f;                 ///< In Sphere Effect mode: Radius of sphere carousel (Z peak depth)
145 const float SPHERE_EFFECT_POSITION_Z = -700.0f;             ///< In Sphere Effect mode: Z position alter (as carousel is coming out to screen we move back)
146 const float SPHERE_EFFECT_ANGLE_SWEEP = 90.0f;              ///< In Sphere Effect mode: Angle sweep from edge to opposite side of circle.
147 const float SPHERE_EFFECT_VERTICAL_DOMAIN = 0.15f;          ///< In Sphere Effect mode: How much the user can pan in the vertical axis. (in stageHeights)
148
149 /**
150  * List of effect types that user can select through.
151  */
152 enum ExampleEffectType
153 {
154   NO_EFFECT,
155   MOTION_BLUR_EFFECT,
156   CAROUSEL_EFFECT,
157   SPHERE_EFFECT,
158   TOTAL_EFFECTS
159 };
160
161 /**
162  * List of effect type names that appear on the Effect button.
163  */
164 const char* EXAMPLE_EFFECT_LABEL[] = { "None",
165                                        "Motion Blur",
166                                        "Carousel",
167                                        "Sphere",
168                                      };
169
170 /**
171  * CarouselEffectOrientationConstraint
172  * Based on the View orientation i.e. portrait (0 degrees), landscape (90 degrees) etc.
173  * carousel shader effect should bend differently (as a function of this orientation),
174  * as shader effect is applied to the screen coordinates.
175  */
176 struct CarouselEffectOrientationConstraint
177 {
178   /**
179    * Constructor
180    * @param[in] angleSweep The amount of degrees to rotate by per pixel.
181    */
182   CarouselEffectOrientationConstraint( const Vector2 angleSweep )
183   : mAngleSweep( angleSweep )
184   {
185   }
186
187   /**
188    * @param[in] current The object's current property value
189    * @return The object's new property value
190    */
191   void operator()( Vector2& current, const PropertyInputContainer& inputs )
192   {
193     Vector3 axis;
194     Radian angle;
195     inputs[0]->GetQuaternion().ToAxisAngle( axis, angle );
196
197     current.x = cosf(angle);
198     current.y = sinf(angle);
199
200     current *= mAngleSweep;
201   }
202
203   Vector2 mAngleSweep;
204
205 };
206
207 /**
208  * SphereEffectOffsetConstraint
209  *
210  * Sets SphereEffect's center to be a function of the
211  * screen orientation (portrait or landscape).
212  */
213 struct SphereEffectOffsetConstraint
214 {
215   /**
216    * @param[in] stageSize The stage size (not subject to orientation)
217    * @param[in] center Shear Center position based on initial orientation.
218    */
219   SphereEffectOffsetConstraint(float offset)
220   : mOffset(offset)
221   {
222   }
223
224   /**
225    * @param[in] current The current center
226    * @param[in] propertyViewSize The current view size
227    * @return vector to provide SphereShaderEffect
228    */
229   void operator()( float& current, const PropertyInputContainer& /* inputs */ )
230   {
231     current += mOffset;
232   }
233
234   float mOffset;
235 };
236
237 /**
238  * ClusterInfo struct
239  *
240  * Contains information about each cluster in mClusterInfo list.
241  */
242 struct ClusterInfo
243 {
244   /**
245    * Constructor
246    *
247    * @param[in] cluster The cluster instance
248    * @param[in] index The cluster's index (starting from 0 for the first cluster)
249    * @param[in] position The cluster's original position
250    * @param[in] size The cluster's original size
251    */
252   ClusterInfo(Cluster cluster, int index, const Vector3& position, const Vector3& size)
253   : mCluster(cluster),
254     mIndex(index),
255     mPosition(position),
256     mSize(size)
257   {
258
259   }
260
261   /**
262    * Copy constructor
263    *
264    * @param[in] rhs The ClusterInfo struct to be copied.
265    */
266   ClusterInfo( const ClusterInfo& rhs )
267   : mCluster(rhs.mCluster),
268     mIndex(rhs.mIndex),
269     mPosition(rhs.mPosition),
270     mSize(rhs.mSize)
271   {
272
273   }
274
275   /**
276    * Assignment operator
277    */
278   ClusterInfo& operator=( const ClusterInfo& rhs )
279   {
280     if( this != &rhs )
281     {
282       mCluster = rhs.mCluster;
283       mIndex = rhs.mIndex;
284       mPosition = rhs.mPosition;
285       mSize = rhs.mSize;
286     }
287     return *this;
288   }
289
290
291   Cluster mCluster;             ///< Cluster instance
292   int mIndex;                   ///< Cluster index
293   Vector3 mPosition;            ///< Cluster original position
294   Vector3 mSize;                ///< Cluster original size
295   Constraint mEffectConstraint; ///< Cluster constraint
296 };
297
298 /**
299  * Shrinks Actor based on parent's aspect ratio.
300  */
301 struct ShrinkConstraint
302 {
303   /**
304    * Constructor
305    */
306   ShrinkConstraint()
307   {
308   }
309
310   /**
311    * @param[in] current The object's current scale value
312    * @param[in] parentScale The parent's scale
313    * @return The object's new scale value
314    */
315   Vector3 operator()(const Vector3&    current,
316                      const PropertyInput& parentScale)
317   {
318     return Vector3( parentScale.GetVector3().x / parentScale.GetVector3().y, 1.0f, 1.0f );
319   }
320 };
321
322 } // unnamed namespace
323
324 /**
325  * Custom position and size of shadow image
326  */
327 namespace ShadowProperty
328 {
329 const Vector3 ANCHOR_POINT      (0.54f, 0.6f, 0.5f);
330 const Vector3 RELATIVE_POSITION (0.0f, 0.0f, -0.1f);
331 const Vector3 SIZE_SCALE        (1.4f, 1.4f, 1.0f);
332 }
333
334 // This example shows how to use Cluster UI control
335 //
336 class ClusterController : public ConnectionTracker
337 {
338 public:
339
340   /**
341    * Constructor
342    * @param application class, stored as reference
343    */
344   ClusterController(Application &app)
345   : mApplication(app),
346     mClusterCount(0),
347     mExampleEffect(NO_EFFECT)
348   {
349     // Connect to the Application's Init signal
350     app.InitSignal().Connect(this, &ClusterController::Create);
351   }
352
353   ~ClusterController()
354   {
355     // Nothing to do here; everything gets deleted automatically
356   }
357
358   /**
359    * This method gets called once the main loop of application is up and running
360    */
361   void Create(Application& application)
362   {
363     Stage::GetCurrent().KeyEventSignal().Connect(this, &ClusterController::OnKeyEvent);
364
365     // The Init signal is received once (only) during the Application lifetime
366
367     // Hide the indicator bar
368     application.GetWindow().ShowIndicator( Dali::Window::INVISIBLE );
369
370     // Creates a default view with a default tool bar.
371     // The view is added to the stage.
372     mContentLayer = DemoHelper::CreateView( application,
373                                             mView,
374                                             mToolBar,
375                                             BACKGROUND_IMAGE,
376                                             TOOLBAR_IMAGE,
377                                             "" );
378
379     // Create a effect toggle button. (right of toolbar)
380     mLayoutButtonImages[ NO_EFFECT ] = ResourceImage::New( LAYOUT_NONE_IMAGE );
381     mLayoutButtonImages[ MOTION_BLUR_EFFECT ] = ResourceImage::New( LAYOUT_MOTION_BLUR_IMAGE );
382     mLayoutButtonImages[ CAROUSEL_EFFECT ] = ResourceImage::New( LAYOUT_CAROUSEL_IMAGE );
383     mLayoutButtonImages[ SPHERE_EFFECT ] = ResourceImage::New( LAYOUT_SPHERE_IMAGE );
384
385     mLayoutButton = Toolkit::PushButton::New();
386     mLayoutButton.ClickedSignal().Connect( this, &ClusterController::OnEffectTouched );
387     mToolBar.AddControl( mLayoutButton, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarButtonPercentage, Toolkit::Alignment::HorizontalRight, DemoHelper::DEFAULT_MODE_SWITCH_PADDING  );
388
389     // create and setup the scroll view...
390     mScrollView = ScrollView::New();
391     mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
392
393     // anchor the scroll view from its center point to the middle of its parent
394     mScrollView.SetAnchorPoint(AnchorPoint::CENTER);
395     mScrollView.SetParentOrigin(ParentOrigin::CENTER);
396
397     // Scale ScrollView to fit parent (mContentLayer)
398     mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
399
400     // Add the scroll view to the content layer
401     mContentLayer.Add(mScrollView);
402
403     // Create the image border shared by all the cluster image actors
404     mClusterBorderImage = ResourceImage::New(CLUSTER_BORDER_IMAGE_PATH);
405
406     AddCluster( PEOPLE,   ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle1) );
407     AddCluster( TODAY,    ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle2) );
408     AddCluster( PHONE,    ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle3) );
409     AddCluster( PICTURES, ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle4) );
410     AddCluster( MUSIC,    ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle2) );
411     AddCluster( MAGAZINE, ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle3) );
412
413     SetEffect(MOTION_BLUR_EFFECT);
414   }
415
416   /**
417    * Helper to create the cluster actors
418    */
419   Cluster CreateClusterActor(ClusterType clusterType, ClusterStyle style, Vector3& clusterSize)
420   {
421     // Create the cluster actor with the given cluster style
422     Cluster clusterActor = Cluster::New(style);
423     clusterActor.SetParentOrigin(ParentOrigin::CENTER);
424     clusterActor.SetAnchorPoint(AnchorPoint::CENTER);
425
426     Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
427     float minStageDimension = std::min(stageSize.x, stageSize.y);
428
429     clusterSize.x = minStageDimension * CLUSTER_RELATIVE_SIZE;
430     clusterSize.y = minStageDimension * CLUSTER_RELATIVE_SIZE;
431
432     clusterActor.SetSize( clusterSize );
433
434     DALI_ASSERT_ALWAYS(clusterType < CLUSTER_COUNT);
435     const char **paths = IMAGE_GROUPS[clusterType];
436     DALI_ASSERT_ALWAYS(paths);
437
438     // Add a background image to the cluster, limiting the loaded size by
439     // fitting it inside a quarter of the stage area with the conservative BOX
440     // filter mode:
441     Image bg = ResourceImage::New( CLUSTER_BACKGROUND_IMAGE_PATH, Dali::ImageDimensions( stageSize.x * 0.5f, stageSize.y * 0.5f ), Dali::FittingMode::SHRINK_TO_FIT, Dali::SamplingMode::BOX );
442     ImageActor image = ImageActor::New(bg);
443     clusterActor.SetBackgroundImage(image);
444
445     // Add actors (pictures) as the children of the cluster
446     for (unsigned int i = 0; (i < style.GetMaximumNumberOfChildren()) && (*paths); i++, paths++)
447     {
448       clusterActor.AddChild(CreateClusterPictureActor(clusterType, *paths), i);
449     }
450
451     return clusterActor;
452   }
453
454   /**
455    * Helper to create the picture actors in the cluster
456    */
457   Actor CreateClusterPictureActor(ClusterType clusterType, const std::string& imagePath)
458   {
459     // Create a picture for this cluster image
460     // actor|->shadow
461     //      |->image
462     //      |->border
463     Actor actor = Actor::New();
464     actor.SetSize(CLUSTER_IMAGE_THUMBNAIL_WIDTH, CLUSTER_IMAGE_THUMBNAIL_HEIGHT);
465     actor.SetParentOrigin( ParentOrigin::CENTER );
466     actor.SetAnchorPoint( AnchorPoint::CENTER );
467
468     // Load the thumbnail at quarter of screen width or standard size if that is smaller:
469     Size stageQuarter = Stage::GetCurrent().GetSize() * 0.25f;
470     const ImageDimensions requestedDims = ImageDimensions( std::min( stageQuarter.x, CLUSTER_IMAGE_THUMBNAIL_WIDTH ), std::min( stageQuarter.y, CLUSTER_IMAGE_THUMBNAIL_HEIGHT ) );
471
472     // Add a shadow image child actor
473     Image shadowImage = ResourceImage::New( CLUSTER_SHADOW_IMAGE_PATH, requestedDims, Dali::FittingMode::SHRINK_TO_FIT, Dali::SamplingMode::BOX );
474     ImageActor shadowActor = ImageActor::New(shadowImage);
475
476     // Shadow is not exactly located on the center of the image, so it is moved to a little
477     // upper-left side of the image relatively using customised AnchorPoint.
478     shadowActor.SetParentOrigin(ShadowProperty::ANCHOR_POINT);
479     shadowActor.SetAnchorPoint(AnchorPoint::CENTER);
480     shadowActor.SetPosition(Vector3(0.0f, 0.0f, -1.0f));
481
482     // Apply size-relative mode to auto-size the image shadow
483     shadowActor.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
484     shadowActor.SetSizeModeFactor( ShadowProperty::SIZE_SCALE );
485     actor.Add( shadowActor );
486
487     // Add a picture image actor to actor (with equal size to the parent).
488     Image image = ResourceImage::New( imagePath, requestedDims, Dali::FittingMode::SHRINK_TO_FIT, Dali::SamplingMode::BOX );
489     ImageActor imageActor = ImageActor::New( image );
490     imageActor.SetParentOrigin( ParentOrigin::CENTER );
491     imageActor.SetAnchorPoint( AnchorPoint::CENTER );
492     imageActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
493     actor.Add( imageActor );
494
495     // Add a border image child actor (with a fixed size offset from parent).
496     ImageActor borderActor = ImageActor::New( mClusterBorderImage );
497     borderActor.SetParentOrigin( ParentOrigin::CENTER );
498     borderActor.SetAnchorPoint( AnchorPoint::CENTER );
499     borderActor.SetStyle( ImageActor::STYLE_NINE_PATCH );
500     borderActor.SetNinePatchBorder( CLUSTER_IMAGE_BORDER_ABSOLUTE );
501     borderActor.SetPosition( Vector3( 0.0f, 0.0f, 1.0f ) );
502     borderActor.SetResizePolicy( ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT, Dimension::ALL_DIMENSIONS );
503     borderActor.SetSizeModeFactor( Vector3( CLUSTER_IMAGE_BORDER_INDENT - 1.0f, CLUSTER_IMAGE_BORDER_INDENT - 1.0f, 0.0f ) * 2.0f );
504     actor.Add( borderActor );
505
506     return actor;
507   }
508
509
510   /**
511    * Adds a Cluster to the ScrollView
512    *
513    * @param[in] clusterType The type of cluster (determines the image content)
514    * @param[in] style The style to be used for this cluster.
515    */
516   void AddCluster(ClusterType clusterType, ClusterStyle style)
517   {
518     Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
519
520     int column = mClusterCount>>1;
521     int row = mClusterCount&1;
522
523     float minStageDimension = std::min(stageSize.x, stageSize.y);
524     float clusterRightShift = 1.0f - CLUSTER_COLUMN_INDENT * 2.0f;
525     Vector3 clusterPosition = Vector3(CLUSTER_COLUMN_INDENT * stageSize.width + row * (clusterRightShift * stageSize.width - minStageDimension * CLUSTER_RELATIVE_SIZE),
526                                       CLUSTER_ROW_INDENT * stageSize.height + row * (clusterRightShift * stageSize.height - minStageDimension * CLUSTER_RELATIVE_SIZE), 0.0f);
527
528     Actor pageView = Actor::New();
529     mScrollView.Add(pageView);
530     pageView.SetParentOrigin(ParentOrigin::CENTER);
531     pageView.SetPosition(Vector3(stageSize.width * column, 0.0f, 0.0f));
532     pageView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
533
534     // Create cluster actors and add them to scroll view
535     Vector3 clusterSize;
536     Cluster cluster = CreateClusterActor( clusterType, style, clusterSize );
537     cluster.SetParentOrigin(ParentOrigin::TOP_LEFT);
538     cluster.SetAnchorPoint(AnchorPoint::TOP_LEFT);
539     cluster.SetPosition( clusterPosition );
540
541     pageView.Add(cluster);
542
543     mClusterInfo.push_back( ClusterInfo( cluster, mClusterCount, clusterPosition, clusterSize ) );
544
545     mClusterCount++;
546   }
547
548   /**
549    * Sets motion blur effect to a cluster and all its children
550    *
551    * @param[in] actor Cluster control to which the effect will be applied
552    */
553   void SetMotionBlurEffect( Actor actor )
554   {
555     // only do something if the actor and effect are valid
556     if( actor )
557     {
558       // first remove from this actor
559       RenderableActor renderable = RenderableActor::DownCast( actor );
560       if( renderable )
561       {
562         MotionBlurEffect shaderEffect = MotionBlurEffect::New();
563         shaderEffect.SetSpeedScalingFactor(0.1f);
564
565         Dali::Property::Index uModelProperty = shaderEffect.GetPropertyIndex( "uModelLastFrame" );
566         Constraint constraint = Constraint::New<Matrix>( shaderEffect, uModelProperty, EqualToConstraint() );
567         constraint.AddSource( Source( actor , Actor::Property::WORLD_MATRIX ) );
568         constraint.Apply();
569         renderable.SetShaderEffect( shaderEffect );
570       }
571       // then all children recursively
572       const unsigned int count = actor.GetChildCount();
573       for( unsigned int index = 0; index < count; ++index )
574       {
575         Actor child( actor.GetChildAt( index ) );
576         SetMotionBlurEffect( child );
577       }
578     }
579   }
580
581   /**
582    * Resets ScrollView and Clusters settings
583    * to reflect the new ExampleEffectType
584    *
585    * TODO: Add animation transition to fade out of existing effect,
586    * and into new effect.
587    *
588    * @param[in] type The desired effect to switch to.
589    */
590   void SetEffect(ExampleEffectType type)
591   {
592     Vector2 stageSize(Dali::Stage::GetCurrent().GetSize());
593
594     mExampleEffect = type;
595
596     std::stringstream ss(APPLICATION_TITLE);
597     ss << APPLICATION_TITLE << ": " << EXAMPLE_EFFECT_LABEL[mExampleEffect];
598     SetTitle(ss.str());
599
600     // Set up default ruler settings (fixed in horizontal, disabled in vertical)
601     RulerPtr rulerX;
602     rulerX = new FixedRuler(stageSize.x);
603     int columns = (mClusterCount + 1) >> 1;
604     rulerX->SetDomain(RulerDomain(0.0f, stageSize.x * columns, true));
605     mScrollView.SetRulerX(rulerX);
606
607     RulerPtr rulerY = new DefaultRuler();
608     rulerY->Disable();
609     mScrollView.SetRulerY(rulerY);
610
611     mScrollView.SetActorAutoSnap(false);
612
613     // Remove all shader-effects from mScrollView and it's children (the clusters)
614     mScrollView.SetPosition(Vector3::ZERO);
615
616     mLayoutButton.SetBackgroundImage( mLayoutButtonImages[ type ] );
617
618     for( std::vector<ClusterInfo>::iterator i = mClusterInfo.begin(); i != mClusterInfo.end(); ++i )
619     {
620       Cluster cluster = i->mCluster;
621       RemoveShaderEffectRecursively( cluster );
622       if( i->mEffectConstraint )
623       {
624         i->mEffectConstraint.Remove();
625         i->mEffectConstraint.Reset();
626       }
627     }
628
629     // Apply new shader-effects.
630     // Move Y to origin incase we came from an effect where user could free pan in y axis.
631     const Vector2 currentScrollPosition(mScrollView.GetCurrentScrollPosition());
632     mScrollView.ScrollTo(Vector2(currentScrollPosition.x, 0.0f));
633
634     switch(type)
635     {
636       case NO_EFFECT:
637       {
638         break;
639       }
640
641       case MOTION_BLUR_EFFECT:
642       {
643         for( std::vector<ClusterInfo>::iterator i = mClusterInfo.begin(); i != mClusterInfo.end(); ++i )
644         {
645           SetMotionBlurEffect( i->mCluster );
646         }
647         break;
648       }
649
650       case CAROUSEL_EFFECT:
651       {
652         // Apply Carousel Shader Effect to scrollView
653         CarouselEffect shaderEffect = CarouselEffect::New();
654         shaderEffect.SetRadius( -CAROUSEL_EFFECT_RADIUS );
655         // dont apply shader effect to scrollview as it might override internal shaders for bounce effect etc
656         for( std::vector<ClusterInfo>::iterator i = mClusterInfo.begin(); i != mClusterInfo.end(); ++i )
657         {
658           Cluster cluster = i->mCluster;
659           SetShaderEffectRecursively( cluster, shaderEffect );
660         }
661         mScrollView.SetPosition( Vector3( 0.0f, 0.0f, CAROUSEL_EFFECT_RADIUS ) );
662
663         const Vector2 angleSweep( CAROUSEL_EFFECT_ANGLE_SWEEP / stageSize.width,
664                                   CAROUSEL_EFFECT_ANGLE_SWEEP / stageSize.width );
665
666         Property::Index anglePerUnit = shaderEffect.GetPropertyIndex( shaderEffect.GetAnglePerUnitPropertyName() );
667         Constraint constraint = Constraint::New<Vector2>( shaderEffect, anglePerUnit, CarouselEffectOrientationConstraint( angleSweep ) );
668         constraint.AddSource( Source(mView, Actor::Property::ORIENTATION) );
669         constraint.Apply();
670
671         break;
672       }
673
674       case SPHERE_EFFECT:
675       {
676         // Change ruler to free panning...
677         RulerPtr rulerX;
678         rulerX = new DefaultRuler();
679         int columns = (mClusterCount + 1) >> 1;
680         rulerX->SetDomain(RulerDomain(0.0f, stageSize.x * columns, true));
681         mScrollView.SetRulerX(rulerX);
682
683         RulerPtr rulerY = new DefaultRuler();
684         rulerY->SetDomain(RulerDomain( -stageSize.y * SPHERE_EFFECT_VERTICAL_DOMAIN, stageSize.y * (1.0f + SPHERE_EFFECT_VERTICAL_DOMAIN), true));
685         mScrollView.SetRulerY(rulerY);
686
687         // Apply Carousel Shader Effect to scrollView (Spherical style)
688         CarouselEffect shaderEffect = CarouselEffect::New();
689
690         shaderEffect.SetRadius( SPHERE_EFFECT_RADIUS );
691         shaderEffect.SetAnglePerUnit( Vector2( SPHERE_EFFECT_ANGLE_SWEEP / stageSize.y, SPHERE_EFFECT_ANGLE_SWEEP / stageSize.y ) );
692         // dont apply shader effect to scrollview as it might override internal shaders for bounce effect etc
693         for( std::vector<ClusterInfo>::iterator i = mClusterInfo.begin(); i != mClusterInfo.end(); ++i )
694         {
695           Cluster cluster = i->mCluster;
696           i->mEffectConstraint = Constraint::New<float>( cluster, Actor::Property::POSITION_Z, SphereEffectOffsetConstraint( SPHERE_EFFECT_POSITION_Z ) );
697           i->mEffectConstraint.SetRemoveAction(Constraint::Discard);
698           SetShaderEffectRecursively( cluster, shaderEffect );
699           i->mEffectConstraint.Apply();
700         }
701         break;
702       }
703
704       default:
705         break;
706     }
707
708   }
709
710   /**
711    * Signal handler, called when quit button is pressed
712    */
713   bool OnEffectTouched( Toolkit::Button button )
714   {
715     // quit the application
716     SetEffect(static_cast<ExampleEffectType>( (mExampleEffect + 1) % TOTAL_EFFECTS) );
717     return true;
718   }
719
720   /**
721    * Sets/Updates the title of the View
722    * @param[in] title The new title for the view.
723    */
724   void SetTitle(const std::string& title)
725   {
726     if(!mTitleActor)
727     {
728       mTitleActor = DemoHelper::CreateToolBarLabel( "" );
729
730       // Add title to the tool bar.
731       mToolBar.AddControl( mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Alignment::HorizontalCenter );
732     }
733
734     mTitleActor.SetProperty( TextLabel::Property::TEXT, title );
735   }
736
737   /**
738    * Main key event handler
739    */
740   void OnKeyEvent(const KeyEvent& event)
741   {
742     if(event.state == KeyEvent::Down)
743     {
744       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
745       {
746         mApplication.Quit();
747       }
748     }
749   }
750
751 private:
752
753   Application&               mApplication;                       ///< Application instance
754   Toolkit::Control           mView;                              ///< The View instance.
755   Toolkit::ToolBar           mToolBar;                           ///< The View's Toolbar.
756   TextLabel                  mTitleActor;                        ///< The Toolbar's Title.
757
758   Layer                      mContentLayer;                      ///< Content layer (scrolling cluster content)
759
760   ScrollView                 mScrollView;                        ///< The ScrollView container for all clusters
761   Image                      mClusterBorderImage;                ///< The border frame that appears on each image
762
763   std::vector<ClusterInfo>   mClusterInfo;                       ///< Keeps track of each cluster's information.
764   int                        mClusterCount;                      ///< Current number of clusters in use
765   ExampleEffectType          mExampleEffect;                     ///< Current example effect.
766
767   Toolkit::PushButton        mLayoutButton;                      ///< The layout button
768   Image                      mLayoutButtonImages[TOTAL_EFFECTS]; ///< Image when no layout
769 };
770
771 void RunTest(Application& app)
772 {
773   ClusterController test(app);
774
775   app.MainLoop();
776 }
777
778 // Entry point for Linux & Tizen applications
779 //
780 int main(int argc, char **argv)
781 {
782   Application app = Application::New(&argc, &argv, DALI_DEMO_THEME_PATH);
783
784   RunTest(app);
785
786   return 0;
787 }