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