Removed NOOP page position setters
[platform/core/uifw/dali-demo.git] / demo / dali-table-view.cpp
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "dali-table-view.h"
20
21 // EXTERNAL INCLUDES
22 #include <algorithm>
23 #include <sstream>
24 #include <unistd.h>
25 #include <dali/devel-api/images/distance-field.h>
26 #include <dali-toolkit/devel-api/shader-effects/alpha-discard-effect.h>
27 #include <dali-toolkit/devel-api/shader-effects/distance-field-effect.h>
28 #include <dali-toolkit/dali-toolkit.h>
29 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
30
31 // INTERNAL INCLUDES
32 #include "shared/view.h"
33 #include "shared/utility.h"
34
35 using namespace Dali;
36 using namespace Dali::Toolkit;
37
38 ///////////////////////////////////////////////////////////////////////////////
39
40 namespace
41 {
42
43 const std::string LOGO_PATH( DEMO_IMAGE_DIR "Logo-for-demo.png" );
44 const std::string TILE_BACKGROUND(DEMO_IMAGE_DIR "item-background.9.png");
45 const std::string TILE_BACKGROUND_ALPHA( DEMO_IMAGE_DIR "demo-tile-texture.9.png" );
46 const std::string TILE_FOCUS( DEMO_IMAGE_DIR "tile-focus.9.png" );
47
48 // Keyboard focus effect constants.
49 const float KEYBOARD_FOCUS_ANIMATION_DURATION = 1.0f;           ///< The total duration of the keyboard focus animation
50 const float KEYBOARD_FOCUS_START_SCALE = 1.05f;                 ///< The starting scale of the focus highlight
51 const float KEYBOARD_FOCUS_END_SCALE = 1.18f;                   ///< The end scale of the focus highlight
52 const float KEYBOARD_FOCUS_END_ALPHA = 0.7f;                    ///< The end alpha of the focus highlight
53 const float KEYBOARD_FOCUS_INITIAL_FADE_PERCENTAGE = 0.16f;     ///< The duration of the initial fade (from translucent to the end-alpha) as a percentage of the overal animation duration.
54 const Vector3 startScale( KEYBOARD_FOCUS_START_SCALE, KEYBOARD_FOCUS_START_SCALE, KEYBOARD_FOCUS_START_SCALE ); ///< @see KEYBOARD_FOCUS_START_SCALE
55 const Vector3 endScale( KEYBOARD_FOCUS_END_SCALE, KEYBOARD_FOCUS_END_SCALE, KEYBOARD_FOCUS_END_SCALE );         ///< @see KEYBOARD_FOCUS_END_SCALE
56 const float initialFadeDuration = KEYBOARD_FOCUS_ANIMATION_DURATION * KEYBOARD_FOCUS_INITIAL_FADE_PERCENTAGE;   ///< @see KEYBOARD_FOCUS_INITIAL_FADE_PERCENTAGE
57
58 const float TILE_LABEL_PADDING = 8.0f;                          ///< Border between edge of tile and the example text
59 const float BUTTON_PRESS_ANIMATION_TIME = 0.35f;                ///< Time to perform button scale effect.
60 const float ROTATE_ANIMATION_TIME = 0.5f;                       ///< Time to perform rotate effect.
61 const int MAX_PAGES = 256;                                      ///< Maximum pages (arbitrary safety limit)
62 const int EXAMPLES_PER_ROW = 3;
63 const int ROWS_PER_PAGE = 3;
64 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
65 const float LOGO_MARGIN_RATIO = 0.1f / 0.3f;
66 const float BOTTOM_PADDING_RATIO = 0.4f / 0.9f;
67 const Vector3 SCROLLVIEW_RELATIVE_SIZE(0.9f, 1.0f, 0.8f );      ///< ScrollView's relative size to its parent
68 const Vector3 TABLE_RELATIVE_SIZE(0.95f, 0.9f, 0.8f );          ///< TableView's relative size to the entire stage. The Y value means sum of the logo and table relative heights.
69 const float STENCIL_RELATIVE_SIZE = 1.0f;
70
71 const float EFFECT_SNAP_DURATION = 0.66f;                       ///< Scroll Snap Duration for Effects
72 const float EFFECT_FLICK_DURATION = 0.5f;                       ///< Scroll Flick Duration for Effects
73 const Vector3 ANGLE_CUBE_PAGE_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
74 const Vector4 TILE_COLOR( 0.4f, 0.6f, 0.9f, 0.6f );
75
76 const Vector4 BUBBLE_COLOR[] =
77 {
78   Vector4( 0.3255f, 0.3412f, 0.6353f, 0.32f ),
79   Vector4( 0.3647f, 0.7569f, 0.8157f, 0.32f ),
80   Vector4( 0.3804f, 0.7412f, 0.6510f, 0.32f ),
81   Vector4( 1.f, 1.f, 1.f, 0.13f )
82 };
83 const int NUMBER_OF_BUBBLE_COLOR( sizeof(BUBBLE_COLOR) / sizeof(BUBBLE_COLOR[0]) );
84
85 const char * const SHAPE_IMAGE_TABLE[] =
86 {
87   DEMO_IMAGE_DIR "shape-circle.png",
88   DEMO_IMAGE_DIR "shape-bubble.png"
89 };
90 const int NUMBER_OF_SHAPE_IMAGES( sizeof( SHAPE_IMAGE_TABLE ) / sizeof( SHAPE_IMAGE_TABLE[0] ) );
91
92 const int NUM_BACKGROUND_IMAGES = 18;
93 const float BACKGROUND_SWIPE_SCALE = 0.025f;
94 const float BACKGROUND_SPREAD_SCALE = 1.5f;
95 const float SCALE_MOD = 1000.0f * Math::PI * 2.0f;
96 const float SCALE_SPEED = 10.0f;
97 const float SCALE_SPEED_SIN = 0.1f;
98
99 const unsigned int BACKGROUND_ANIMATION_DURATION = 15000; // 15 secs
100
101 const Vector4 BACKGROUND_COLOR( 0.3569f, 0.5451f, 0.7294f, 1.0f );
102
103 const float BUBBLE_MIN_Z = -1.0;
104 const float BUBBLE_MAX_Z = 0.0f;
105
106 // This shader takes a texture.
107 // An alpha discard is performed.
108 // The shader uses the tiles position within the scroll-view page and the scroll-views rotation position to create a parallax effect.
109 const char* FRAGMENT_SHADER_TEXTURED = DALI_COMPOSE_SHADER(
110   varying mediump vec2  vTexCoord;
111   varying mediump vec3  vIllumination;
112   uniform lowp    vec4  uColor;
113   uniform sampler2D     sTexture;
114   uniform mediump vec3  uCustomPosition;
115
116   void main()
117   {
118     if( texture2D( sTexture, vTexCoord ).a <= 0.0001 )
119     {
120       discard;
121     }
122
123     mediump vec2 wrapTexCoord = vec2( ( vTexCoord.x / 4.0 ) + ( uCustomPosition.x / 4.0 ) + ( uCustomPosition.z / 2.0 ), vTexCoord.y / 4.0 );
124     mediump vec4 color = texture2D( sTexture, wrapTexCoord );
125     mediump float positionWeight = ( uCustomPosition.y + 0.3 ) * color.r * 2.0;
126
127     gl_FragColor = vec4( positionWeight, positionWeight, positionWeight, 0.9 ) * uColor + vec4( uColor.xyz, 0.0 );
128   }
129 );
130
131 /**
132  * Creates the background image
133  */
134 Control CreateBackground( std::string stylename )
135 {
136   Control background = Control::New();
137   Stage::GetCurrent().Add( background );
138   background.SetStyleName( stylename );
139   background.SetName( "BACKGROUND" );
140   background.SetAnchorPoint( AnchorPoint::CENTER );
141   background.SetParentOrigin( ParentOrigin::CENTER );
142   background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
143   return background;
144 }
145
146 /**
147  * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
148  */
149 struct AnimateBubbleConstraint
150 {
151 public:
152   AnimateBubbleConstraint( const Vector3& initialPos, float scale )
153   : mInitialX( initialPos.x ),
154     mScale( scale )
155   {
156   }
157
158   void operator()( Vector3& position, const PropertyInputContainer& inputs )
159   {
160     const Vector3& parentSize = inputs[1]->GetVector3();
161     const Vector3& childSize = inputs[2]->GetVector3();
162
163     // Wrap bubbles vertically.
164     float range = parentSize.y + childSize.y;
165     // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
166     position.y -= range * ( floor( position.y / range ) + 0.5f );
167
168     // Bubbles X position moves parallax to horizontal
169     // panning by a scale factor unique to each bubble.
170     position.x = mInitialX + ( inputs[0]->GetVector2().x * mScale );
171   }
172
173 private:
174   float mInitialX;
175   float mScale;
176 };
177
178 /**
179  * Constraint to precalculate values from the scroll-view
180  * and tile positions to pass to the tile shader.
181  */
182 struct TileShaderPositionConstraint
183 {
184   TileShaderPositionConstraint( float pageWidth, float tileXOffset )
185   : mPageWidth( pageWidth ),
186     mTileXOffset( tileXOffset )
187   {
188   }
189
190   void operator()( Vector3& position, const PropertyInputContainer& inputs )
191   {
192     // Set up position.x as the tiles X offset (0.0 -> 1.0).
193     position.x = mTileXOffset;
194     // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
195     position.z = 1.0f * ( -fmod( inputs[0]->GetVector2().x, mPageWidth ) / mPageWidth );
196     // Set up position.y as a rectified version of the scroll-views X offset.
197     // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
198     if( position.z > 0.5f )
199     {
200       position.y = 1.0f - position.z;
201     }
202     else
203     {
204       position.y = position.z;
205     }
206   }
207
208 private:
209   float mPageWidth;
210   float mTileXOffset;
211 };
212
213 bool CompareByTitle( const Example& lhs, const Example& rhs )
214 {
215   return lhs.title < rhs.title;
216 }
217
218 } // namespace
219
220 DaliTableView::DaliTableView( Application& application )
221 : mApplication( application ),
222   mRootActor(),
223   mRotateAnimation(),
224   mPressedAnimation(),
225   mScrollView(),
226   mScrollViewEffect(),
227   mScrollRulerX(),
228   mScrollRulerY(),
229   mPressedActor(),
230   mAnimationTimer(),
231   mLogoTapDetector(),
232   mVersionPopup(),
233   mPages(),
234   mBackgroundAnimations(),
235   mExampleList(),
236   mPageWidth( 0.0f ),
237   mTotalPages(),
238   mScrolling( false ),
239   mSortAlphabetically( false ),
240   mBackgroundAnimsPlaying( false )
241 {
242   application.InitSignal().Connect( this, &DaliTableView::Initialize );
243 }
244
245 DaliTableView::~DaliTableView()
246 {
247 }
248
249 void DaliTableView::AddExample( Example example )
250 {
251   mExampleList.push_back( example );
252 }
253
254 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
255 {
256   mSortAlphabetically = sortAlphabetically;
257 }
258
259 void DaliTableView::Initialize( Application& application )
260 {
261   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
262   const Vector2 stageSize = Stage::GetCurrent().GetSize();
263
264   // Background
265   mRootActor = CreateBackground( "LauncherBackground" );
266   Stage::GetCurrent().Add( mRootActor );
267
268   // Add logo
269   ImageView logo = CreateLogo( LOGO_PATH );
270   logo.SetName( "LOGO_IMAGE" );
271   logo.SetAnchorPoint( AnchorPoint::TOP_CENTER );
272   logo.SetParentOrigin( Vector3( 0.5f, 0.1f, 0.5f ) );
273   logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
274   // The logo should appear on top of everything.
275   logo.SetDrawMode( DrawMode::OVERLAY_2D );
276   mRootActor.Add( logo );
277
278   // Show version in a popup when log is tapped
279   mLogoTapDetector = TapGestureDetector::New();
280   mLogoTapDetector.Attach( logo );
281   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
282
283   // Scrollview occupying the majority of the screen
284   mScrollView = ScrollView::New();
285   mScrollView.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
286   mScrollView.SetParentOrigin( Vector3( 0.5f, 1.0f - 0.05f, 0.5f ) );
287   mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
288   mScrollView.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT );
289   mScrollView.SetSizeModeFactor( Vector3( 0.0f, 0.6f, 0.0f ) );
290
291   const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * stageSize.width;
292   mScrollView.SetPadding( Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
293
294   mScrollView.SetAxisAutoLock( true );
295   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
296   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
297   mScrollView.TouchSignal().Connect( this, &DaliTableView::OnScrollTouched );
298
299   mPageWidth = stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f;
300
301   // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
302   Actor bubbleContainer = Actor::New();
303   bubbleContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
304   bubbleContainer.SetAnchorPoint( AnchorPoint::CENTER );
305   bubbleContainer.SetParentOrigin( ParentOrigin::CENTER );
306   SetupBackground( bubbleContainer );
307
308   mRootActor.Add( bubbleContainer );
309   mRootActor.Add( mScrollView );
310
311   // Add scroll view effect and setup constraints on pages
312   ApplyScrollViewEffect();
313
314   // Add pages and tiles
315   Populate();
316
317   // Remove constraints for inner cube effect
318   ApplyCubeEffectToPages();
319
320   Dali::Window winHandle = application.GetWindow();
321   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
322   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
323   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
324   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
325
326   // Set initial orientation
327   unsigned int degrees = 0;
328   Rotate( degrees );
329
330   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
331
332   // Background animation
333   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
334   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
335   mAnimationTimer.Start();
336   mBackgroundAnimsPlaying = true;
337
338   CreateFocusEffect();
339 }
340
341 void DaliTableView::CreateFocusEffect()
342 {
343   // Hook the required signals to manage the focus.
344   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
345   KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
346   AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
347
348   // Loop to create both actors for the focus highlight effect.
349   for( unsigned int i = 0; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i )
350   {
351     mFocusEffect[i].actor = ImageView::New( TILE_FOCUS );
352     mFocusEffect[i].actor.SetParentOrigin( ParentOrigin::CENTER );
353     mFocusEffect[i].actor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
354     mFocusEffect[i].actor.SetInheritScale( false );
355     mFocusEffect[i].actor.SetColorMode( USE_OWN_COLOR );
356
357     // Setup initial values pre-animation.
358     mFocusEffect[i].actor.SetScale( startScale );
359     mFocusEffect[i].actor.SetOpacity( 0.0f );
360
361     // Create and setup the animation to do the following:
362     //   1) Initial fade in over short period of time
363     //   2) Zoom in (larger) and fade out simultaneously over longer period of time.
364     mFocusEffect[i].animation = Animation::New( KEYBOARD_FOCUS_ANIMATION_DURATION );
365
366     mFocusEffect[i].animation.AnimateTo( Property( mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA ), KEYBOARD_FOCUS_END_ALPHA, AlphaFunction::LINEAR, TimePeriod( 0.0f, initialFadeDuration ) );
367     mFocusEffect[i].animation.AnimateTo( Property( mFocusEffect[i].actor, Actor::Property::SCALE ), endScale, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
368     mFocusEffect[i].animation.AnimateTo( Property( mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
369
370     mFocusEffect[i].animation.SetLooping( true );
371   }
372
373   // Parent the secondary effect from the primary.
374   mFocusEffect[0].actor.Add( mFocusEffect[1].actor );
375
376   // Play the animation on the 1st glow object.
377   mFocusEffect[0].animation.Play();
378   // Stagger the animation on the 2st glow object half way through.
379   mFocusEffect[1].animation.PlayFrom( KEYBOARD_FOCUS_ANIMATION_DURATION / 2.0f );
380
381   KeyboardFocusManager::Get().SetFocusIndicatorActor( mFocusEffect[0].actor );
382 }
383
384 void DaliTableView::ApplyCubeEffectToPages()
385 {
386   ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
387   unsigned int pageCount(0);
388   for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
389   {
390     Actor page = *pageIter;
391     effect.ApplyToPage( page, pageCount++ );
392   }
393 }
394
395 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
396 {
397 }
398
399 void DaliTableView::Populate()
400 {
401   const Vector2 stageSize = Stage::GetCurrent().GetSize();
402
403   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
404
405   // Populate ScrollView.
406   if( mExampleList.size() > 0 )
407   {
408     if( mSortAlphabetically )
409     {
410       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
411     }
412
413     unsigned int exampleCount = 0;
414     ExampleListConstIter iter = mExampleList.begin();
415
416     for( int t = 0; t < mTotalPages; t++ )
417     {
418       // Create Table
419       TableView page = TableView::New( ROWS_PER_PAGE, EXAMPLES_PER_ROW );
420       page.SetAnchorPoint( AnchorPoint::CENTER );
421       page.SetParentOrigin( ParentOrigin::CENTER );
422       page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
423       mScrollView.Add( page );
424
425       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
426       const float margin = 2.0f;
427       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
428
429       for(int row = 0; row < ROWS_PER_PAGE; row++)
430       {
431         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
432         {
433           const Example& example = ( *iter );
434
435           // Calculate the tiles relative position on the page (between 0 & 1 in each dimension).
436           Vector2 position( static_cast<float>( column ) / ( EXAMPLES_PER_ROW - 1.0f ), static_cast<float>( row ) / ( EXAMPLES_PER_ROW - 1.0f ) );
437           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), position );
438           AccessibilityManager accessibilityManager = AccessibilityManager::Get();
439           accessibilityManager.SetFocusOrder( tile, ++exampleCount );
440           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
441                                                   example.title );
442           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
443           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT,
444                                                   "You can run this example" );
445
446           tile.SetPadding( Padding( margin, margin, margin, margin ) );
447           page.AddChild( tile, TableView::CellPosition( row, column ) );
448
449           iter++;
450
451           if( iter == mExampleList.end() )
452           {
453             break;
454           }
455         }
456
457         if( iter == mExampleList.end() )
458         {
459           break;
460         }
461       }
462
463       mPages.push_back( page );
464
465       if( iter == mExampleList.end() )
466       {
467         break;
468       }
469     }
470   }
471
472   // Update Ruler info.
473   mScrollRulerX = new FixedRuler( mPageWidth );
474   mScrollRulerY = new DefaultRuler();
475   mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
476   mScrollRulerY->Disable();
477   mScrollView.SetRulerX( mScrollRulerX );
478   mScrollView.SetRulerY( mScrollRulerY );
479 }
480
481 void DaliTableView::Rotate( unsigned int degrees )
482 {
483   // Resize the root actor
484   Vector2 stageSize = Stage::GetCurrent().GetSize();
485   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
486
487   if( degrees == 90 || degrees == 270 )
488   {
489     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
490   }
491
492   if( mRotateAnimation )
493   {
494     mRotateAnimation.Stop();
495     mRotateAnimation.Clear();
496   }
497
498   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
499   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
500   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
501   mRotateAnimation.Play();
502 }
503
504 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position )
505 {
506   Actor focusableTile = Actor::New();
507   focusableTile.SetParentOrigin( ParentOrigin::CENTER );
508   focusableTile.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
509   focusableTile.SetSizeModeFactor( sizeMultiplier );
510   focusableTile.SetName( name );
511
512   // Set the tile to be keyboard focusable
513   focusableTile.SetKeyboardFocusable( true );
514
515   Toolkit::ImageView tileContent = ImageView::New();
516   tileContent.SetParentOrigin( ParentOrigin::CENTER );
517   tileContent.SetAnchorPoint( AnchorPoint::CENTER );
518   tileContent.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
519
520   // Add the image via the property first.
521   tileContent.SetProperty( Toolkit::ImageView::Property::IMAGE, TILE_BACKGROUND_ALPHA );
522   // Register a property with the ImageView. This allows us to inject the scroll-view position into the shader.
523   Property::Value value = Vector3( 0.0f, 0.0f, 0.0f );
524   Property::Index propertyIndex = tileContent.RegisterProperty( "uCustomPosition", value );
525
526   // Add a shader to the image (details in shader source).
527   Property::Map map;
528   Property::Map customShader;
529   customShader[ Visual::Shader::Property::FRAGMENT_SHADER ] = FRAGMENT_SHADER_TEXTURED;
530   map[ Visual::Property::SHADER ] = customShader;
531   tileContent.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
532   tileContent.SetColor( TILE_COLOR );
533
534   // We create a constraint to perform a precalculation on the scroll-view X offset
535   // and pass it to the shader uniform, along with the tile's position.
536   Constraint shaderPosition = Constraint::New < Vector3 > ( tileContent, propertyIndex, TileShaderPositionConstraint( mPageWidth, position.x ) );
537   shaderPosition.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
538   shaderPosition.SetRemoveAction( Constraint::Discard );
539   shaderPosition.Apply();
540   focusableTile.Add( tileContent );
541
542   // Create an ImageView for the 9-patch border around the tile.
543   ImageView borderImage = ImageView::New( TILE_BACKGROUND );
544   borderImage.SetAnchorPoint( AnchorPoint::CENTER );
545   borderImage.SetParentOrigin( ParentOrigin::CENTER );
546   borderImage.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
547   borderImage.SetOpacity( 0.8f );
548   tileContent.Add( borderImage );
549
550   TextLabel label = TextLabel::New();
551   label.SetAnchorPoint( AnchorPoint::CENTER );
552   label.SetParentOrigin( ParentOrigin::CENTER );
553   label.SetStyleName( "LauncherLabel" );
554   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
555   label.SetProperty( TextLabel::Property::TEXT, title );
556   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
557   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
558   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
559
560   // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
561   label.SetPadding( Padding( TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING ) );
562   focusableTile.Add( label );
563
564   // Connect to the touch events
565   focusableTile.TouchSignal().Connect( this, &DaliTableView::OnTilePressed );
566   focusableTile.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
567
568   return focusableTile;
569 }
570
571 bool DaliTableView::OnTilePressed( Actor actor, const TouchData& event )
572 {
573   return DoTilePress( actor, event.GetState( 0 ) );
574 }
575
576 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
577 {
578   bool consumed = false;
579
580   if( PointState::DOWN == pointState )
581   {
582     mPressedActor = actor;
583     consumed = true;
584   }
585
586   // A button press is only valid if the Down & Up events
587   // both occurred within the button.
588   if( ( PointState::UP == pointState ) &&
589       ( mPressedActor == actor ) )
590   {
591     // ignore Example button presses when scrolling or button animating.
592     if( ( !mScrolling ) && ( !mPressedAnimation ) )
593     {
594       std::string name = actor.GetName();
595       const ExampleListIter end = mExampleList.end();
596       for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
597       {
598         if( (*iter).name == name )
599         {
600           // do nothing, until pressed animation finished.
601           consumed = true;
602           break;
603         }
604       }
605     }
606
607     if( consumed )
608     {
609       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
610       mPressedAnimation.SetEndAction( Animation::Discard );
611
612       // scale the content actor within the Tile, as to not affect the placement within the Table.
613       Actor content = actor.GetChildAt(0);
614       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.7f, 0.7f, 1.0f ), AlphaFunction::EASE_IN_OUT,
615                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
616       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
617                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
618
619       // Rotate button on the Y axis when pressed.
620       mPressedAnimation.AnimateBy( Property( content, Actor::Property::ORIENTATION ), Quaternion( Degree( 0.0f ), Degree( 180.0f ), Degree( 0.0f ) ) );
621
622       mPressedAnimation.Play();
623       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
624     }
625   }
626   return consumed;
627 }
628
629 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
630 {
631   mPressedAnimation.Reset();
632   if( mPressedActor )
633   {
634     std::string name = mPressedActor.GetName();
635
636     std::stringstream stream;
637     stream << DEMO_EXAMPLE_BIN << name.c_str();
638     pid_t pid = fork();
639     if( pid == 0)
640     {
641       execlp( stream.str().c_str(), name.c_str(), NULL );
642       DALI_ASSERT_ALWAYS(false && "exec failed!");
643     }
644     mPressedActor.Reset();
645   }
646 }
647
648 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
649 {
650   mScrolling = true;
651
652   PlayAnimation();
653 }
654
655 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
656 {
657   mScrolling = false;
658
659   // move focus to 1st item of new page
660   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
661   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
662 }
663
664 bool DaliTableView::OnScrollTouched( Actor actor, const TouchData& event )
665 {
666   if( PointState::DOWN == event.GetState( 0 ) )
667   {
668     mPressedActor = actor;
669   }
670
671   return false;
672 }
673
674 void DaliTableView::ApplyScrollViewEffect()
675 {
676   // Remove old effect if exists.
677
678   if( mScrollViewEffect )
679   {
680     mScrollView.RemoveEffect( mScrollViewEffect );
681   }
682
683   // Just one effect for now
684   SetupInnerPageCubeEffect();
685
686   mScrollView.ApplyEffect( mScrollViewEffect );
687 }
688
689 void DaliTableView::SetupInnerPageCubeEffect()
690 {
691   const Vector2 stageSize = Stage::GetCurrent().GetSize();
692
693   Dali::Path path = Dali::Path::New();
694   Dali::Property::Array points;
695   points.Resize(3);
696   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
697   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
698   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
699   path.SetProperty( Path::Property::POINTS, points );
700
701   Dali::Property::Array controlPoints;
702   controlPoints.Resize(4);
703   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
704   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
705   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
706   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
707   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
708
709
710   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
711                                                     Vector3(-1.0f,0.0f,0.0f),
712                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
713                                                     Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
714 }
715
716 void DaliTableView::OnKeyEvent( const KeyEvent& event )
717 {
718   if( event.state == KeyEvent::Down )
719   {
720     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
721     {
722       // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
723       // Otherwise quit.
724       if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
725       {
726         mVersionPopup.SetDisplayState( Popup::HIDDEN );
727       }
728       else
729       {
730         mApplication.Quit();
731       }
732     }
733   }
734 }
735
736 void DaliTableView::SetupBackground( Actor bubbleContainer )
737 {
738   // Add bubbles to the bubbleContainer.
739   // Note: The bubbleContainer is parented externally to this function.
740   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES );
741 }
742
743 void DaliTableView::InitialiseBackgroundActors( Actor actor )
744 {
745   // Delete current animations
746   mBackgroundAnimations.clear();
747
748   // Create new animations
749   const Vector3 size = actor.GetTargetSize();
750
751   for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
752   {
753     Actor child = actor.GetChildAt( i );
754
755     // Calculate a random position
756     Vector3 childPos( Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.85f * BACKGROUND_SPREAD_SCALE ),
757                       Random::Range( -size.y, size.y ),
758                       Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
759
760     child.SetPosition( childPos );
761
762     // Define bubble horizontal parallax and vertical wrapping
763     Constraint animConstraint = Constraint::New < Vector3 > ( child, Actor::Property::POSITION, AnimateBubbleConstraint( childPos, Random::Range( -0.85f, 0.25f ) ) );
764     animConstraint.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
765     animConstraint.AddSource( Dali::ParentSource( Dali::Actor::Property::SIZE ) );
766     animConstraint.AddSource( Dali::LocalSource( Dali::Actor::Property::SIZE ) );
767     animConstraint.SetRemoveAction( Constraint::Discard );
768     animConstraint.Apply();
769
770     // Kickoff animation
771     Animation animation = Animation::New( Random::Range( 30.0f, 160.0f ) );
772     animation.AnimateBy( Property( child, Actor::Property::POSITION ), Vector3( 0.0f, -2000.0f, 0.0f ), AlphaFunction::LINEAR );
773     animation.SetLooping( true );
774     animation.Play();
775     mBackgroundAnimations.push_back( animation );
776   }
777 }
778
779 void DaliTableView::AddBackgroundActors( Actor layer, int count )
780 {
781   for( int i = 0; i < count; ++i )
782   {
783     float randSize = Random::Range( 10.0f, 400.0f );
784     int shapeType = static_cast<int>( Random::Range( 0.0f, NUMBER_OF_SHAPE_IMAGES - 1 ) + 0.5f );
785     ImageView dfActor = ImageView::New( SHAPE_IMAGE_TABLE[ shapeType ] );
786     dfActor.SetSize( Vector2( randSize, randSize ) );
787     dfActor.SetParentOrigin( ParentOrigin::CENTER );
788
789     Dali::Property::Map effect = Toolkit::CreateDistanceFieldEffect();
790     dfActor.SetProperty( Toolkit::ImageView::Property::IMAGE, effect );
791     dfActor.SetColor( BUBBLE_COLOR[ i%NUMBER_OF_BUBBLE_COLOR ] );
792
793     layer.Add( dfActor );
794   }
795
796   // Positioning will occur when the layer is relaid out
797   layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
798 }
799
800 ImageView DaliTableView::CreateLogo( std::string imagePath )
801 {
802   ImageView logo = ImageView::New( imagePath );
803
804   logo.SetAnchorPoint( AnchorPoint::CENTER );
805   logo.SetParentOrigin( ParentOrigin::CENTER );
806
807   return logo;
808 }
809
810 bool DaliTableView::PauseBackgroundAnimation()
811 {
812   PauseAnimation();
813
814   return false;
815 }
816
817 void DaliTableView::PauseAnimation()
818 {
819   if( mBackgroundAnimsPlaying )
820   {
821     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
822     {
823       Animation anim = *animIter;
824
825       anim.Stop();
826     }
827
828     mBackgroundAnimsPlaying = false;
829   }
830 }
831
832 void DaliTableView::PlayAnimation()
833 {
834   if ( !mBackgroundAnimsPlaying )
835   {
836     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
837     {
838       Animation anim = *animIter;
839
840       anim.Play();
841     }
842
843     mBackgroundAnimsPlaying = true;
844   }
845
846   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
847 }
848
849 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
850 {
851   Actor nextFocusActor = proposed;
852
853   if( !current && !proposed  )
854   {
855     // Set the initial focus to the first tile in the current page should be focused.
856     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
857   }
858   else if( !proposed )
859   {
860     // ScrollView is being focused but nothing in the current page can be focused further
861     // in the given direction. We should work out which page to scroll to next.
862     int currentPage = mScrollView.GetCurrentPage();
863     int newPage = currentPage;
864     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
865     {
866       newPage--;
867     }
868     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
869     {
870       newPage++;
871     }
872
873     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
874     if( newPage == currentPage )
875     {
876       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
877       {
878         newPage = mTotalPages - 1;
879       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
880       {
881         newPage = 0;
882       }
883     }
884
885     // Scroll to the page in the given direction
886     mScrollView.ScrollTo(newPage);
887
888     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
889     {
890       // Work out the cell position for the last tile
891       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
892       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
893       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
894
895       // Move the focus to the last tile in the new page.
896       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
897     }
898     else
899     {
900       // Move the focus to the first tile in the new page.
901       nextFocusActor = mPages[newPage].GetChildAt(0);
902     }
903   }
904
905   return nextFocusActor;
906 }
907
908 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
909 {
910   if(activatedActor)
911   {
912     mPressedActor = activatedActor;
913
914     // Activate the current focused actor;
915     DoTilePress( mPressedActor, PointState::UP );
916   }
917 }
918
919 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
920 {
921   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
922   return true;
923 }
924
925 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
926 {
927   // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
928   if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
929   {
930     if ( !mVersionPopup )
931     {
932       std::ostringstream stream;
933       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
934       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
935       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
936
937       mVersionPopup = Dali::Toolkit::Popup::New();
938
939       Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
940       titleActor.SetName( "titleActor" );
941       titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
942
943       Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
944       contentActor.SetName( "contentActor" );
945       contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
946       contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
947       contentActor.SetPadding( Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
948
949       mVersionPopup.SetTitle( titleActor );
950       mVersionPopup.SetContent( contentActor );
951
952       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
953       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
954       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
955
956       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
957       Stage::GetCurrent().Add( mVersionPopup );
958     }
959
960     mVersionPopup.SetDisplayState( Popup::SHOWN );
961   }
962 }
963
964 void DaliTableView::HideVersionPopup()
965 {
966   // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
967   if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
968   {
969     mVersionPopup.SetDisplayState( Popup::HIDDEN );
970   }
971 }