6d238333b3c0e5f9f515d6426e6b2c07de346f3c
[platform/core/uifw/dali-demo.git] / demo / dali-table-view.cpp
1 /*
2  * Copyright (c) 2016 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 Vector4 TILE_COLOR( 0.5f, 0.6f, 0.8f, 0.23f );            ///< Color (including alpha) of tile contents.
48 const float BUTTON_PRESS_ANIMATION_TIME = 0.25f;                ///< Time to perform button scale effect.
49 const float ROTATE_ANIMATION_TIME = 0.5f;                       ///< Time to perform rotate effect.
50 const int MAX_PAGES = 256;                                      ///< Maximum pages (arbitrary safety limit)
51 const int EXAMPLES_PER_ROW = 3;
52 const int ROWS_PER_PAGE = 3;
53 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
54 const float LOGO_MARGIN_RATIO = 0.1f / 0.3f;
55 const float BOTTOM_PADDING_RATIO = 0.4f / 0.9f;
56 const Vector3 SCROLLVIEW_RELATIVE_SIZE(0.9f, 1.0f, 0.8f );     ///< ScrollView's relative size to its parent
57 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.
58
59 /**
60  * Creates the background image
61  */
62 Control CreateBackground( std::string stylename )
63 {
64   Control background = Control::New();
65   Stage::GetCurrent().Add( background );
66   background.SetProperty( Control::Property::STYLE_NAME,stylename);
67   background.SetName( "BACKGROUND" );
68   background.SetAnchorPoint( AnchorPoint::CENTER );
69   background.SetParentOrigin( ParentOrigin::CENTER );
70   background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
71
72   return background;
73 }
74
75 bool CompareByTitle( const Example& lhs, const Example& rhs )
76 {
77   return lhs.title < rhs.title;
78 }
79
80 } // namespace
81
82 DaliTableView::DaliTableView( Application& application )
83 : mApplication( application ),
84   mBackgroundLayer(),
85   mRootActor(),
86   mRotateAnimation(),
87   mPressedAnimation(),
88   mScrollViewLayer(),
89   mScrollView(),
90   mScrollViewEffect(),
91   mScrollRulerX(),
92   mScrollRulerY(),
93   mPressedActor(),
94   mLogoTapDetector(),
95   mVersionPopup(),
96   mPages(),
97   mExampleList(),
98   mTotalPages(),
99   mScrolling( false ),
100   mSortAlphabetically( false )
101 {
102   application.InitSignal().Connect( this, &DaliTableView::Initialize );
103 }
104
105 DaliTableView::~DaliTableView()
106 {
107 }
108
109 void DaliTableView::AddExample( Example example )
110 {
111   mExampleList.push_back( example );
112 }
113
114 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
115 {
116   mSortAlphabetically = sortAlphabetically;
117 }
118
119 void DaliTableView::Initialize( Application& application )
120 {
121   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
122
123   const Vector2 stageSize = Stage::GetCurrent().GetSize();
124
125   // Background
126   Control background = CreateBackground( "launcherbackground" );
127   Stage::GetCurrent().Add( background );
128
129   // Add root actor
130   mRootActor = TableView::New( 4, 1 );
131   mRootActor.SetAnchorPoint( AnchorPoint::CENTER );
132   mRootActor.SetParentOrigin( ParentOrigin::CENTER );
133   mRootActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
134   Stage::GetCurrent().Add( mRootActor );
135
136   // Toolbar at top
137   Dali::Toolkit::ToolBar toolbar;
138   Dali::Layer toolBarLayer = DemoHelper::CreateToolbar(toolbar,
139                                                        DEFAULT_TOOLBAR_IMAGE_PATH,
140                                                        DEFAULT_TOOLBAR_TEXT,
141                                                        DemoHelper::DEFAULT_VIEW_STYLE);
142
143   mRootActor.AddChild( toolBarLayer, TableView::CellPosition( 0, 0 ) );
144   mRootActor.SetFitHeight( 0 );
145
146   // Add logo
147   ImageView logo = CreateLogo( LOGO_PATH );
148   logo.SetName( "LOGO_IMAGE" );
149   logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
150   const float paddingHeight = ( ( 1.f-TABLE_RELATIVE_SIZE.y ) * stageSize.y );
151   const float logoMargin = paddingHeight * LOGO_MARGIN_RATIO;
152
153   // Show version in a popup when log is tapped
154   mLogoTapDetector = TapGestureDetector::New();
155   mLogoTapDetector.Attach( logo );
156   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
157
158   const float bottomMargin = paddingHeight * BOTTOM_PADDING_RATIO;
159
160   Alignment alignment = Alignment::New();
161   alignment.SetName( "LOGO_ALIGNMENT" );
162   alignment.Add( logo );
163   alignment.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
164   alignment.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
165   Actor alignmentActor = alignment;
166   alignmentActor.SetPadding( Padding( 0.0f, 0.0f, logoMargin, logoMargin ));
167   mRootActor.AddChild( alignment, TableView::CellPosition( 1, 0 ) );
168   mRootActor.SetFitHeight( 1 );
169
170   // scrollview occupying the majority of the screen
171   mScrollView = ScrollView::New();
172
173   mScrollView.SetAnchorPoint( AnchorPoint::CENTER );
174   mScrollView.SetParentOrigin( ParentOrigin::CENTER );
175   mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
176   const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * stageSize.width;
177   mScrollView.SetPadding( Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
178
179   mScrollView.SetAxisAutoLock( true );
180   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
181   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
182   mScrollView.TouchSignal().Connect( this, &DaliTableView::OnScrollTouched );
183
184   mScrollViewLayer = Layer::New();
185
186   // Disable the depth test for performance
187   mScrollViewLayer.SetDepthTestDisabled( true );
188   mScrollViewLayer.SetAnchorPoint( AnchorPoint::CENTER );
189   mScrollViewLayer.SetParentOrigin( ParentOrigin::CENTER );
190   mScrollViewLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
191
192   Alignment buttonsAlignment = Alignment::New();
193   buttonsAlignment.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
194   buttonsAlignment.Add( mScrollViewLayer );
195
196   mScrollViewLayer.Add( mScrollView );
197
198   mRootActor.AddChild( buttonsAlignment, TableView::CellPosition( 2, 0 ) );
199
200   mRootActor.SetFixedHeight( 3, bottomMargin );
201
202   // Add scroll view effect and setup constraints on pages
203   ApplyScrollViewEffect();
204
205   // Add pages and tiles
206   Populate();
207
208   // Remove constraints for inner cube effect
209   ApplyCubeEffectToPages();
210
211   Dali::Window winHandle = application.GetWindow();
212   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
213   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
214   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
215   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
216
217   // Set initial orientation
218   unsigned int degrees = 0;
219   Rotate( degrees );
220
221   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
222
223   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
224   KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
225   AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
226 }
227
228 void DaliTableView::ApplyCubeEffectToPages()
229 {
230   ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
231   unsigned int pageCount(0);
232   for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
233   {
234     Actor page = *pageIter;
235     effect.ApplyToPage( page, pageCount++ );
236   }
237 }
238
239 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
240 {
241 }
242
243 void DaliTableView::Populate()
244 {
245   const Vector2 stageSize = Stage::GetCurrent().GetSize();
246
247   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
248
249   // Populate ScrollView.
250   if( mExampleList.size() > 0 )
251   {
252     if( mSortAlphabetically )
253     {
254       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
255     }
256
257     unsigned int exampleCount = 0;
258     ExampleListConstIter iter = mExampleList.begin();
259
260     for( int t = 0; t < mTotalPages; t++ )
261     {
262       // Create Table
263       TableView page = TableView::New( ROWS_PER_PAGE, EXAMPLES_PER_ROW );
264       page.SetAnchorPoint( AnchorPoint::CENTER );
265       page.SetParentOrigin( ParentOrigin::CENTER );
266       page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
267       mScrollView.Add( page );
268
269       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
270       const float margin = 2.0f;
271       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
272
273       for(int row = 0; row < ROWS_PER_PAGE; row++)
274       {
275         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
276         {
277           const Example& example = ( *iter );
278
279           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), TILE_COLOR );
280           AccessibilityManager accessibilityManager = AccessibilityManager::Get();
281           accessibilityManager.SetFocusOrder( tile, ++exampleCount );
282           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
283                                                   example.title );
284           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
285           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT,
286                                                   "You can run this example" );
287
288           tile.SetPadding( Padding( margin, margin, margin, margin ) );
289
290           page.AddChild( tile, TableView::CellPosition( row, column ) );
291
292           iter++;
293
294           if( iter == mExampleList.end() )
295           {
296             break;
297           }
298         }
299
300         if( iter == mExampleList.end() )
301         {
302           break;
303         }
304       }
305
306       // Set tableview position
307       Vector3 pagePos( stageSize.width * TABLE_RELATIVE_SIZE.x * t, 0.0f, 0.0f );
308       page.SetPosition( pagePos );
309
310       mPages.push_back( page );
311
312       if( iter == mExampleList.end() )
313       {
314         break;
315       }
316     }
317   }
318
319   // Update Ruler info.
320   mScrollRulerX = new FixedRuler( stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f );
321   mScrollRulerY = new DefaultRuler();
322   mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
323   mScrollRulerY->Disable();
324   mScrollView.SetRulerX( mScrollRulerX );
325   mScrollView.SetRulerY( mScrollRulerY );
326 }
327
328 void DaliTableView::Rotate( unsigned int degrees )
329 {
330   // Resize the root actor
331   Vector2 stageSize = Stage::GetCurrent().GetSize();
332   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
333
334   if( degrees == 90 || degrees == 270 )
335   {
336     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
337   }
338
339   if( mRotateAnimation )
340   {
341     mRotateAnimation.Stop();
342     mRotateAnimation.Clear();
343   }
344
345   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
346   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
347   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
348   mRotateAnimation.Play();
349 }
350
351 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, const Dali::Vector4& color )
352 {
353   Actor content = Actor::New();
354   content.SetName( name );
355   content.SetAnchorPoint( AnchorPoint::CENTER );
356   content.SetParentOrigin( ParentOrigin::CENTER );
357   content.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
358   content.SetSizeModeFactor( sizeMultiplier );
359
360   // Create background image.
361   ImageView image = ImageView::New( TILE_BACKGROUND );
362   image.SetAnchorPoint( AnchorPoint::CENTER );
363   image.SetParentOrigin( ParentOrigin::CENTER );
364   // Make the image 100% of tile.
365   image.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
366   content.Add( image );
367
368   // Create the tile background.
369   Actor tileBackground = ImageView::New( TILE_BACKGROUND_ALPHA );
370   tileBackground.SetParentOrigin( ParentOrigin::CENTER );
371   tileBackground.SetAnchorPoint( AnchorPoint::CENTER );
372   tileBackground.SetProperty( Actor::Property::COLOR, color );
373   Property::Map shaderEffect = CreateAlphaDiscardEffect();
374   tileBackground.SetProperty( Toolkit::ImageView::Property::IMAGE, shaderEffect );
375   tileBackground.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
376   image.Add( tileBackground );
377
378   // Create the tile label.
379   TextLabel label = TextLabel::New();
380   label.SetAnchorPoint( AnchorPoint::TOP_LEFT );
381   label.SetProperty( Control::Property::STYLE_NAME, "launcherlabel" );
382   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
383   label.SetProperty( TextLabel::Property::TEXT, title );
384   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
385   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
386   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
387   content.Add( label );
388
389   // Set the tile to be keyboard focusable
390   content.SetKeyboardFocusable(true);
391
392   // connect to the touch events
393   content.TouchSignal().Connect( this, &DaliTableView::OnTilePressed );
394   content.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
395
396   return content;
397 }
398
399 bool DaliTableView::OnTilePressed( Actor actor, const TouchData& event )
400 {
401   return DoTilePress( actor, event.GetState( 0 ) );
402 }
403
404 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
405 {
406   bool consumed = false;
407
408   if( PointState::DOWN == pointState )
409   {
410     mPressedActor = actor;
411     consumed = true;
412   }
413
414   // A button press is only valid if the Down & Up events
415   // both occurred within the button.
416   if( ( PointState::UP == pointState ) &&
417       ( mPressedActor == actor ) )
418   {
419     // ignore Example button presses when scrolling or button animating.
420     if( ( !mScrolling ) && ( !mPressedAnimation ) )
421     {
422       std::string name = actor.GetName();
423       const ExampleListIter end = mExampleList.end();
424       for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
425       {
426         if( (*iter).name == name )
427         {
428           // do nothing, until pressed animation finished.
429           consumed = true;
430           break;
431         }
432       }
433     }
434
435     if( consumed )
436     {
437       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
438       mPressedAnimation.SetEndAction( Animation::Discard );
439
440       // scale the content actor within the Tile, as to not affect the placement within the Table.
441       Actor content = actor.GetChildAt(0);
442       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.9f, 0.9f, 1.0f ), AlphaFunction::EASE_IN_OUT,
443                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
444       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
445                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
446       mPressedAnimation.Play();
447       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
448     }
449   }
450   return consumed;
451 }
452
453 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
454 {
455   mPressedAnimation.Reset();
456   if( mPressedActor )
457   {
458     std::string name = mPressedActor.GetName();
459
460     std::stringstream stream;
461     stream << DEMO_EXAMPLE_BIN << name.c_str();
462     pid_t pid = fork();
463     if( pid == 0)
464     {
465       execlp( stream.str().c_str(), name.c_str(), NULL );
466       DALI_ASSERT_ALWAYS(false && "exec failed!");
467     }
468     mPressedActor.Reset();
469   }
470 }
471
472 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
473 {
474   mScrolling = true;
475 }
476
477 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
478 {
479   mScrolling = false;
480
481   // move focus to 1st item of new page
482   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
483   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
484 }
485
486 bool DaliTableView::OnScrollTouched( Actor actor, const TouchData& event )
487 {
488   if( PointState::DOWN == event.GetState( 0 ) )
489   {
490     mPressedActor = actor;
491   }
492
493   return false;
494 }
495
496 void DaliTableView::ApplyScrollViewEffect()
497 {
498   // Remove old effect if exists.
499
500   if( mScrollViewEffect )
501   {
502     mScrollView.RemoveEffect( mScrollViewEffect );
503   }
504
505   // Just one effect for now
506   SetupInnerPageCubeEffect();
507
508   mScrollView.ApplyEffect( mScrollViewEffect );
509 }
510
511 void DaliTableView::SetupInnerPageCubeEffect()
512 {
513   const Vector2 stageSize = Stage::GetCurrent().GetSize();
514
515   Dali::Path path = Dali::Path::New();
516   Dali::Property::Array points;
517   points.Resize(3);
518   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
519   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
520   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
521   path.SetProperty( Path::Property::POINTS, points );
522
523   Dali::Property::Array controlPoints;
524   controlPoints.Resize(4);
525   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
526   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
527   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
528   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
529   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
530
531
532   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
533                                                     Vector3(-1.0f,0.0f,0.0f),
534                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
535                                                     Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
536 }
537
538 void DaliTableView::OnKeyEvent( const KeyEvent& event )
539 {
540   if( event.state == KeyEvent::Down )
541   {
542     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
543     {
544       // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
545       // Otherwise quit.
546       if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
547       {
548         mVersionPopup.SetDisplayState( Popup::HIDDEN );
549       }
550       else
551       {
552         mApplication.Quit();
553       }
554     }
555   }
556 }
557
558 ImageView DaliTableView::CreateLogo( std::string imagePath )
559 {
560   ImageView logo = ImageView::New( imagePath );
561
562   logo.SetAnchorPoint( AnchorPoint::CENTER );
563   logo.SetParentOrigin( ParentOrigin::CENTER );
564
565   return logo;
566 }
567
568 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
569 {
570   Actor nextFocusActor = proposed;
571
572   if ( !current && !proposed  )
573   {
574     // Set the initial focus to the first tile in the current page should be focused.
575     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
576   }
577   else if( !proposed || (proposed && proposed == mScrollViewLayer) )
578   {
579     // ScrollView is being focused but nothing in the current page can be focused further
580     // in the given direction. We should work out which page to scroll to next.
581     int currentPage = mScrollView.GetCurrentPage();
582     int newPage = currentPage;
583     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
584     {
585       newPage--;
586     }
587     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
588     {
589       newPage++;
590     }
591
592     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
593     if( newPage == currentPage )
594     {
595       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
596       {
597         newPage = mTotalPages - 1;
598       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
599       {
600         newPage = 0;
601       }
602     }
603
604     // Scroll to the page in the given direction
605     mScrollView.ScrollTo(newPage);
606
607     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
608     {
609       // Work out the cell position for the last tile
610       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
611       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
612       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
613
614       // Move the focus to the last tile in the new page.
615       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
616     }
617     else
618     {
619       // Move the focus to the first tile in the new page.
620       nextFocusActor = mPages[newPage].GetChildAt(0);
621     }
622   }
623
624   return nextFocusActor;
625 }
626
627 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
628 {
629   if(activatedActor)
630   {
631     mPressedActor = activatedActor;
632
633     // Activate the current focused actor;
634     DoTilePress( mPressedActor, PointState::UP );
635   }
636 }
637
638 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
639 {
640   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
641   return true;
642 }
643
644 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
645 {
646   // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
647   if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
648   {
649     if ( !mVersionPopup )
650     {
651       std::ostringstream stream;
652       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
653       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
654       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
655
656       mVersionPopup = Dali::Toolkit::Popup::New();
657
658       Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
659       titleActor.SetName( "titleActor" );
660       titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
661       titleActor.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE );
662
663       Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
664       contentActor.SetName( "contentActor" );
665       contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
666       contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
667       contentActor.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE );
668       contentActor.SetPadding( Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
669
670       mVersionPopup.SetTitle( titleActor );
671       mVersionPopup.SetContent( contentActor );
672
673       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
674       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
675       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
676
677       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
678       Stage::GetCurrent().Add( mVersionPopup );
679     }
680
681     mVersionPopup.SetDisplayState( Popup::SHOWN );
682   }
683 }
684
685 void DaliTableView::HideVersionPopup()
686 {
687   // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
688   if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
689   {
690     mVersionPopup.SetDisplayState( Popup::HIDDEN );
691   }
692 }