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