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