Fix for TextLabel demo styling
[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( FILL_TO_PARENT, ALL_DIMENSIONS );
295   backgroundColourActor.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
296   backgroundColourActor.SetSizeModeFactor( Vector3( 1.0f, 1.5f, 1.0f ) );
297   backgroundColourActor.SetZ( BACKGROUND_Z );
298   mScrollViewLayer.Add( backgroundColourActor );
299
300   // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
301   Actor bubbleContainer = Actor::New();
302   bubbleContainer.SetRelayoutEnabled( true );
303   bubbleContainer.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
304   bubbleContainer.SetAnchorPoint( AnchorPoint::CENTER );
305   bubbleContainer.SetParentOrigin( ParentOrigin::CENTER );
306   mScrollViewLayer.Add( bubbleContainer );
307
308   SetupBackground( bubbleContainer );
309
310   Alignment buttonsAlignment = Alignment::New();
311   buttonsAlignment.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
312   buttonsAlignment.Add( mScrollViewLayer );
313
314   mScrollViewLayer.Add( mScrollView );
315
316   mRootActor.AddChild( buttonsAlignment, TableView::CellPosition( 2, 0 ) );
317
318   mRootActor.SetFixedHeight( 3, bottomMargin );
319
320   // Add scroll view effect and setup constraints on pages
321   ApplyScrollViewEffect();
322
323   // Add pages and tiles
324   Populate();
325
326   // Remove constraints for inner cube effect
327   ApplyCubeEffectToActors();
328
329   // Set initial orientation
330   unsigned int degrees = application.GetOrientation().GetDegrees();
331   Rotate( degrees );
332
333   Dali::Window winHandle = application.GetWindow();
334   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
335   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
336   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
337   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
338
339   Dali::Orientation orientation = winHandle.GetOrientation();
340   orientation.ChangedSignal().Connect( this, &DaliTableView::OrientationChanged );
341
342   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
343
344   // Background animation
345   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
346   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
347   mAnimationTimer.Start();
348   mBackgroundAnimsPlaying = true;
349
350   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
351   KeyboardFocusManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
352 }
353
354 void DaliTableView::ApplyCubeEffectToActors()
355 {
356   for( ActorIter pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
357   {
358     Actor page = *pageIter;
359
360     for( unsigned int i = 0, numChildren = page.GetChildCount(); i < numChildren; ++i)
361     {
362       // Remove old effect's manual constraints.
363       Actor child = page.GetChildAt(i);
364       if( child )
365       {
366         ApplyCubeEffectToActor( child );
367       }
368     }
369   }
370 }
371
372 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
373 {
374
375 }
376
377 void DaliTableView::Populate()
378 {
379   const Vector2 stageSize = Stage::GetCurrent().GetSize();
380
381   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
382
383   // Populate ScrollView.
384   if( mExampleList.size() > 0 )
385   {
386     if( mSortAlphabetically )
387     {
388       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
389     }
390
391     unsigned int exampleCount = 0;
392     ExampleListConstIter iter = mExampleList.begin();
393
394     for( int t = 0; t < mTotalPages; t++ )
395     {
396       // Create Table. (contains up to 9 Examples)
397       TableView page = TableView::New( 3, 3 );
398       page.SetAnchorPoint( AnchorPoint::CENTER );
399       page.SetParentOrigin( ParentOrigin::CENTER );
400       page.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
401       mScrollView.Add( page );
402
403       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
404       const float margin = 2.0f;
405       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
406
407       for(int row = 0; row < ROWS_PER_PAGE; row++)
408       {
409         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
410         {
411           const Example& example = ( *iter );
412
413           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), true );
414           FocusManager focusManager = FocusManager::Get();
415           focusManager.SetFocusOrder( tile, ++exampleCount );
416           focusManager.SetAccessibilityAttribute( tile, Dali::Toolkit::FocusManager::ACCESSIBILITY_LABEL,
417                                                   example.title );
418           focusManager.SetAccessibilityAttribute( tile, Dali::Toolkit::FocusManager::ACCESSIBILITY_TRAIT, "Tile" );
419           focusManager.SetAccessibilityAttribute( tile, Dali::Toolkit::FocusManager::ACCESSIBILITY_HINT,
420                                                   "You can run this example" );
421
422           tile.SetPadding( Padding( margin, margin, margin, margin ) );
423
424           page.AddChild( tile, TableView::CellPosition( row, column ) );
425
426           iter++;
427
428           if( iter == mExampleList.end() )
429           {
430             break;
431           }
432         }
433
434         if( iter == mExampleList.end() )
435         {
436           break;
437         }
438       }
439
440       // Set tableview position
441       Vector3 pagePos( stageSize.width * TABLE_RELATIVE_SIZE.x * t, 0.0f, 0.0f );
442       page.SetPosition( pagePos );
443
444       mPages.push_back( page );
445
446       if( iter == mExampleList.end() )
447       {
448         break;
449       }
450     }
451   }
452
453   // Update Ruler info.
454   mScrollRulerX = new FixedRuler( stageSize.width * TABLE_RELATIVE_SIZE.x );
455   mScrollRulerY = new DefaultRuler();
456   mScrollRulerX->SetDomain( RulerDomain( 0.0f, mTotalPages * stageSize.width * TABLE_RELATIVE_SIZE.x, true ) );
457   mScrollRulerY->Disable();
458   mScrollView.SetRulerX( mScrollRulerX );
459   mScrollView.SetRulerY( mScrollRulerY );
460 }
461
462 void DaliTableView::OrientationChanged( Orientation orientation )
463 {
464   // TODO: Implement if orientation change required
465 }
466
467 void DaliTableView::Rotate( unsigned int degrees )
468 {
469   // Resize the root actor
470   Vector2 stageSize = Stage::GetCurrent().GetSize();
471   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
472
473   if( degrees == 90 || degrees == 270 )
474   {
475     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
476   }
477
478   if( mRotateAnimation )
479   {
480     mRotateAnimation.Stop();
481     mRotateAnimation.Clear();
482   }
483
484   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
485   mRotateAnimation.RotateTo( mRootActor, Degree( 360 - degrees ), Vector3::ZAXIS, AlphaFunctions::EaseOut );
486   mRotateAnimation.Resize( mRootActor, targetSize, AlphaFunctions::EaseOut );
487   mRotateAnimation.Play();
488 }
489
490 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, bool addBackground )
491 {
492   Actor content = Actor::New();
493   content.SetName( name );
494   content.SetAnchorPoint( AnchorPoint::CENTER );
495   content.SetParentOrigin( ParentOrigin::CENTER );
496   content.SetRelayoutEnabled( true );
497   content.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
498   content.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
499   content.SetSizeModeFactor( sizeMultiplier );
500
501   // create background image
502   if( addBackground )
503   {
504     Image bg = ResourceImage::New( TILE_BACKGROUND );
505     ImageActor image = ImageActor::New( bg );
506     image.SetAnchorPoint( AnchorPoint::CENTER );
507     image.SetParentOrigin( ParentOrigin::CENTER );
508     // make the image 100% of tile
509     image.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
510
511     // move image back to get text appear in front
512     image.SetZ( -1 );
513     image.SetStyle( ImageActor::STYLE_NINE_PATCH );
514     image.SetNinePatchBorder( Vector4( IMAGE_BORDER_LEFT, IMAGE_BORDER_TOP, IMAGE_BORDER_RIGHT, IMAGE_BORDER_BOTTOM ) );
515     content.Add( image );
516
517     // Add stencil
518     ImageActor stencil = NewStencilImage();
519     stencil.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
520     image.Add( stencil );
521   }
522
523   TextLabel label = TextLabel::New();
524   label.SetAnchorPoint( AnchorPoint::TOP_LEFT );
525   label.SetProperty( Control::Property::STYLE_NAME, "launcherlabel" );
526   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
527   label.SetProperty( TextLabel::Property::TEXT, title );
528   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
529   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
530   label.SetResizePolicy( FILL_TO_PARENT, HEIGHT );
531   label.SetColor( TABLE_TEXT_STYLE_COLOR );
532   content.Add( label );
533
534   // Set the tile to be keyboard focusable
535   content.SetKeyboardFocusable(true);
536
537   // connect to the touch events
538   content.TouchedSignal().Connect( this, &DaliTableView::OnTilePressed );
539   content.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
540
541   return content;
542 }
543
544 ImageActor DaliTableView::NewStencilImage()
545 {
546   Image alpha = ResourceImage::New( TILE_BACKGROUND_ALPHA );
547
548   ImageActor stencilActor = ImageActor::New( alpha );
549   stencilActor.SetStyle( ImageActor::STYLE_NINE_PATCH );
550   stencilActor.SetNinePatchBorder( Vector4( IMAGE_BORDER_LEFT, IMAGE_BORDER_TOP, IMAGE_BORDER_RIGHT, IMAGE_BORDER_BOTTOM ) );
551
552   stencilActor.SetParentOrigin( ParentOrigin::CENTER );
553   stencilActor.SetAnchorPoint( AnchorPoint::CENTER );
554   stencilActor.SetDrawMode( DrawMode::STENCIL );
555
556   Dali::ShaderEffect shaderEffect = AlphaDiscardEffect::New();
557   stencilActor.SetShaderEffect( shaderEffect );
558
559   return stencilActor;
560 }
561
562 bool DaliTableView::OnTilePressed( Actor actor, const TouchEvent& event )
563 {
564   bool consumed = false;
565
566   const TouchPoint& point = event.GetPoint( 0 );
567   if( TouchPoint::Down == point.state )
568   {
569     mPressedActor = actor;
570     consumed = true;
571   }
572
573   // A button press is only valid if the Down & Up events
574   // both occurred within the button.
575   if( ( TouchPoint::Up == point.state ) &&
576       ( mPressedActor == actor ) )
577   {
578     std::string name = actor.GetName();
579     ExampleMapConstIter iter = mExampleMap.find( name );
580
581     FocusManager focusManager = FocusManager::Get();
582
583     if( iter != mExampleMap.end() )
584     {
585       // ignore Example button presses when scrolling or button animating.
586       if( ( !mScrolling ) && ( !mPressedAnimation ) )
587       {
588         // do nothing, until pressed animation finished.
589         consumed = true;
590       }
591     }
592
593     if( consumed )
594     {
595       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
596       mPressedAnimation.SetEndAction( Animation::Discard );
597
598       // scale the content actor within the Tile, as to not affect the placement within the Table.
599       Actor content = actor.GetChildAt(0);
600       mPressedAnimation.ScaleTo( content, Vector3( 0.9f, 0.9f, 1.0f ), AlphaFunctions::EaseInOut, 0.0f,
601                                  BUTTON_PRESS_ANIMATION_TIME * 0.5f );
602       mPressedAnimation.ScaleTo( content, Vector3::ONE, AlphaFunctions::EaseInOut, BUTTON_PRESS_ANIMATION_TIME * 0.5f,
603                                  BUTTON_PRESS_ANIMATION_TIME * 0.5f );
604       mPressedAnimation.Play();
605       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
606     }
607   }
608   return consumed;
609 }
610
611 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
612 {
613   mPressedAnimation.Reset();
614   if( mPressedActor )
615   {
616     std::string name = mPressedActor.GetName();
617     ExampleMapConstIter iter = mExampleMap.find( name );
618
619     if( iter == mExampleMap.end() )
620     {
621       if( name == BUTTON_QUIT )
622       {
623         // Move focus to the OK button
624         FocusManager focusManager = FocusManager::Get();
625
626         // Enable the group mode and wrap mode
627         focusManager.SetGroupMode( true );
628         focusManager.SetWrapMode( true );
629       }
630     }
631     else
632     {
633       const Example& example( iter->second );
634
635       std::stringstream stream;
636       stream << DALI_EXAMPLE_BIN << example.name.c_str();
637       pid_t pid = fork();
638       if( pid == 0)
639       {
640         execlp( stream.str().c_str(), example.name.c_str(), NULL );
641         DALI_ASSERT_ALWAYS(false && "exec failed!");
642       }
643     }
644     mPressedActor.Reset();
645   }
646 }
647
648 void DaliTableView::OnScrollStart( const Dali::Vector3& position )
649 {
650   mScrolling = true;
651
652   PlayAnimation();
653 }
654
655 void DaliTableView::OnScrollComplete( const Dali::Vector3& position )
656 {
657   mScrolling = false;
658
659   // move focus to 1st item of new page
660   FocusManager focusManager = FocusManager::Get();
661   focusManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
662 }
663
664 bool DaliTableView::OnScrollTouched( Actor actor, const TouchEvent& event )
665 {
666   const TouchPoint& point = event.GetPoint( 0 );
667   if( TouchPoint::Down == point.state )
668   {
669     mPressedActor = actor;
670   }
671
672   return false;
673 }
674
675 void DaliTableView::ApplyScrollViewEffect()
676 {
677   // Remove old effect if exists.
678
679   if( mScrollViewEffect )
680   {
681     mScrollView.RemoveEffect( mScrollViewEffect );
682   }
683
684   // Just one effect for now
685   SetupInnerPageCubeEffect();
686
687   mScrollView.ApplyEffect( mScrollViewEffect );
688 }
689
690 void DaliTableView::SetupInnerPageCubeEffect()
691 {
692   mScrollViewEffect = ScrollViewCubeEffect::New();
693   mScrollView.SetScrollSnapDuration( EFFECT_SNAP_DURATION );
694   mScrollView.SetScrollFlickDuration( EFFECT_FLICK_DURATION );
695   mScrollView.RemoveConstraintsFromChildren();
696 }
697
698 void DaliTableView::ApplyCubeEffectToActor( Actor actor )
699 {
700   actor.RemoveConstraints();
701
702   ScrollViewCubeEffect cubeEffect = ScrollViewCubeEffect::DownCast(mScrollViewEffect);
703   cubeEffect.ApplyToActor( actor,
704                            ScalePointSize( ( rand() & 1 ) ? ANCHOR_3DEFFECT_STYLE0 : ANCHOR_3DEFFECT_STYLE1 ),
705                            ANGLE_SWING_3DEFFECT,
706                            POSITION_SWING_3DEFFECT * Vector2(Stage::GetCurrent().GetSize()));
707 }
708
709 void DaliTableView::OnKeyEvent( const KeyEvent& event )
710 {
711   if( event.state == KeyEvent::Down )
712   {
713     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
714     {
715       if ( mVersionPopup && mVersionPopupShown )
716       {
717         HideVersionPopup();
718       }
719       else
720       {
721         mApplication.Quit();
722       }
723     }
724   }
725 }
726
727 void DaliTableView::SetupBackground( Actor bubbleContainer )
728 {
729   // Create distance field shape.
730   BufferImage distanceField;
731   Size imageSize( 512, 512 );
732   CreateShapeImage( CIRCLE, imageSize, distanceField );
733
734   // Add bubbles to the bubbleContainer.
735   // Note: The bubbleContainer is parented externally to this function.
736   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES, distanceField );
737 }
738
739 void DaliTableView::InitialiseBackgroundActors( Actor actor )
740 {
741   // Delete current animations
742   mBackgroundAnimations.clear();
743
744   // Create new animations
745   const Vector3 size = actor.GetTargetSize();
746
747   for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
748   {
749     Actor child = actor.GetChildAt( i );
750
751     const Vector3 childSize = child.GetTargetSize();
752
753     // Calculate a random position
754     Vector3 childPos( Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.5f * BACKGROUND_SPREAD_SCALE ),
755                       Random::Range( -size.y * 0.5f - childSize.height, size.y * 0.5f + childSize.height ),
756                       Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
757
758     child.SetPosition( childPos );
759
760     // Define bubble horizontal parallax and vertical wrapping
761     Constraint animConstraint = Constraint::New < Vector3 > ( Actor::Property::POSITION,
762       Source( mScrollView, mScrollView.GetPropertyIndex( ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ),
763       Dali::ParentSource( Dali::Actor::Property::SIZE ),
764       AnimateBubbleConstraint( childPos, Random::Range( -0.85f, 0.25f ), childSize.height ) );
765     child.ApplyConstraint( animConstraint );
766
767     // Kickoff animation
768     Animation animation = Animation::New( Random::Range( 40.0f, 80.0f ) );
769     animation.MoveBy( child, Vector3( 0.0f, -1.0f, 0.0f ), AlphaFunctions::Linear );
770     animation.SetLooping( true );
771     animation.Play();
772     mBackgroundAnimations.push_back( animation );
773   }
774 }
775
776 void DaliTableView::AddBackgroundActors( Actor layer, int count, BufferImage distanceField )
777 {
778   for( int i = 0; i < count; ++i )
779   {
780     float randSize = Random::Range( 10.0f, 400.0f );
781     float hue = Random::Range( 0.3f, 1.0f );
782     Vector4 randColour( hue, hue * 0.5, 0.0f, Random::Range( 0.3f, 0.6f ));
783
784     ImageActor dfActor = ImageActor::New( distanceField );
785     dfActor.SetRelayoutEnabled( false );
786     dfActor.SetSize( Vector2( randSize, randSize ) );
787     dfActor.SetParentOrigin( ParentOrigin::CENTER );
788
789     Toolkit::DistanceFieldEffect effect = Toolkit::DistanceFieldEffect::New();
790     dfActor.SetShaderEffect( effect );
791     dfActor.SetColor( randColour );
792     effect.SetOutlineParams( Vector2( 0.55f, 0.00f ) );
793     effect.SetSmoothingEdge( 0.5f );
794     layer.Add( dfActor );
795   }
796
797   // Positioning will occur when the layer is relaid out
798   layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
799 }
800
801 void DaliTableView::CreateShapeImage( ShapeType shapeType, const Size& size, BufferImage& distanceFieldOut )
802 {
803   // this bitmap will hold the alpha map for the distance field shader
804   distanceFieldOut = BufferImage::New( size.width, size.height, Pixel::A8 );
805
806   // Generate bit pattern
807   std::vector< unsigned char > imageDataA8;
808   imageDataA8.reserve( size.width * size.height ); // A8
809
810   switch( shapeType )
811   {
812     case CIRCLE:
813       GenerateCircle( size, imageDataA8 );
814       break;
815     case SQUARE:
816       GenerateSquare( size, imageDataA8 );
817       break;
818     default:
819       break;
820   }
821
822   PixelBuffer* buffer = distanceFieldOut.GetBuffer();
823   if( buffer )
824   {
825     GenerateDistanceFieldMap( &imageDataA8[ 0 ], size, buffer, size, 8.0f, size );
826     distanceFieldOut.Update();
827   }
828 }
829
830 void DaliTableView::GenerateSquare( const Size& size, std::vector< unsigned char >& distanceFieldOut )
831 {
832   for( int h = 0; h < size.height; ++h )
833   {
834     for( int w = 0; w < size.width; ++w )
835     {
836       distanceFieldOut.push_back( 0xFF );
837     }
838   }
839 }
840
841 void DaliTableView::GenerateCircle( const Size& size, std::vector< unsigned char >& distanceFieldOut )
842 {
843   const float radius = size.width * 0.5f * size.width * 0.5f;
844   Vector2 center( size.width / 2, size.height / 2 );
845
846   for( int h = 0; h < size.height; ++h )
847   {
848     for( int w = 0; w < size.width; ++w )
849     {
850       Vector2 pos( w, h );
851       Vector2 dist = pos - center;
852
853       if( dist.x * dist.x + dist.y * dist.y > radius )
854       {
855         distanceFieldOut.push_back( 0x00 );
856       }
857       else
858       {
859         distanceFieldOut.push_back( 0xFF );
860       }
861     }
862   }
863 }
864
865 ImageActor DaliTableView::CreateLogo( std::string imagePath )
866 {
867   Image image = ResourceImage::New( imagePath );
868   ImageActor logo = ImageActor::New( image );
869
870   logo.SetAnchorPoint( AnchorPoint::CENTER );
871   logo.SetParentOrigin( ParentOrigin::CENTER );
872
873   return logo;
874 }
875
876 bool DaliTableView::PauseBackgroundAnimation()
877 {
878   PauseAnimation();
879
880   return false;
881 }
882
883 void DaliTableView::PauseAnimation()
884 {
885   if( mBackgroundAnimsPlaying )
886   {
887     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
888     {
889       Animation anim = *animIter;
890
891       anim.Pause();
892     }
893
894     mBackgroundAnimsPlaying = false;
895   }
896 }
897
898 void DaliTableView::PlayAnimation()
899 {
900   if ( !mBackgroundAnimsPlaying )
901   {
902     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
903     {
904       Animation anim = *animIter;
905
906       anim.Play();
907     }
908
909     mBackgroundAnimsPlaying = true;
910   }
911
912   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
913 }
914
915 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocusNavigationDirection direction )
916 {
917   Actor nextFocusActor = proposed;
918
919   if ( !current && !proposed  )
920   {
921     // Set the initial focus to the first tile in the current page should be focused.
922     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
923   }
924   else if( !proposed || (proposed && proposed == mScrollViewLayer) )
925   {
926     // ScrollView is being focused but nothing in the current page can be focused further
927     // in the given direction. We should work out which page to scroll to next.
928     int currentPage = mScrollView.GetCurrentPage();
929     int newPage = currentPage;
930     if( direction == Dali::Toolkit::Control::Left )
931     {
932       newPage--;
933     }
934     else if( direction == Dali::Toolkit::Control::Right )
935     {
936       newPage++;
937     }
938
939     newPage = std::max(0, std::min(static_cast<int>(mScrollRulerX->GetTotalPages() - 1), newPage));
940     if( newPage == currentPage )
941     {
942       if( direction == Dali::Toolkit::Control::Left )
943       {
944         newPage = mScrollRulerX->GetTotalPages() - 1;
945       } else if( direction == Dali::Toolkit::Control::Right )
946       {
947         newPage = 0;
948       }
949     }
950
951     // Scroll to the page in the given direction
952     mScrollView.ScrollTo(newPage);
953
954     if( direction == Dali::Toolkit::Control::Left )
955     {
956       // Work out the cell position for the last tile
957       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
958       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
959       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
960
961       // Move the focus to the last tile in the new page.
962       nextFocusActor = mPages[newPage].GetChildAt(colPos * EXAMPLES_PER_ROW + rowPos);
963     }
964     else
965     {
966       // Move the focus to the first tile in the new page.
967       nextFocusActor = mPages[newPage].GetChildAt(0);
968     }
969   }
970
971   return nextFocusActor;
972 }
973
974 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
975 {
976   if(activatedActor)
977   {
978     mPressedActor = activatedActor;
979
980     // Activate the current focused actor;
981     TouchEvent touchEventUp;
982     touchEventUp.points.push_back( TouchPoint ( 0, TouchPoint::Up, 0.0f, 0.0f ) );
983     OnTilePressed(mPressedActor, touchEventUp);
984   }
985 }
986
987 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
988 {
989   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
990   return true;
991 }
992
993 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
994 {
995   if ( !mVersionPopupShown )
996   {
997     if ( !mVersionPopup )
998     {
999       std::ostringstream stream;
1000       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")" << std::endl << std::endl;
1001       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")" << std::endl << std::endl;
1002       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")";
1003
1004       mVersionPopup = Dali::Toolkit::Popup::New();
1005       mVersionPopup.SetParentOrigin( ParentOrigin::CENTER );
1006       mVersionPopup.SetAnchorPoint( AnchorPoint::CENTER );
1007       mVersionPopup.SetResizePolicy( FILL_TO_PARENT, WIDTH );
1008       mVersionPopup.SetSizeMode( SIZE_RELATIVE_TO_PARENT );
1009       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
1010       mVersionPopup.SetResizePolicy( FIT_TO_CHILDREN, HEIGHT );
1011       mVersionPopup.SetTitle( stream.str() );
1012       mVersionPopup.HideTail();
1013       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
1014       mVersionPopup.HiddenSignal().Connect( this, &DaliTableView::PopupHidden );
1015
1016       mVersionPopup.MarkDirtyForRelayout();
1017     }
1018
1019     mVersionPopup.Show();
1020     mVersionPopupShown = true;
1021   }
1022 }
1023
1024 void DaliTableView::HideVersionPopup()
1025 {
1026   if ( mVersionPopup )
1027   {
1028     mVersionPopup.Hide();
1029   }
1030 }
1031
1032 void DaliTableView::PopupHidden()
1033 {
1034   if ( mVersionPopup )
1035   {
1036     mVersionPopupShown = false;
1037   }
1038 }