Merge branch 'devel/master(1.1.39)' into tizen
[platform/core/uifw/dali-demo.git] / demo / dali-table-view.cpp
1 /*
2  * Copyright (c) 2015 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/devel-api/images/distance-field.h>
26 #include <dali-toolkit/devel-api/shader-effects/alpha-discard-effect.h>
27 #include <dali-toolkit/devel-api/shader-effects/distance-field-effect.h>
28
29 // INTERNAL INCLUDES
30 #include "shared/view.h"
31
32 using namespace Dali;
33 using namespace Dali::Toolkit;
34
35 ///////////////////////////////////////////////////////////////////////////////
36
37 namespace
38 {
39
40 const std::string LOGO_PATH( DEMO_IMAGE_DIR "Logo-for-demo.png" );
41 const std::string DEFAULT_TOOLBAR_IMAGE_PATH( DEMO_IMAGE_DIR "top-bar.png" );
42 const std::string TILE_BACKGROUND(DEMO_IMAGE_DIR "item-background.9.png");
43 const std::string TILE_BACKGROUND_ALPHA(DEMO_IMAGE_DIR "item-background-alpha.9.png");
44
45 const char * const DEFAULT_TOOLBAR_TEXT( "TOUCH TO LAUNCH EXAMPLE" );
46
47 const float BUTTON_PRESS_ANIMATION_TIME = 0.25f;                ///< Time to perform button scale effect.
48 const float ROTATE_ANIMATION_TIME = 0.5f;                       ///< Time to perform rotate effect.
49 const int MAX_PAGES = 256;                                      ///< Maximum pages (arbitrary safety limit)
50 const int EXAMPLES_PER_ROW = 3;
51 const int ROWS_PER_PAGE = 3;
52 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
53 const float LOGO_MARGIN_RATIO = 0.1f / 0.3f;
54 const float BOTTOM_PADDING_RATIO = 0.4f / 0.9f;
55 const Vector3 SCROLLVIEW_RELATIVE_SIZE(0.9f, 1.0f, 0.8f );     ///< ScrollView's relative size to its parent
56 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.
57 const float STENCIL_RELATIVE_SIZE = 1.0f;
58
59 const float EFFECT_SNAP_DURATION = 0.66f;                       ///< Scroll Snap Duration for Effects
60 const float EFFECT_FLICK_DURATION = 0.5f;                       ///< Scroll Flick Duration for Effects
61 const Vector3 ANGLE_CUBE_PAGE_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
62
63 const Vector4 BUBBLE_COLOR[] =
64 {
65   Vector4( 0.3255f, 0.3412f, 0.6353f, 0.38f ),
66   Vector4( 0.3647f, 0.7569f, 0.8157f, 0.38f ),
67   Vector4( 0.3804f, 0.7412f, 0.6510f, 0.38f ),
68   Vector4( 1.f, 1.f, 1.f, 0.2f )
69 };
70 const int NUMBER_OF_BUBBLE_COLOR( sizeof(BUBBLE_COLOR) / sizeof(BUBBLE_COLOR[0]) );
71
72 const int NUM_BACKGROUND_IMAGES = 18;
73 const float BACKGROUND_SWIPE_SCALE = 0.025f;
74 const float BACKGROUND_SPREAD_SCALE = 1.5f;
75 const float SCALE_MOD = 1000.0f * Math::PI * 2.0f;
76 const float SCALE_SPEED = 10.0f;
77 const float SCALE_SPEED_SIN = 0.1f;
78
79 const unsigned int BACKGROUND_ANIMATION_DURATION = 15000; // 15 secs
80
81 const Vector4 BACKGROUND_COLOR( 0.3569f, 0.5451f, 0.7294f, 1.0f );
82
83 const float BUBBLE_MIN_Z = -1.0;
84 const float BUBBLE_MAX_Z = 0.0f;
85
86 /**
87  * Creates the background image
88  */
89 Control CreateBackground( std::string stylename )
90 {
91   Control background = Control::New();
92   Stage::GetCurrent().Add( background );
93   background.SetProperty( Control::Property::STYLE_NAME,stylename);
94   background.SetName( "BACKGROUND" );
95   background.SetAnchorPoint( AnchorPoint::CENTER );
96   background.SetParentOrigin( ParentOrigin::CENTER );
97   background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
98
99   return background;
100 }
101
102 /**
103  * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
104  */
105 struct AnimateBubbleConstraint
106 {
107 public:
108   AnimateBubbleConstraint( const Vector3& initialPos, float scale )
109   : mInitialX( initialPos.x ),
110     mScale( scale )
111   {
112   }
113
114   void operator()( Vector3& position, const PropertyInputContainer& inputs )
115   {
116     const Vector3& parentSize = inputs[1]->GetVector3();
117     const Vector3& childSize = inputs[2]->GetVector3();
118
119     // Wrap bubbles vertically.
120     float range = parentSize.y + childSize.y;
121     // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
122     position.y -= range * ( floor( position.y / range ) + 0.5f );
123
124     // Bubbles X position moves parallax to horizontal
125     // panning by a scale factor unique to each bubble.
126     position.x = mInitialX + ( inputs[0]->GetVector2().x * mScale );
127   }
128
129 private:
130   float mInitialX;
131   float mScale;
132 };
133
134 bool CompareByTitle( const Example& lhs, const Example& rhs )
135 {
136   return lhs.title < rhs.title;
137 }
138
139 } // namespace
140
141 DaliTableView::DaliTableView( Application& application )
142 : mApplication( application ),
143   mBackgroundLayer(),
144   mRootActor(),
145   mRotateAnimation(),
146   mPressedAnimation(),
147   mScrollViewLayer(),
148   mScrollView(),
149   mScrollViewEffect(),
150   mScrollRulerX(),
151   mScrollRulerY(),
152   mPressedActor(),
153   mAnimationTimer(),
154   mLogoTapDetector(),
155   mVersionPopup(),
156   mPages(),
157   mBackgroundAnimations(),
158   mExampleList(),
159   mTotalPages(),
160   mScrolling( false ),
161   mSortAlphabetically( false ),
162   mBackgroundAnimsPlaying( false )
163 {
164   application.InitSignal().Connect( this, &DaliTableView::Initialize );
165 }
166
167 DaliTableView::~DaliTableView()
168 {
169 }
170
171 void DaliTableView::AddExample( Example example )
172 {
173   mExampleList.push_back( example );
174 }
175
176 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
177 {
178   mSortAlphabetically = sortAlphabetically;
179 }
180
181 void DaliTableView::Initialize( Application& application )
182 {
183   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
184
185   const Vector2 stageSize = Stage::GetCurrent().GetSize();
186
187   // Background
188   Control background = CreateBackground( "launcherbackground" );
189   Stage::GetCurrent().Add( background );
190
191   // Add root actor
192   mRootActor = TableView::New( 4, 1 );
193   mRootActor.SetAnchorPoint( AnchorPoint::CENTER );
194   mRootActor.SetParentOrigin( ParentOrigin::CENTER );
195   mRootActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
196   Stage::GetCurrent().Add( mRootActor );
197
198   // Toolbar at top
199   Dali::Toolkit::ToolBar toolbar;
200   Dali::Layer toolBarLayer = DemoHelper::CreateToolbar(toolbar,
201                                                        DEFAULT_TOOLBAR_IMAGE_PATH,
202                                                        DEFAULT_TOOLBAR_TEXT,
203                                                        DemoHelper::DEFAULT_VIEW_STYLE);
204
205   mRootActor.AddChild( toolBarLayer, TableView::CellPosition( 0, 0 ) );
206   mRootActor.SetFitHeight( 0 );
207
208   // Add logo
209   ImageView logo = CreateLogo( LOGO_PATH );
210   logo.SetName( "LOGO_IMAGE" );
211   logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
212   const float paddingHeight = ( ( 1.f-TABLE_RELATIVE_SIZE.y ) * stageSize.y );
213   const float logoMargin = paddingHeight * LOGO_MARGIN_RATIO;
214
215   // Show version in a popup when log is tapped
216   mLogoTapDetector = TapGestureDetector::New();
217   mLogoTapDetector.Attach( logo );
218   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
219
220   const float bottomMargin = paddingHeight * BOTTOM_PADDING_RATIO;
221
222   Alignment alignment = Alignment::New();
223   alignment.SetName( "LOGO_ALIGNMENT" );
224   alignment.Add( logo );
225   alignment.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
226   alignment.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
227   Actor alignmentActor = alignment;
228   alignmentActor.SetPadding( Padding( 0.0f, 0.0f, logoMargin, logoMargin ));
229   mRootActor.AddChild( alignment, TableView::CellPosition( 1, 0 ) );
230   mRootActor.SetFitHeight( 1 );
231
232   // scrollview occupying the majority of the screen
233   mScrollView = ScrollView::New();
234
235   mScrollView.SetAnchorPoint( AnchorPoint::CENTER );
236   mScrollView.SetParentOrigin( ParentOrigin::CENTER );
237   mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
238   const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * stageSize.width;
239   mScrollView.SetPadding( Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
240
241   mScrollView.SetAxisAutoLock( true );
242   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
243   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
244   mScrollView.TouchSignal().Connect( this, &DaliTableView::OnScrollTouched );
245
246   mScrollViewLayer = Layer::New();
247
248   // Disable the depth test for performance
249   mScrollViewLayer.SetDepthTestDisabled( true );
250   mScrollViewLayer.SetAnchorPoint( AnchorPoint::CENTER );
251   mScrollViewLayer.SetParentOrigin( ParentOrigin::CENTER );
252   mScrollViewLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
253
254   // Create solid background colour.
255   Control backgroundColourActor = Control::New();
256   backgroundColourActor.SetBackgroundColor( BACKGROUND_COLOR );
257   backgroundColourActor.SetAnchorPoint( AnchorPoint::CENTER );
258   backgroundColourActor.SetParentOrigin( ParentOrigin::CENTER );
259   backgroundColourActor.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
260   backgroundColourActor.SetSizeModeFactor( Vector3( 1.0f, 1.5f, 1.0f ) );
261
262   mScrollViewLayer.Add( backgroundColourActor );
263
264   // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
265   Actor bubbleContainer = Actor::New();
266   bubbleContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
267   bubbleContainer.SetAnchorPoint( AnchorPoint::CENTER );
268   bubbleContainer.SetParentOrigin( ParentOrigin::CENTER );
269   backgroundColourActor.Add( bubbleContainer );
270
271   SetupBackground( bubbleContainer );
272
273   Alignment buttonsAlignment = Alignment::New();
274   buttonsAlignment.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
275   buttonsAlignment.Add( mScrollViewLayer );
276
277   mScrollViewLayer.Add( mScrollView );
278
279   mRootActor.AddChild( buttonsAlignment, TableView::CellPosition( 2, 0 ) );
280
281   mRootActor.SetFixedHeight( 3, bottomMargin );
282
283   // Add scroll view effect and setup constraints on pages
284   ApplyScrollViewEffect();
285
286   // Add pages and tiles
287   Populate();
288
289   // Remove constraints for inner cube effect
290   ApplyCubeEffectToPages();
291
292   Dali::Window winHandle = application.GetWindow();
293   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
294   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
295   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
296   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
297
298   // Set initial orientation
299   unsigned int degrees = 0;
300   Rotate( degrees );
301
302   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
303
304   // Background animation
305   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
306   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
307   mAnimationTimer.Start();
308   mBackgroundAnimsPlaying = true;
309
310   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
311   KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
312   AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
313 }
314
315 void DaliTableView::ApplyCubeEffectToPages()
316 {
317   ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
318   unsigned int pageCount(0);
319   for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
320   {
321     Actor page = *pageIter;
322     effect.ApplyToPage( page, pageCount++ );
323   }
324 }
325
326 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
327 {
328
329 }
330
331 void DaliTableView::Populate()
332 {
333   const Vector2 stageSize = Stage::GetCurrent().GetSize();
334
335   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
336
337   // Populate ScrollView.
338   if( mExampleList.size() > 0 )
339   {
340     if( mSortAlphabetically )
341     {
342       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
343     }
344
345     unsigned int exampleCount = 0;
346     ExampleListConstIter iter = mExampleList.begin();
347
348     for( int t = 0; t < mTotalPages; t++ )
349     {
350       // Create Table
351       TableView page = TableView::New( ROWS_PER_PAGE, EXAMPLES_PER_ROW );
352       page.SetAnchorPoint( AnchorPoint::CENTER );
353       page.SetParentOrigin( ParentOrigin::CENTER );
354       page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
355       mScrollView.Add( page );
356
357       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
358       const float margin = 2.0f;
359       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
360
361       for(int row = 0; row < ROWS_PER_PAGE; row++)
362       {
363         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
364         {
365           const Example& example = ( *iter );
366
367           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), true );
368           AccessibilityManager accessibilityManager = AccessibilityManager::Get();
369           accessibilityManager.SetFocusOrder( tile, ++exampleCount );
370           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
371                                                   example.title );
372           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
373           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT,
374                                                   "You can run this example" );
375
376           tile.SetPadding( Padding( margin, margin, margin, margin ) );
377
378           page.AddChild( tile, TableView::CellPosition( row, column ) );
379
380           iter++;
381
382           if( iter == mExampleList.end() )
383           {
384             break;
385           }
386         }
387
388         if( iter == mExampleList.end() )
389         {
390           break;
391         }
392       }
393
394       // Set tableview position
395       Vector3 pagePos( stageSize.width * TABLE_RELATIVE_SIZE.x * t, 0.0f, 0.0f );
396       page.SetPosition( pagePos );
397
398       mPages.push_back( page );
399
400       if( iter == mExampleList.end() )
401       {
402         break;
403       }
404     }
405   }
406
407   // Update Ruler info.
408   mScrollRulerX = new FixedRuler( stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f );
409   mScrollRulerY = new DefaultRuler();
410   mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
411   mScrollRulerY->Disable();
412   mScrollView.SetRulerX( mScrollRulerX );
413   mScrollView.SetRulerY( mScrollRulerY );
414 }
415
416 void DaliTableView::Rotate( unsigned int degrees )
417 {
418   // Resize the root actor
419   Vector2 stageSize = Stage::GetCurrent().GetSize();
420   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
421
422   if( degrees == 90 || degrees == 270 )
423   {
424     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
425   }
426
427   if( mRotateAnimation )
428   {
429     mRotateAnimation.Stop();
430     mRotateAnimation.Clear();
431   }
432
433   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
434   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
435   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
436   mRotateAnimation.Play();
437 }
438
439 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, bool addBackground )
440 {
441   Actor content = Actor::New();
442   content.SetName( name );
443   content.SetAnchorPoint( AnchorPoint::CENTER );
444   content.SetParentOrigin( ParentOrigin::CENTER );
445   content.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
446   content.SetSizeModeFactor( sizeMultiplier );
447
448   // create background image
449   if( addBackground )
450   {
451     ImageView image = ImageView::New( TILE_BACKGROUND );
452     image.SetAnchorPoint( AnchorPoint::CENTER );
453     image.SetParentOrigin( ParentOrigin::CENTER );
454     // make the image 100% of tile
455     image.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
456
457     content.Add( image );
458
459     // Add stencil
460     Toolkit::ImageView stencil = NewStencilImage();
461     stencil.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
462     image.Add( stencil );
463   }
464
465   TextLabel label = TextLabel::New();
466   label.SetAnchorPoint( AnchorPoint::TOP_LEFT );
467   label.SetProperty( Control::Property::STYLE_NAME, "launcherlabel" );
468   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
469   label.SetProperty( TextLabel::Property::TEXT, title );
470   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
471   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
472   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
473   content.Add( label );
474
475   // Set the tile to be keyboard focusable
476   content.SetKeyboardFocusable(true);
477
478   // connect to the touch events
479   content.TouchSignal().Connect( this, &DaliTableView::OnTilePressed );
480   content.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
481
482   return content;
483 }
484
485 Toolkit::ImageView DaliTableView::NewStencilImage()
486 {
487   Toolkit::ImageView stencil = ImageView::New( TILE_BACKGROUND_ALPHA );
488
489   stencil.SetParentOrigin( ParentOrigin::CENTER );
490   stencil.SetAnchorPoint( AnchorPoint::CENTER );
491   stencil.SetDrawMode( DrawMode::STENCIL );
492
493   Property::Map shaderEffect = CreateAlphaDiscardEffect();
494   stencil.SetProperty( Toolkit::ImageView::Property::IMAGE, shaderEffect );
495
496   return stencil;
497 }
498
499 bool DaliTableView::OnTilePressed( Actor actor, const TouchData& event )
500 {
501   return DoTilePress( actor, event.GetState( 0 ) );
502 }
503
504 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
505 {
506   bool consumed = false;
507
508   if( PointState::DOWN == pointState )
509   {
510     mPressedActor = actor;
511     consumed = true;
512   }
513
514   // A button press is only valid if the Down & Up events
515   // both occurred within the button.
516   if( ( PointState::UP == pointState ) &&
517       ( mPressedActor == actor ) )
518   {
519     // ignore Example button presses when scrolling or button animating.
520     if( ( !mScrolling ) && ( !mPressedAnimation ) )
521     {
522       std::string name = actor.GetName();
523       const ExampleListIter end = mExampleList.end();
524       for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
525       {
526         if( (*iter).name == name )
527         {
528           // do nothing, until pressed animation finished.
529           consumed = true;
530           break;
531         }
532       }
533     }
534
535     if( consumed )
536     {
537       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
538       mPressedAnimation.SetEndAction( Animation::Discard );
539
540       // scale the content actor within the Tile, as to not affect the placement within the Table.
541       Actor content = actor.GetChildAt(0);
542       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.9f, 0.9f, 1.0f ), AlphaFunction::EASE_IN_OUT,
543                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
544       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
545                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
546       mPressedAnimation.Play();
547       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
548     }
549   }
550   return consumed;
551 }
552
553 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
554 {
555   mPressedAnimation.Reset();
556   if( mPressedActor )
557   {
558     std::string name = mPressedActor.GetName();
559
560     std::stringstream stream;
561     stream << DEMO_EXAMPLE_BIN << name.c_str();
562     pid_t pid = fork();
563     if( pid == 0)
564     {
565       execlp( stream.str().c_str(), name.c_str(), NULL );
566       DALI_ASSERT_ALWAYS(false && "exec failed!");
567     }
568     mPressedActor.Reset();
569   }
570 }
571
572 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
573 {
574   mScrolling = true;
575
576   PlayAnimation();
577 }
578
579 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
580 {
581   mScrolling = false;
582
583   // move focus to 1st item of new page
584   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
585   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
586 }
587
588 bool DaliTableView::OnScrollTouched( Actor actor, const TouchData& event )
589 {
590   if( PointState::DOWN == event.GetState( 0 ) )
591   {
592     mPressedActor = actor;
593   }
594
595   return false;
596 }
597
598 void DaliTableView::ApplyScrollViewEffect()
599 {
600   // Remove old effect if exists.
601
602   if( mScrollViewEffect )
603   {
604     mScrollView.RemoveEffect( mScrollViewEffect );
605   }
606
607   // Just one effect for now
608   SetupInnerPageCubeEffect();
609
610   mScrollView.ApplyEffect( mScrollViewEffect );
611 }
612
613 void DaliTableView::SetupInnerPageCubeEffect()
614 {
615   const Vector2 stageSize = Stage::GetCurrent().GetSize();
616
617   Dali::Path path = Dali::Path::New();
618   Dali::Property::Array points;
619   points.Resize(3);
620   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
621   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
622   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
623   path.SetProperty( Path::Property::POINTS, points );
624
625   Dali::Property::Array controlPoints;
626   controlPoints.Resize(4);
627   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
628   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
629   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
630   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
631   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
632
633
634   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
635                                                     Vector3(-1.0f,0.0f,0.0f),
636                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
637                                                     Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
638 }
639
640 void DaliTableView::OnKeyEvent( const KeyEvent& event )
641 {
642   if( event.state == KeyEvent::Down )
643   {
644     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
645     {
646       // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
647       // Otherwise quit.
648       if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
649       {
650         mVersionPopup.SetDisplayState( Popup::HIDDEN );
651       }
652       else
653       {
654         mApplication.Quit();
655       }
656     }
657   }
658 }
659
660 void DaliTableView::SetupBackground( Actor bubbleContainer )
661 {
662   // Create distance field shape.
663   BufferImage distanceField;
664   Size imageSize( 512, 512 );
665   CreateShapeImage( CIRCLE, imageSize, distanceField );
666
667   // Add bubbles to the bubbleContainer.
668   // Note: The bubbleContainer is parented externally to this function.
669   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES, distanceField );
670 }
671
672 void DaliTableView::InitialiseBackgroundActors( Actor actor )
673 {
674   // Delete current animations
675   mBackgroundAnimations.clear();
676
677   // Create new animations
678   const Vector3 size = actor.GetTargetSize();
679
680   for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
681   {
682     Actor child = actor.GetChildAt( i );
683
684     // Calculate a random position
685     Vector3 childPos( Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.5f * BACKGROUND_SPREAD_SCALE ),
686                       Random::Range( -size.y, size.y ),
687                       Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
688
689     child.SetPosition( childPos );
690
691     // Define bubble horizontal parallax and vertical wrapping
692     Constraint animConstraint = Constraint::New < Vector3 > ( child, Actor::Property::POSITION, AnimateBubbleConstraint( childPos, Random::Range( -0.85f, 0.25f ) ) );
693     animConstraint.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
694     animConstraint.AddSource( Dali::ParentSource( Dali::Actor::Property::SIZE ) );
695     animConstraint.AddSource( Dali::LocalSource( Dali::Actor::Property::SIZE ) );
696     animConstraint.SetRemoveAction( Constraint::Discard );
697     animConstraint.Apply();
698
699     // Kickoff animation
700     Animation animation = Animation::New( Random::Range( 30.0f, 160.0f ) );
701     animation.AnimateBy( Property( child, Actor::Property::POSITION ), Vector3( 0.0f, -2000.0f, 0.0f ), AlphaFunction::LINEAR );
702     animation.SetLooping( true );
703     animation.Play();
704     mBackgroundAnimations.push_back( animation );
705   }
706 }
707
708 void DaliTableView::AddBackgroundActors( Actor layer, int count, BufferImage distanceField )
709 {
710   for( int i = 0; i < count; ++i )
711   {
712     float randSize = Random::Range( 10.0f, 400.0f );
713     ImageView dfActor = ImageView::New( distanceField );
714     dfActor.SetSize( Vector2( randSize, randSize ) );
715     dfActor.SetParentOrigin( ParentOrigin::CENTER );
716
717     Dali::Property::Map effect = Toolkit::CreateDistanceFieldEffect();
718     dfActor.SetProperty( Toolkit::ImageView::Property::IMAGE, effect );
719     dfActor.SetColor( BUBBLE_COLOR[ i%NUMBER_OF_BUBBLE_COLOR ] );
720     layer.Add( dfActor );
721   }
722
723   // Positioning will occur when the layer is relaid out
724   layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
725 }
726
727 void DaliTableView::CreateShapeImage( ShapeType shapeType, const Size& size, BufferImage& distanceFieldOut )
728 {
729   // this bitmap will hold the alpha map for the distance field shader
730   distanceFieldOut = BufferImage::New( size.width, size.height, Pixel::A8 );
731
732   // Generate bit pattern
733   std::vector< unsigned char > imageDataA8;
734   imageDataA8.reserve( size.width * size.height ); // A8
735
736   switch( shapeType )
737   {
738     case CIRCLE:
739       GenerateCircle( size, imageDataA8 );
740       break;
741     case SQUARE:
742       GenerateSquare( size, imageDataA8 );
743       break;
744     default:
745       break;
746   }
747
748   PixelBuffer* buffer = distanceFieldOut.GetBuffer();
749   if( buffer )
750   {
751     GenerateDistanceFieldMap( &imageDataA8[ 0 ], size, buffer, size, 8.0f, size );
752     distanceFieldOut.Update();
753   }
754 }
755
756 void DaliTableView::GenerateSquare( const Size& size, std::vector< unsigned char >& distanceFieldOut )
757 {
758   for( int h = 0; h < size.height; ++h )
759   {
760     for( int w = 0; w < size.width; ++w )
761     {
762       distanceFieldOut.push_back( 0xFF );
763     }
764   }
765 }
766
767 void DaliTableView::GenerateCircle( const Size& size, std::vector< unsigned char >& distanceFieldOut )
768 {
769   const float radius = size.width * 0.5f * size.width * 0.5f;
770   Vector2 center( size.width / 2, size.height / 2 );
771
772   for( int h = 0; h < size.height; ++h )
773   {
774     for( int w = 0; w < size.width; ++w )
775     {
776       Vector2 pos( w, h );
777       Vector2 dist = pos - center;
778
779       if( dist.x * dist.x + dist.y * dist.y > radius )
780       {
781         distanceFieldOut.push_back( 0x00 );
782       }
783       else
784       {
785         distanceFieldOut.push_back( 0xFF );
786       }
787     }
788   }
789 }
790
791 ImageView DaliTableView::CreateLogo( std::string imagePath )
792 {
793   Image image = ResourceImage::New( imagePath );
794   ImageView logo = ImageView::New( image );
795
796   logo.SetAnchorPoint( AnchorPoint::CENTER );
797   logo.SetParentOrigin( ParentOrigin::CENTER );
798
799   return logo;
800 }
801
802 bool DaliTableView::PauseBackgroundAnimation()
803 {
804   PauseAnimation();
805
806   return false;
807 }
808
809 void DaliTableView::PauseAnimation()
810 {
811   if( mBackgroundAnimsPlaying )
812   {
813     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
814     {
815       Animation anim = *animIter;
816
817       anim.Stop();
818     }
819
820     mBackgroundAnimsPlaying = false;
821   }
822 }
823
824 void DaliTableView::PlayAnimation()
825 {
826   if ( !mBackgroundAnimsPlaying )
827   {
828     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
829     {
830       Animation anim = *animIter;
831
832       anim.Play();
833     }
834
835     mBackgroundAnimsPlaying = true;
836   }
837
838   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
839 }
840
841 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
842 {
843   Actor nextFocusActor = proposed;
844
845   if ( !current && !proposed  )
846   {
847     // Set the initial focus to the first tile in the current page should be focused.
848     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
849   }
850   else if( !proposed || (proposed && proposed == mScrollViewLayer) )
851   {
852     // ScrollView is being focused but nothing in the current page can be focused further
853     // in the given direction. We should work out which page to scroll to next.
854     int currentPage = mScrollView.GetCurrentPage();
855     int newPage = currentPage;
856     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
857     {
858       newPage--;
859     }
860     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
861     {
862       newPage++;
863     }
864
865     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
866     if( newPage == currentPage )
867     {
868       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
869       {
870         newPage = mTotalPages - 1;
871       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
872       {
873         newPage = 0;
874       }
875     }
876
877     // Scroll to the page in the given direction
878     mScrollView.ScrollTo(newPage);
879
880     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
881     {
882       // Work out the cell position for the last tile
883       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
884       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
885       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
886
887       // Move the focus to the last tile in the new page.
888       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
889     }
890     else
891     {
892       // Move the focus to the first tile in the new page.
893       nextFocusActor = mPages[newPage].GetChildAt(0);
894     }
895   }
896
897   return nextFocusActor;
898 }
899
900 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
901 {
902   if(activatedActor)
903   {
904     mPressedActor = activatedActor;
905
906     // Activate the current focused actor;
907     DoTilePress( mPressedActor, PointState::UP );
908   }
909 }
910
911 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
912 {
913   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
914   return true;
915 }
916
917 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
918 {
919   // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
920   if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
921   {
922     if ( !mVersionPopup )
923     {
924       std::ostringstream stream;
925       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
926       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
927       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
928
929       mVersionPopup = Dali::Toolkit::Popup::New();
930
931       Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
932       titleActor.SetName( "titleActor" );
933       titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
934
935       Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
936       contentActor.SetName( "contentActor" );
937       contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
938       contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
939       contentActor.SetPadding( Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
940
941       mVersionPopup.SetTitle( titleActor );
942       mVersionPopup.SetContent( contentActor );
943
944       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
945       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
946       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
947
948       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
949       Stage::GetCurrent().Add( mVersionPopup );
950     }
951
952     mVersionPopup.SetDisplayState( Popup::SHOWN );
953   }
954 }
955
956 void DaliTableView::HideVersionPopup()
957 {
958   // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
959   if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
960   {
961     mVersionPopup.SetDisplayState( Popup::HIDDEN );
962   }
963 }