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