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