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