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