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