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