Show DALi version info when the logo is tapped
[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.5f / 0.9f;
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.9f, 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 float BACKGROUND_SIZE_SCALE = 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 std::string             DEFAULT_TEXT_STYLE_FONT_FAMILY("HelveticaNeue");
91 const std::string             DEFAULT_TEXT_STYLE_FONT_STYLE("Regular");
92 const Dali::PointSize         DEFAULT_TEXT_STYLE_POINT_SIZE( 8.0f );
93 const Dali::TextStyle::Weight DEFAULT_TEXT_STYLE_WEIGHT(Dali::TextStyle::REGULAR);
94 const Dali::Vector4           DEFAULT_TEXT_STYLE_COLOR(0.7f, 0.7f, 0.7f, 1.0f);
95
96 const std::string             TABLE_TEXT_STYLE_FONT_FAMILY("HelveticaNeue");
97 const std::string             TABLE_TEXT_STYLE_FONT_STYLE("Regular");
98 const Dali::PointSize         TABLE_TEXT_STYLE_POINT_SIZE( 8.0f );
99 const Dali::TextStyle::Weight TABLE_TEXT_STYLE_WEIGHT(Dali::TextStyle::LIGHT);
100 const Dali::Vector4           TABLE_TEXT_STYLE_COLOR(0.0f, 0.0f, 0.0f, 1.0f);
101
102 Vector3 ScalePointSize(const Vector3& vec)
103 {
104   return Vector3( DemoHelper::ScalePointSize( vec.x ), DemoHelper::ScalePointSize( vec.y ), DemoHelper::ScalePointSize( vec.z ) );
105 }
106
107 #define DP(x) DemoHelper::ScalePointSize(x)
108
109 TextStyle GetTableTextStyle()
110 {
111   TextStyle textStyle;
112   textStyle.SetFontName(TABLE_TEXT_STYLE_FONT_FAMILY);
113   textStyle.SetFontStyle(TABLE_TEXT_STYLE_FONT_STYLE);
114   textStyle.SetFontPointSize( Dali::PointSize(DemoHelper::ScalePointSize(TABLE_TEXT_STYLE_POINT_SIZE)));
115   textStyle.SetWeight(TABLE_TEXT_STYLE_WEIGHT);
116   textStyle.SetTextColor(TABLE_TEXT_STYLE_COLOR);
117   return textStyle;
118 }
119
120 /**
121  * Creates the background image
122  */
123 ImageActor CreateBackground( std::string imagePath )
124 {
125   Image image = ResourceImage::New( imagePath );
126   ImageActor background = ImageActor::New( image );
127
128   background.SetAnchorPoint( AnchorPoint::CENTER );
129   background.SetParentOrigin( ParentOrigin::CENTER );
130   background.SetZ( -1.0f );
131
132   return background;
133 }
134
135 // These values depend on the tile image
136 const float IMAGE_BORDER_LEFT = 11.0f;
137 const float IMAGE_BORDER_RIGHT = IMAGE_BORDER_LEFT;
138 const float IMAGE_BORDER_TOP = IMAGE_BORDER_LEFT;
139 const float IMAGE_BORDER_BOTTOM = IMAGE_BORDER_LEFT;
140
141 /**
142  * Constraint to return a position for a bubble based on the scroll value and vertical wrapping.
143  */
144 struct AnimateBubbleConstraint
145 {
146 public:
147   AnimateBubbleConstraint( const Vector3& initialPos, float scale, float size )
148       : mInitialX( initialPos.x ),
149         mScale( scale ),
150         mShapeSize( size )
151   {
152   }
153
154   Vector3 operator()( const Vector3& current, const PropertyInput& scrollProperty, const PropertyInput& parentSize )
155   {
156     Vector3 pos( current );
157
158     // Wrap bubbles verically.
159     if( pos.y + mShapeSize * 0.5f < -parentSize.GetVector3().y * 0.5f )
160     {
161       pos.y += parentSize.GetVector3().y + mShapeSize;
162     }
163
164     // Bubbles X position moves parallax to horizontal
165     // panning by a scale factor unique to each bubble.
166     pos.x = mInitialX + ( scrollProperty.GetVector3().x * mScale );
167     return pos;
168   }
169
170 private:
171   float mInitialX;
172   float mScale;
173   float mShapeSize;
174 };
175
176 bool CompareByTitle( const Example& lhs, const Example& rhs )
177 {
178   return lhs.title < rhs.title;
179 }
180
181 } // namespace
182
183 DaliTableView::DaliTableView( Application& application )
184 : mApplication( application ),
185   mBackgroundLayer(),
186   mRootActor(),
187   mRotateAnimation(),
188   mBackground(),
189   mLogo(),
190   mPressedAnimation(),
191   mScrollViewLayer(),
192   mScrollView(),
193   mScrollViewEffect(),
194   mScrollRulerX(),
195   mScrollRulerY(),
196   mButtons(),
197   mPressedActor(),
198   mAnimationTimer(),
199   mLogoTapDetector(),
200   mVersionPopup(),
201   mButtonsPageRelativeSize(),
202   mPages(),
203   mTableViewImages(),
204   mBackgroundActors(),
205   mBackgroundAnimations(),
206   mExampleList(),
207   mExampleMap(),
208   mBackgroundImagePath( DEFAULT_BACKGROUND_IMAGE_PATH ),
209   mTotalPages(),
210   mScrolling( false ),
211   mSortAlphabetically( false ),
212   mBackgroundAnimsPlaying( false ),
213   mVersionPopupShown( false )
214 {
215   application.InitSignal().Connect( this, &DaliTableView::Initialize );
216 }
217
218 DaliTableView::~DaliTableView()
219 {
220 }
221
222 void DaliTableView::AddExample( Example example )
223 {
224   mExampleList.push_back( example );
225   mExampleMap[ example.name ] = example;
226 }
227
228 void DaliTableView::SetBackgroundPath( std::string imagePath )
229 {
230   mBackgroundImagePath = imagePath;
231 }
232
233 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
234 {
235   mSortAlphabetically = sortAlphabetically;
236 }
237
238 void DaliTableView::Initialize( Application& application )
239 {
240   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
241
242   const Vector2 stageSize = Stage::GetCurrent().GetSize();
243
244   // Background
245   mBackground = CreateBackground( mBackgroundImagePath );
246   // set same size as parent actor
247   mBackground.SetSize( stageSize );
248   Stage::GetCurrent().Add( mBackground );
249
250   // Render entire content as overlays, as is all on same 2D plane.
251   mRootActor = TableView::New( 4, 1 );
252   mRootActor.SetAnchorPoint( AnchorPoint::CENTER );
253   mRootActor.SetParentOrigin( ParentOrigin::CENTER );
254   Stage::GetCurrent().Add( mRootActor );
255
256   // Toolbar at top
257   Dali::Toolkit::ToolBar toolbar;
258   Dali::Layer toolBarLayer = DemoHelper::CreateToolbar(toolbar,
259                                                        DEFAULT_TOOLBAR_IMAGE_PATH,
260                                                        DEFAULT_TOOLBAR_TEXT,
261                                                        DemoHelper::DEFAULT_VIEW_STYLE,
262                                                        DemoHelper::GetDefaultTextStyle());
263
264   mRootActor.AddChild( toolBarLayer, TableView::CellPosition( 0, 0 ) );
265   const float toolbarHeight = DemoHelper::DEFAULT_VIEW_STYLE.mToolBarHeight;
266   mRootActor.SetFixedHeight( 0, toolbarHeight );
267
268   // Add logo
269   mLogo = CreateLogo( LOGO_PATH );
270   const float paddingHeight = ( ( 1.f-TABLE_RELATIVE_SIZE.y ) * stageSize.y );
271   const float logoMargin = paddingHeight * LOGO_MARGIN_RATIO;
272   const float logoHeight = mLogo.GetImage().GetHeight() + logoMargin;
273   mRootActor.SetFixedHeight( 1, logoHeight );
274
275   // Show version in a popup when log is tapped
276   mLogoTapDetector = TapGestureDetector::New();
277   mLogoTapDetector.Attach( mLogo );
278   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
279
280   const float bottomMargin = paddingHeight * BOTTOM_PADDING_RATIO;
281   mButtonsPageRelativeSize = Vector3( TABLE_RELATIVE_SIZE.x, 1.f - ( toolbarHeight + logoHeight + bottomMargin) / stageSize.height, TABLE_RELATIVE_SIZE.z );
282   mRootActor.SetFixedHeight( 2, mButtonsPageRelativeSize.y * stageSize.height );
283
284   Alignment alignment = Alignment::New();
285   alignment.Add(mLogo);
286   mRootActor.AddChild( alignment, TableView::CellPosition( 1, 0 ) );
287
288   // scrollview occupying the majority of the screen
289   mScrollView = ScrollView::New();
290
291   mScrollView.SetAnchorPoint( AnchorPoint::CENTER );
292   mScrollView.SetParentOrigin( ParentOrigin::CENTER );
293   // Note: Currently, changing mScrollView to use SizeMode RELATIVE_TO_PARENT
294   // will cause scroll ends to appear in the wrong position.
295   mScrollView.ApplyConstraint( Dali::Constraint::New<Dali::Vector3>( Dali::Actor::Property::SIZE, Dali::ParentSource( Dali::Actor::Property::SIZE ), Dali::RelativeToConstraint( SCROLLVIEW_RELATIVE_SIZE ) ) );
296   mScrollView.SetAxisAutoLock( true );
297   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
298   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
299   mScrollView.TouchedSignal().Connect( this, &DaliTableView::OnScrollTouched );
300
301   mScrollViewLayer = Layer::New();
302   mScrollViewLayer.SetAnchorPoint( AnchorPoint::CENTER );
303   mScrollViewLayer.SetParentOrigin( ParentOrigin::CENTER );
304   mScrollViewLayer.SetDrawMode( DrawMode::OVERLAY );
305
306   // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
307   SetupBackground( mScrollView, mScrollViewLayer, stageSize );
308
309   mScrollViewLayer.Add( mScrollView );
310   mRootActor.AddChild( mScrollViewLayer, TableView::CellPosition( 2, 0 ) );
311
312   // Add scroll view effect and setup constraints on pages
313   ApplyScrollViewEffect();
314
315   // Add pages and tiles
316   Populate();
317
318   // Remove constraints for inner cube effect
319   ApplyCubeEffectToActors();
320
321   // Set initial orientation
322   unsigned int degrees = application.GetOrientation().GetDegrees();
323   Rotate( degrees );
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   Dali::Orientation orientation = winHandle.GetOrientation();
332   orientation.ChangedSignal().Connect( this, &DaliTableView::OrientationChanged );
333
334   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
335
336   //
337   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
338   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
339   mAnimationTimer.Start();
340   mBackgroundAnimsPlaying = true;
341
342   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
343   KeyboardFocusManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
344 }
345
346 void DaliTableView::ApplyCubeEffectToActors()
347 {
348   for( ActorIter pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
349   {
350     Actor page = *pageIter;
351
352     unsigned int numChildren = page.GetChildCount();
353     Actor pageActor = page;
354     for( unsigned int i=0; i<numChildren; ++i)
355     {
356       // Remove old effect's manual constraints.
357       Actor child = pageActor.GetChildAt(i);
358       if( child )
359       {
360         ApplyCubeEffectToActor( child );
361       }
362     }
363   }
364 }
365 void DaliTableView::Populate()
366 {
367   const Vector2 stageSize = Stage::GetCurrent().GetSize();
368
369   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
370
371   // Populate ScrollView.
372   if( mExampleList.size() > 0 )
373   {
374     if( mSortAlphabetically )
375     {
376       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
377     }
378
379     unsigned int exampleCount = 0;
380     ExampleListConstIter iter = mExampleList.begin();
381
382     for( int t = 0; t < mTotalPages; t++ )
383     {
384       // Create Table. (contains up to 9 Examples)
385       Actor page = Actor::New();
386
387       // Add tableView to container.
388       mScrollView.Add( page );
389
390       page.SetAnchorPoint( AnchorPoint::CENTER );
391       page.SetParentOrigin( ParentOrigin::CENTER );
392       page.SetSizeMode( SIZE_EQUAL_TO_PARENT );
393
394       // add cells to table
395       const float margin = 4.0f;
396
397       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
398       const Size tileSize((stageSize.x * mButtonsPageRelativeSize.x / EXAMPLES_PER_ROW) - margin, (stageSize.y * mButtonsPageRelativeSize.y / ROWS_PER_PAGE) - margin );
399
400       for(int row = 0; row < ROWS_PER_PAGE; row++)
401       {
402         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
403         {
404           const Example& example = ( *iter );
405
406           Actor tile = CreateTile( example.name, example.title, tileSize, true );
407           FocusManager focusManager = FocusManager::Get();
408           focusManager.SetFocusOrder( tile, ++exampleCount );
409           focusManager.SetAccessibilityAttribute( tile, Dali::Toolkit::FocusManager::ACCESSIBILITY_LABEL,
410                                                   example.title );
411           focusManager.SetAccessibilityAttribute( tile, Dali::Toolkit::FocusManager::ACCESSIBILITY_TRAIT, "Tile" );
412           focusManager.SetAccessibilityAttribute( tile, Dali::Toolkit::FocusManager::ACCESSIBILITY_HINT,
413                                                   "You can run this example" );
414
415           Vector3 position( margin * 0.5f + (tileSize.x + margin) * column - stageSize.width * mButtonsPageRelativeSize.x * 0.5f,
416                            margin * 0.5f + (tileSize.y + margin) * row - stageSize.height * mButtonsPageRelativeSize.y * 0.5f,
417                             0.0f);
418           tile.SetPosition( position + Vector3( tileSize.x, tileSize.y, 0.0f ) * 0.5f );
419           tile.SetSize( tileSize );
420           page.Add( tile );
421
422           iter++;
423
424           if( iter == mExampleList.end() )
425           {
426             break;
427           }
428         }
429
430         if( iter == mExampleList.end() )
431         {
432           break;
433         }
434       }
435
436       // Set tableview position
437       Vector3 pagePos( stageSize.x * mButtonsPageRelativeSize.x * t, 0.0f, 0.0f );
438       page.SetPosition( pagePos );
439
440       mPages.push_back( page );
441
442       if( iter == mExampleList.end() )
443       {
444         break;
445       }
446     }
447   }
448
449   // Update Ruler info.
450   mScrollRulerX = new FixedRuler( stageSize.width * mButtonsPageRelativeSize.x );
451   mScrollRulerY = new DefaultRuler();
452   mScrollRulerX->SetDomain( RulerDomain( 0.0f, mTotalPages * stageSize.width * mButtonsPageRelativeSize.x, true ) );
453   mScrollRulerY->Disable();
454   mScrollView.SetRulerX( mScrollRulerX );
455   mScrollView.SetRulerY( mScrollRulerY );
456 }
457
458 void DaliTableView::OrientationChanged( Orientation orientation )
459 {
460   // TODO: Implement if orientation change required
461 }
462
463 void DaliTableView::Rotate( unsigned int degrees )
464 {
465   // Resize the root actor
466   Vector2 stageSize = Stage::GetCurrent().GetSize();
467   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
468
469   if( degrees == 90 || degrees == 270 )
470   {
471     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
472   }
473
474   if( mRotateAnimation )
475   {
476     mRotateAnimation.Stop();
477     mRotateAnimation.Clear();
478   }
479
480   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
481   mRotateAnimation.RotateTo( mRootActor, Degree( 360 - degrees ), Vector3::ZAXIS, AlphaFunctions::EaseOut );
482   mRotateAnimation.Resize( mRootActor, targetSize, AlphaFunctions::EaseOut );
483   mRotateAnimation.Play();
484 }
485
486 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Size& parentSize, bool addBackground )
487 {
488   Actor tile = Actor::New();
489   tile.SetName( name );
490   tile.SetAnchorPoint( AnchorPoint::CENTER );
491   tile.SetParentOrigin( ParentOrigin::CENTER );
492
493   // create background image
494   if( addBackground )
495   {
496     Image bg = ResourceImage::New( TILE_BACKGROUND );
497     ImageActor image = ImageActor::New( bg );
498     image.SetAnchorPoint( AnchorPoint::CENTER );
499     image.SetParentOrigin( ParentOrigin::CENTER );
500     // make the image 100% of tile
501     image.SetSizeMode( SIZE_EQUAL_TO_PARENT );
502     // move image back to get text appear in front
503     image.SetZ( -1 );
504     image.SetStyle( ImageActor::STYLE_NINE_PATCH );
505     image.SetNinePatchBorder( Vector4( IMAGE_BORDER_LEFT, IMAGE_BORDER_TOP, IMAGE_BORDER_RIGHT, IMAGE_BORDER_BOTTOM ) );
506     tile.Add( image );
507
508     // Add stencil
509     ImageActor stencil = NewStencilImage();
510     stencil.SetSizeMode( SIZE_EQUAL_TO_PARENT );
511     image.Add( stencil );
512   }
513
514   TextView text = TextView::New( title );
515   text.SetAnchorPoint( AnchorPoint::CENTER );
516   text.SetParentOrigin( ParentOrigin::CENTER );
517   text.SetWidthExceedPolicy( Toolkit::TextView::ShrinkToFit );
518   text.SetMultilinePolicy( Toolkit::TextView::SplitByWord );
519   text.SetLineJustification( Toolkit::TextView::Center );
520   text.SetTextAlignment( Toolkit::Alignment::Type( Alignment::HorizontalCenter | Alignment::VerticalCenter ) );
521   text.SetColor( Color::WHITE );
522   text.SetZ( 1 );
523   // make the text 90% of tile
524   text.SetSize( 0.9f * parentSize.width, 0.9f * parentSize.height );
525   text.SetStyleToCurrentText( GetTableTextStyle() );
526   text.SetSnapshotModeEnabled( false );
527   tile.Add( text );
528
529   // Set the tile to be keyboard focusable
530   tile.SetKeyboardFocusable(true);
531
532   // connect to the touch events
533   tile.TouchedSignal().Connect( this, &DaliTableView::OnTilePressed );
534   tile.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
535
536   return tile;
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.ScaleTo( content, Vector3( 0.9f, 0.9f, 1.0f ), AlphaFunctions::EaseInOut, 0.0f,
596                                  BUTTON_PRESS_ANIMATION_TIME * 0.5f );
597       mPressedAnimation.ScaleTo( content, Vector3::ONE, AlphaFunctions::EaseInOut, BUTTON_PRESS_ANIMATION_TIME * 0.5f,
598                                  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   ApplyCubeEffectToActors();
659 }
660
661 bool DaliTableView::OnScrollTouched( Actor actor, const TouchEvent& event )
662 {
663   const TouchPoint& point = event.GetPoint( 0 );
664   if( TouchPoint::Down == point.state )
665   {
666     mPressedActor = actor;
667   }
668
669   return false;
670 }
671
672 void DaliTableView::ApplyScrollViewEffect()
673 {
674   // Remove old effect if exists.
675
676   if( mScrollViewEffect )
677   {
678     mScrollView.RemoveEffect( mScrollViewEffect );
679   }
680
681   // Just one effect for now
682   SetupInnerPageCubeEffect();
683
684   mScrollView.ApplyEffect( mScrollViewEffect );
685 }
686
687 void DaliTableView::SetupInnerPageCubeEffect()
688 {
689   mScrollViewEffect = ScrollViewCubeEffect::New();
690   mScrollView.SetScrollSnapDuration( EFFECT_SNAP_DURATION );
691   mScrollView.SetScrollFlickDuration( EFFECT_FLICK_DURATION );
692   mScrollView.RemoveConstraintsFromChildren();
693 }
694
695 void DaliTableView::ApplyCubeEffectToActor( Actor actor )
696 {
697   actor.RemoveConstraints();
698
699   ScrollViewCubeEffect cubeEffect = ScrollViewCubeEffect::DownCast(mScrollViewEffect);
700   cubeEffect.ApplyToActor( actor,
701                            ScalePointSize( ( rand() & 1 ) ? ANCHOR_3DEFFECT_STYLE0 : ANCHOR_3DEFFECT_STYLE1 ),
702                            ANGLE_SWING_3DEFFECT,
703                            POSITION_SWING_3DEFFECT * Vector2(Stage::GetCurrent().GetSize()));
704 }
705
706 void DaliTableView::OnKeyEvent( const KeyEvent& event )
707 {
708   if( event.state == KeyEvent::Down )
709   {
710     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
711     {
712       if ( mVersionPopup && mVersionPopupShown )
713       {
714         HideVersionPopup();
715       }
716       else
717       {
718         mApplication.Quit();
719       }
720     }
721   }
722 }
723
724 void DaliTableView::SetupBackground( Actor bubbleContainer, Actor backgroundLayer, const Vector2& size )
725 {
726   // Create distance field shape.
727   BufferImage distanceField;
728   Size imageSize( 512, 512 );
729   CreateShapeImage( CIRCLE, imageSize, distanceField );
730
731   // Create solid background colour.
732   ImageActor backgroundColourActor = Dali::Toolkit::CreateSolidColorActor( BACKGROUND_COLOR );
733   backgroundColourActor.SetAnchorPoint( AnchorPoint::CENTER );
734   backgroundColourActor.SetParentOrigin( ParentOrigin::CENTER );
735   backgroundColourActor.SetSize( size * BACKGROUND_SIZE_SCALE );
736   backgroundColourActor.SetZ( BACKGROUND_Z );
737   backgroundColourActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
738   backgroundLayer.Add( backgroundColourActor );
739
740   // Add bubbles to the bubbleContainer.
741   // Note: The bubbleContainer is parented externally to this function.
742   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES, distanceField, size );
743 }
744
745 void DaliTableView::AddBackgroundActors( Actor layer, int count, BufferImage distanceField, const Dali::Vector2& size )
746 {
747   for( int i = 0; i < count; ++i )
748   {
749     float randSize = Random::Range( 10.0f, 400.0f );
750     float hue = Random::Range( 0.3f, 1.0f );
751     Vector4 randColour( hue, hue*0.5, 0.0f, Random::Range( 0.3f, 0.6f ));
752
753     ImageActor dfActor = ImageActor::New( distanceField );
754     mBackgroundActors.push_back( dfActor );
755     dfActor.SetSize( Vector2( randSize, randSize ) );
756     dfActor.SetParentOrigin( ParentOrigin::CENTER );
757
758     Toolkit::DistanceFieldEffect effect = Toolkit::DistanceFieldEffect::New();
759     dfActor.SetShaderEffect( effect );
760     dfActor.SetColor( randColour );
761     effect.SetOutlineParams( Vector2( 0.55f, 0.00f ) );
762     effect.SetSmoothingEdge( 0.5f );
763     layer.Add( dfActor );
764
765     // Setup animation
766     Vector3 actorPos(
767         Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.5f * BACKGROUND_SPREAD_SCALE ),
768         Random::Range( -size.y * 0.5f - randSize, size.y * 0.5f + randSize ),
769         Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
770     dfActor.SetPosition( actorPos );
771
772     // Define bubble horizontal parallax and vertical wrapping
773     Constraint animConstraint = Constraint::New < Vector3 > ( Actor::Property::POSITION,
774       Source( mScrollView, mScrollView.GetPropertyIndex( ScrollView::SCROLL_POSITION_PROPERTY_NAME ) ),
775       Dali::ParentSource( Dali::Actor::Property::SIZE ),
776       AnimateBubbleConstraint( actorPos, Random::Range( -0.85f, 0.25f ), randSize ) );
777     dfActor.ApplyConstraint( animConstraint );
778
779     // Kickoff animation
780     Animation animation = Animation::New( Random::Range( 40.0f, 200.0f ) );
781     KeyFrames keyframes = KeyFrames::New();
782     keyframes.Add( 0.0f, actorPos );
783     Vector3 toPos( actorPos );
784     toPos.y -= ( size.y + randSize );
785     keyframes.Add( 1.0f, toPos );
786     animation.AnimateBetween( Property( dfActor, Actor::Property::POSITION ), keyframes );
787     animation.SetLooping( true );
788     animation.Play();
789     mBackgroundAnimations.push_back( animation );
790   }
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::BOTTOM_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.Pause();
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.SetTitle( stream.str() );
998       mVersionPopup.SetParentOrigin( ParentOrigin::CENTER );
999       mVersionPopup.SetAnchorPoint( AnchorPoint::CENTER );
1000       mVersionPopup.HideTail();
1001       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
1002       mVersionPopup.HiddenSignal().Connect( this, &DaliTableView::PopupHidden );
1003
1004       Dali::Stage::GetCurrent().Add( mVersionPopup );
1005     }
1006
1007     mVersionPopup.Show();
1008     mVersionPopupShown = true;
1009   }
1010 }
1011
1012 void DaliTableView::HideVersionPopup()
1013 {
1014   if ( mVersionPopup )
1015   {
1016     mVersionPopup.Hide();
1017   }
1018 }
1019
1020 void DaliTableView::PopupHidden()
1021 {
1022   if ( mVersionPopup )
1023   {
1024     mVersionPopupShown = false;
1025   }
1026 }