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