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