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