Merge "Use pre-defined animatable property index in ScrollView" into tizen
[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.SetRelayoutEnabled( false );
481     mScrollView.SetSize(stageSize);
482
483     // attach Wobble Effect to ScrollView
484     mScrollViewEffect = ScrollViewWobbleEffect::New();
485     mScrollView.ApplyEffect(mScrollViewEffect);
486
487     // anchor the scroll view from its center point to the middle of its parent
488     mScrollView.SetAnchorPoint(AnchorPoint::CENTER);
489     mScrollView.SetParentOrigin(ParentOrigin::CENTER);
490
491     // Scale ScrollView to fit parent (mContentLayer)
492     mScrollView.SetRelayoutEnabled( true );
493     mScrollView.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
494
495     // Add the scroll view to the content layer
496     mContentLayer.Add(mScrollView);
497
498     // Create the image border shared by all the cluster image actors
499     mClusterBorderImage = ResourceImage::New(CLUSTER_BORDER_IMAGE_PATH);
500
501     AddCluster( PEOPLE,   ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle1) );
502     AddCluster( TODAY,    ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle2) );
503     AddCluster( PHONE,    ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle3) );
504     AddCluster( PICTURES, ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle4) );
505     AddCluster( MUSIC,    ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle2) );
506     AddCluster( MAGAZINE, ClusterStyleStandard::New(ClusterStyleStandard::ClusterStyle3) );
507
508     SetEffect(WOBBLE_EFFECT);
509   }
510
511   /**
512    * Helper to create the cluster actors
513    */
514   Cluster CreateClusterActor(ClusterType clusterType, ClusterStyle style)
515   {
516     // Create the cluster actor with the given cluster style
517     Cluster clusterActor = Cluster::New(style);
518     clusterActor.SetParentOrigin(ParentOrigin::CENTER);
519     clusterActor.SetAnchorPoint(AnchorPoint::CENTER);
520     clusterActor.SetRelayoutEnabled( false );
521
522     Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
523     float minStageDimension = std::min(stageSize.x, stageSize.y);
524     clusterActor.SetSize(minStageDimension * CLUSTER_RELATIVE_SIZE, minStageDimension * CLUSTER_RELATIVE_SIZE, 0.0f);
525
526     DALI_ASSERT_ALWAYS(clusterType < CLUSTER_COUNT);
527     const char **paths = IMAGE_GROUPS[clusterType];
528     DALI_ASSERT_ALWAYS(paths);
529
530     // Add a background image to the cluster, limiting the loaded size by
531     // fitting it inside a quarter of the stage area with the conservative Box
532     // filter mode:
533     Dali::ImageAttributes backgroundAttributes;
534     backgroundAttributes.SetSize( Stage::GetCurrent().GetSize() * 0.5f );
535     backgroundAttributes.SetFilterMode( Dali::ImageAttributes::Box );
536     backgroundAttributes.SetScalingMode( Dali::ImageAttributes::ShrinkToFit );
537     Image bg = ResourceImage::New( CLUSTER_BACKGROUND_IMAGE_PATH );
538     ImageActor image = ImageActor::New(bg);
539     image.SetRelayoutEnabled( false );
540     clusterActor.SetBackgroundImage(image);
541
542     // Add actors (pictures) as the children of the cluster
543     for (unsigned int i = 0; (i < style.GetMaximumNumberOfChildren()) && (*paths); i++, paths++)
544     {
545       clusterActor.AddChild(CreateClusterPictureActor(clusterType, *paths), i);
546     }
547
548     return clusterActor;
549   }
550
551   /**
552    * Helper to create the picture actors in the cluster
553    */
554   Actor CreateClusterPictureActor(ClusterType clusterType, const std::string& imagePath)
555   {
556     // Create a picture for this cluster image
557     // actor|->shadow
558     //      |->image
559     //      |->border
560     Actor actor = Actor::New();
561     actor.SetSize(CLUSTER_IMAGE_THUMBNAIL_WIDTH, CLUSTER_IMAGE_THUMBNAIL_HEIGHT);
562     actor.SetParentOrigin( ParentOrigin::CENTER );
563     actor.SetAnchorPoint( AnchorPoint::CENTER );
564
565     // Load the thumbnail at quarter of screen width or standard size if that is smaller:
566     ImageAttributes attribs = ImageAttributes::New();
567     Size stageQuarter = Stage::GetCurrent().GetSize() * 0.25f;
568     attribs.SetSize( std::min( stageQuarter.x, CLUSTER_IMAGE_THUMBNAIL_WIDTH), std::min( stageQuarter.y, CLUSTER_IMAGE_THUMBNAIL_HEIGHT ) );
569     attribs.SetFilterMode( Dali::ImageAttributes::BoxThenLinear );
570     attribs.SetScalingMode(Dali::ImageAttributes::ShrinkToFit );
571
572     // Add a shadow image child actor
573     Image shadowImage = ResourceImage::New( CLUSTER_SHADOW_IMAGE_PATH, attribs );
574     ImageActor shadowActor = ImageActor::New(shadowImage);
575
576     // Shadow is not exactly located on the center of the image, so it is moved to a little
577     // upper-left side of the image relatively using customised AnchorPoint.
578     shadowActor.SetParentOrigin(ShadowProperty::ANCHOR_POINT);
579     shadowActor.SetAnchorPoint(AnchorPoint::CENTER);
580     shadowActor.SetPosition(Vector3(0.0f, 0.0f, -1.0f));
581
582     // Apply size-relative mode to auto-size the image shadow
583     shadowActor.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
584     shadowActor.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
585     shadowActor.SetSizeModeFactor( ShadowProperty::SIZE_SCALE );
586     actor.Add( shadowActor );
587
588     // Add a picture image actor to actor (with equal size to the parent).
589     Image image = ResourceImage::New( imagePath, attribs );
590     ImageActor imageActor = ImageActor::New( image );
591     imageActor.SetParentOrigin( ParentOrigin::CENTER );
592     imageActor.SetAnchorPoint( AnchorPoint::CENTER );
593     imageActor.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
594     actor.Add( imageActor );
595
596     // Add a border image child actor (with a fixed size offset from parent).
597     ImageActor borderActor = ImageActor::New( mClusterBorderImage );
598     borderActor.SetParentOrigin( ParentOrigin::CENTER );
599     borderActor.SetAnchorPoint( AnchorPoint::CENTER );
600     borderActor.SetStyle( ImageActor::STYLE_NINE_PATCH );
601     borderActor.SetNinePatchBorder( CLUSTER_IMAGE_BORDER_ABSOLUTE );
602     borderActor.SetPosition( Vector3( 0.0f, 0.0f, 1.0f ) );
603     borderActor.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
604     borderActor.SetSizeMode( SIZE_FIXED_OFFSET_FROM_PARENT );
605     borderActor.SetSizeModeFactor( Vector3( CLUSTER_IMAGE_BORDER_INDENT - 1.0f, CLUSTER_IMAGE_BORDER_INDENT - 1.0f, 0.0f ) * 2.0f );
606     actor.Add( borderActor );
607
608     return actor;
609   }
610
611
612   /**
613    * Adds a Cluster to the ScrollView
614    *
615    * @param[in] clusterType The type of cluster (determines the image content)
616    * @param[in] style The style to be used for this cluster.
617    */
618   void AddCluster(ClusterType clusterType, ClusterStyle style)
619   {
620     Vector2 stageSize = Dali::Stage::GetCurrent().GetSize();
621
622     int column = mClusterCount>>1;
623     int row = mClusterCount&1;
624
625     float minStageDimension = std::min(stageSize.x, stageSize.y);
626     float clusterRightShift = 1.0f - CLUSTER_COLUMN_INDENT * 2.0f;
627     Vector3 clusterPosition = Vector3(CLUSTER_COLUMN_INDENT * stageSize.width + row * (clusterRightShift * stageSize.width - minStageDimension * CLUSTER_RELATIVE_SIZE),
628                                       CLUSTER_ROW_INDENT * stageSize.height + row * (clusterRightShift * stageSize.height - minStageDimension * CLUSTER_RELATIVE_SIZE), 0.0f);
629
630     Actor pageView = Actor::New();
631     mScrollView.Add(pageView);
632     pageView.SetParentOrigin(ParentOrigin::CENTER);
633     pageView.SetPosition(Vector3(stageSize.width * column, 0.0f, 0.0f));
634     pageView.SetRelayoutEnabled( true );
635     pageView.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
636
637     // Create cluster actors, add them to scroll view, and set the shear effect with the given center point.
638     Cluster cluster = CreateClusterActor(clusterType, style);
639     cluster.SetParentOrigin(ParentOrigin::TOP_LEFT);
640     cluster.SetAnchorPoint(AnchorPoint::TOP_LEFT);
641     cluster.SetPosition( clusterPosition );
642
643     pageView.Add(cluster);
644     Vector3 clusterSize = cluster.GetCurrentSize();
645
646     mClusterInfo.push_back(ClusterInfo( cluster, mClusterCount, clusterPosition, clusterSize ));
647
648     mClusterCount++;
649   }
650
651   /**
652    * Resets ScrollView and Clusters settings
653    * to reflect the new ExampleEffectType
654    *
655    * TODO: Add animation transition to fade out of existing effect,
656    * and into new effect.
657    *
658    * @param[in] type The desired effect to switch to.
659    */
660   void SetEffect(ExampleEffectType type)
661   {
662     Vector2 stageSize(Dali::Stage::GetCurrent().GetSize());
663
664     mExampleEffect = type;
665
666     std::stringstream ss(APPLICATION_TITLE);
667     ss << APPLICATION_TITLE << ": " << EXAMPLE_EFFECT_LABEL[mExampleEffect];
668     SetTitle(ss.str());
669
670     // Set up default ruler settings (fixed in horizontal, disabled in vertical)
671     RulerPtr rulerX;
672     rulerX = new FixedRuler(stageSize.x);
673     int columns = (mClusterCount + 1) >> 1;
674     rulerX->SetDomain(RulerDomain(0.0f, stageSize.x * columns, true));
675     mScrollView.SetRulerX(rulerX);
676
677     RulerPtr rulerY = new DefaultRuler();
678     rulerY->Disable();
679     mScrollView.SetRulerY(rulerY);
680
681     mScrollView.SetActorAutoSnap(false);
682
683     // Remove all shader-effects from mScrollView and it's children (the clusters)
684     mScrollView.SetPosition(Vector3::ZERO);
685
686     mLayoutButton.SetBackgroundImage( mLayoutButtonImages[ type ] );
687
688     for( std::vector<ClusterInfo>::iterator i = mClusterInfo.begin(); i != mClusterInfo.end(); ++i )
689     {
690       Cluster cluster = i->mCluster;
691       RemoveShaderEffectRecursively( cluster );
692       if( i->mEffectConstraint )
693       {
694         cluster.RemoveConstraint(i->mEffectConstraint);
695         i->mEffectConstraint.Reset();
696       }
697     }
698
699     // Apply new shader-effects.
700     // Move Y to origin incase we came from an effect where user could free pan in y axis.
701     const Vector3 currentScrollPosition(mScrollView.GetCurrentScrollPosition());
702     mScrollView.ScrollTo(Vector3(currentScrollPosition.x, 0.0f, 0.0f));
703
704     switch(type)
705     {
706       case NO_EFFECT:
707       {
708         break;
709       }
710
711       case WOBBLE_EFFECT:
712       {
713         for( std::vector<ClusterInfo>::iterator i = mClusterInfo.begin(); i != mClusterInfo.end(); ++i )
714         {
715           Cluster cluster = i->mCluster;
716           Vector3 position = i->mPosition;
717           Vector3 size = i->mSize;
718
719           ShearEffect shaderEffect = ShearEffect::New();
720           Vector3 shearAnchor = SHEAR_EFFECT_ANCHOR_POINT;
721
722           Vector2 shearCenter( Vector2(position.x + size.width * shearAnchor.x, position.y + size.height * shearAnchor.y) );
723           Property::Index centerProperty = shaderEffect.GetPropertyIndex(shaderEffect.GetCenterPropertyName());
724           Constraint constraint = Constraint::New<Vector2>( centerProperty,
725                                                             Source(mView, Actor::Property::SIZE),
726                                                             ShearEffectCenterConstraint(stageSize, shearCenter) );
727           shaderEffect.ApplyConstraint(constraint);
728
729           SetShaderEffectRecursively( cluster,shaderEffect );
730
731           // Apply Constraint to Shader Effect
732           Property::Index scrollOvershootProperty = /*targetGroup*/mScrollView.GetPropertyIndex(ScrollViewWobbleEffect::EFFECT_OVERSHOOT);
733           Property::Index angleXAxisProperty = shaderEffect.GetPropertyIndex(shaderEffect.GetAngleXAxisPropertyName());
734           Property::Index angleYAxisProperty = shaderEffect.GetPropertyIndex(shaderEffect.GetAngleYAxisPropertyName());
735
736           constraint = Constraint::New<float>( angleXAxisProperty,
737                                                Source(mScrollView, scrollOvershootProperty),
738                                                Source(mView, Actor::Property::ORIENTATION),
739                                                ShearEffectConstraint(stageSize, SHEAR_EFFECT_MAX_OVERSHOOT, Vector2::XAXIS) );
740           shaderEffect.ApplyConstraint(constraint);
741           constraint = Constraint::New<float>( angleYAxisProperty,
742                                                Source(mScrollView, scrollOvershootProperty),
743                                                Source(mView, Actor::Property::ORIENTATION),
744                                                ShearEffectConstraint(stageSize, SHEAR_EFFECT_MAX_OVERSHOOT, Vector2::YAXIS) );
745           shaderEffect.ApplyConstraint(constraint);
746
747
748         }
749         break;
750       }
751
752       case CAROUSEL_EFFECT:
753       {
754         // Apply Carousel Shader Effect to scrollView
755         CarouselEffect shaderEffect = CarouselEffect::New();
756         shaderEffect.SetRadius( -CAROUSEL_EFFECT_RADIUS );
757         // dont apply shader effect to scrollview as it might override internal shaders for bounce effect etc
758         for( std::vector<ClusterInfo>::iterator i = mClusterInfo.begin(); i != mClusterInfo.end(); ++i )
759         {
760           Cluster cluster = i->mCluster;
761           SetShaderEffectRecursively( cluster, shaderEffect );
762         }
763         mScrollView.SetPosition( Vector3( 0.0f, 0.0f, CAROUSEL_EFFECT_RADIUS ) );
764
765         const Vector2 angleSweep( CAROUSEL_EFFECT_ANGLE_SWEEP / stageSize.width,
766                                   CAROUSEL_EFFECT_ANGLE_SWEEP / stageSize.width );
767
768         Property::Index anglePerUnit = shaderEffect.GetPropertyIndex( shaderEffect.GetAnglePerUnitPropertyName() );
769         shaderEffect.ApplyConstraint( Constraint::New<Vector2>( anglePerUnit,
770                                                                 Source(mView, Actor::Property::ORIENTATION),
771                                                                 CarouselEffectOrientationConstraint( angleSweep ) ) );
772
773         break;
774       }
775
776       case SPHERE_EFFECT:
777       {
778         // Change ruler to free panning...
779         RulerPtr rulerX;
780         rulerX = new DefaultRuler();
781         int columns = (mClusterCount + 1) >> 1;
782         rulerX->SetDomain(RulerDomain(0.0f, stageSize.x * columns, true));
783         mScrollView.SetRulerX(rulerX);
784
785         RulerPtr rulerY = new DefaultRuler();
786         rulerY->SetDomain(RulerDomain( -stageSize.y * SPHERE_EFFECT_VERTICAL_DOMAIN, stageSize.y * (1.0f + SPHERE_EFFECT_VERTICAL_DOMAIN), true));
787         mScrollView.SetRulerY(rulerY);
788
789         // Apply Carousel Shader Effect to scrollView (Spherical style)
790         CarouselEffect shaderEffect = CarouselEffect::New();
791
792         shaderEffect.SetRadius( SPHERE_EFFECT_RADIUS );
793         shaderEffect.SetAnglePerUnit( Vector2( SPHERE_EFFECT_ANGLE_SWEEP / stageSize.y, SPHERE_EFFECT_ANGLE_SWEEP / stageSize.y ) );
794         // dont apply shader effect to scrollview as it might override internal shaders for bounce effect etc
795         for( std::vector<ClusterInfo>::iterator i = mClusterInfo.begin(); i != mClusterInfo.end(); ++i )
796         {
797           Constraint constraint = Constraint::New<float>(Actor::Property::POSITION_Z, SphereEffectOffsetConstraint(SPHERE_EFFECT_POSITION_Z));
798           constraint.SetRemoveAction(Constraint::Discard);
799           Cluster cluster = i->mCluster;
800           SetShaderEffectRecursively( cluster, shaderEffect );
801           i->mEffectConstraint = cluster.ApplyConstraint(constraint);
802         }
803         break;
804       }
805
806       default:
807         break;
808     }
809
810   }
811
812   /**
813    * Signal handler, called when quit button is pressed
814    */
815   bool OnEffectTouched( Toolkit::Button button )
816   {
817     // quit the application
818     SetEffect(static_cast<ExampleEffectType>( (mExampleEffect + 1) % TOTAL_EFFECTS) );
819     return true;
820   }
821
822   /**
823    * Sets/Updates the title of the View
824    * @param[in] title The new title for the view.
825    */
826   void SetTitle(const std::string& title)
827   {
828     if(!mTitleActor)
829     {
830       mTitleActor = TextView::New();
831       // Add title to the tool bar.
832       mToolBar.AddControl( mTitleActor, DemoHelper::DEFAULT_VIEW_STYLE.mToolBarTitlePercentage, Alignment::HorizontalCenter );
833     }
834
835     Font font = Font::New();
836     mTitleActor.SetText( title );
837     mTitleActor.SetSize( font.MeasureText( title ) );
838     mTitleActor.SetStyleToCurrentText(DemoHelper::GetDefaultTextStyle());
839   }
840
841   /**
842    * Main key event handler
843    */
844   void OnKeyEvent(const KeyEvent& event)
845   {
846     if(event.state == KeyEvent::Down)
847     {
848       if( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
849       {
850         mApplication.Quit();
851       }
852     }
853   }
854
855 private:
856
857   Application&               mApplication;                       ///< Application instance
858   Toolkit::View              mView;                              ///< The View instance.
859   Toolkit::ToolBar           mToolBar;                           ///< The View's Toolbar.
860   TextView                   mTitleActor;                        ///< The Toolbar's Title.
861
862   Layer                      mContentLayer;                      ///< Content layer (scrolling cluster content)
863
864   ScrollView                 mScrollView;                        ///< The ScrollView container for all clusters
865   ScrollViewWobbleEffect     mScrollViewEffect;                  ///< ScrollView Wobble effect
866   Image                      mClusterBorderImage;                ///< The border frame that appears on each image
867
868   std::vector<ClusterInfo>   mClusterInfo;                       ///< Keeps track of each cluster's information.
869   int                        mClusterCount;                      ///< Current number of clusters in use
870   ExampleEffectType          mExampleEffect;                     ///< Current example effect.
871
872   Toolkit::PushButton        mLayoutButton;                      ///< The layout button
873   Image                      mLayoutButtonImages[TOTAL_EFFECTS]; ///< Image when no layout
874 };
875
876 void RunTest(Application& app)
877 {
878   ClusterController test(app);
879
880   app.MainLoop();
881 }
882
883 // Entry point for Linux & Tizen applications
884 //
885 int main(int argc, char **argv)
886 {
887   Application app = Application::New(&argc, &argv);
888
889   RunTest(app);
890
891   return 0;
892 }