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