Merge "Cmake - Remove the CONFIGURE_FILE() step for json style files." into devel...
[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
291   if( stageSize.width <= stageSize.height )
292   {
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   else
299   {
300     winHandle.AddAvailableOrientation( Dali::Window::LANDSCAPE );
301     winHandle.RemoveAvailableOrientation( Dali::Window::PORTRAIT );
302     winHandle.AddAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
303     winHandle.RemoveAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
304   }
305
306   // Set initial orientation
307   unsigned int degrees = 0;
308   Rotate( degrees );
309
310   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
311
312   // Background animation
313   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
314   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
315   mAnimationTimer.Start();
316   mBackgroundAnimsPlaying = true;
317
318   CreateFocusEffect();
319 }
320
321 void DaliTableView::CreateFocusEffect()
322 {
323   // Hook the required signals to manage the focus.
324   auto keyboardFocusManager = KeyboardFocusManager::Get();
325   keyboardFocusManager.PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
326   keyboardFocusManager.FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
327   AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
328
329   // Loop to create both actors for the focus highlight effect.
330   for( unsigned int i = 0; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i )
331   {
332     mFocusEffect[i].actor = ImageView::New();
333     mFocusEffect[i].actor.SetStyleName( "FocusActor" );
334     mFocusEffect[i].actor.SetParentOrigin( ParentOrigin::CENTER );
335     mFocusEffect[i].actor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
336     mFocusEffect[i].actor.SetInheritScale( false );
337     mFocusEffect[i].actor.SetColorMode( USE_OWN_COLOR );
338
339     KeyFrames alphaKeyFrames = KeyFrames::New();
340     alphaKeyFrames.Add( 0.0f, KEYBOARD_FOCUS_FINAL_ALPHA );
341     alphaKeyFrames.Add( KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, KEYBOARD_FOCUS_ANIMATING_ALPHA );
342     alphaKeyFrames.Add( KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_ALPHA );
343
344     KeyFrames scaleKeyFrames = KeyFrames::New();
345     scaleKeyFrames.Add( 0.0f, KEYBOARD_FOCUS_FINAL_SCALE );
346     scaleKeyFrames.Add( KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, FOCUS_INDICATOR_ANIMATING_SCALE );
347     scaleKeyFrames.Add( KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_SCALE );
348
349     mFocusEffect[i].animation = Animation::New( KEYBOARD_FOCUS_ANIMATION_DURATION );
350     mFocusEffect[i].animation.AnimateBetween( Property( mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA ), alphaKeyFrames );
351     mFocusEffect[i].animation.AnimateBetween( Property( mFocusEffect[i].actor, Actor::Property::SCALE ), scaleKeyFrames );
352
353     mFocusEffect[i].animation.SetLoopCount( KEYBOARD_FOCUS_ANIMATION_LOOP_COUNT );
354   }
355
356   // Parent the secondary effect from the primary.
357   mFocusEffect[0].actor.Add( mFocusEffect[1].actor );
358
359   keyboardFocusManager.SetFocusIndicatorActor( mFocusEffect[0].actor );
360
361   // Connect to the on & off stage signals of the indicator which represents when it is enabled & disabled respectively
362   mFocusEffect[0].actor.OnStageSignal().Connect( this, &DaliTableView::OnFocusIndicatorEnabled );
363   mFocusEffect[0].actor.OffStageSignal().Connect( this, &DaliTableView::OnFocusIndicatorDisabled );
364 }
365
366 void DaliTableView::OnFocusIndicatorEnabled( Actor /* actor */ )
367 {
368   // Play the animation on the 1st glow object.
369   mFocusEffect[0].animation.Play();
370
371   // Stagger the animation on the 2nd glow object half way through.
372   mFocusEffect[1].animation.PlayFrom( KEYBOARD_FOCUS_ANIMATION_DURATION / 2.0f );
373 }
374
375 void DaliTableView::OnFocusIndicatorDisabled( Dali::Actor /* actor */ )
376 {
377   // Stop the focus effect animations
378   for( auto i = 0u; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i )
379   {
380     mFocusEffect[ i ].animation.Stop();
381   }
382 }
383
384 void DaliTableView::ApplyCubeEffectToPages()
385 {
386   ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
387   unsigned int pageCount(0);
388   for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
389   {
390     Actor page = *pageIter;
391     effect.ApplyToPage( page, pageCount++ );
392   }
393 }
394
395 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
396 {
397 }
398
399 void DaliTableView::Populate()
400 {
401   const Vector2 stageSize = Stage::GetCurrent().GetSize();
402
403   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
404
405   // Populate ScrollView.
406   if( mExampleList.size() > 0 )
407   {
408     if( mSortAlphabetically )
409     {
410       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
411     }
412
413     unsigned int exampleCount = 0;
414     ExampleListConstIter iter = mExampleList.begin();
415
416     for( int t = 0; t < mTotalPages; t++ )
417     {
418       // Create Table
419       TableView page = TableView::New( ROWS_PER_PAGE, EXAMPLES_PER_ROW );
420       page.SetAnchorPoint( AnchorPoint::CENTER );
421       page.SetParentOrigin( ParentOrigin::CENTER );
422       page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
423       mScrollView.Add( page );
424
425       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
426       const float margin = 2.0f;
427       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
428
429       for(int row = 0; row < ROWS_PER_PAGE; row++)
430       {
431         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
432         {
433           const Example& example = ( *iter );
434
435           // Calculate the tiles relative position on the page (between 0 & 1 in each dimension).
436           Vector2 position( static_cast<float>( column ) / ( EXAMPLES_PER_ROW - 1.0f ), static_cast<float>( row ) / ( EXAMPLES_PER_ROW - 1.0f ) );
437           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), position );
438           AccessibilityManager accessibilityManager = AccessibilityManager::Get();
439           accessibilityManager.SetFocusOrder( tile, ++exampleCount );
440           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
441                                                   example.title );
442           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
443           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT,
444                                                   "You can run this example" );
445
446           tile.SetPadding( Padding( margin, margin, margin, margin ) );
447           page.AddChild( tile, TableView::CellPosition( row, column ) );
448
449           iter++;
450
451           if( iter == mExampleList.end() )
452           {
453             break;
454           }
455         }
456
457         if( iter == mExampleList.end() )
458         {
459           break;
460         }
461       }
462
463       mPages.push_back( page );
464
465       if( iter == mExampleList.end() )
466       {
467         break;
468       }
469     }
470   }
471
472   // Update Ruler info.
473   mScrollRulerX = new FixedRuler( mPageWidth );
474   mScrollRulerY = new DefaultRuler();
475   mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
476   mScrollRulerY->Disable();
477   mScrollView.SetRulerX( mScrollRulerX );
478   mScrollView.SetRulerY( mScrollRulerY );
479 }
480
481 void DaliTableView::Rotate( unsigned int degrees )
482 {
483   // Resize the root actor
484   Vector2 stageSize = Stage::GetCurrent().GetSize();
485   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
486
487   if( degrees == 90 || degrees == 270 )
488   {
489     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
490   }
491
492   if( mRotateAnimation )
493   {
494     mRotateAnimation.Stop();
495     mRotateAnimation.Clear();
496   }
497
498   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
499   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
500   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
501   mRotateAnimation.Play();
502 }
503
504 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position )
505 {
506   Toolkit::ImageView focusableTile = ImageView::New();
507
508   focusableTile.SetStyleName( "DemoTile" );
509   focusableTile.SetParentOrigin( ParentOrigin::CENTER );
510   focusableTile.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
511   focusableTile.SetSizeModeFactor( sizeMultiplier );
512   focusableTile.SetName( name );
513
514   // Set the tile to be keyboard focusable
515   focusableTile.SetKeyboardFocusable( true );
516
517   // Register a property with the ImageView. This allows us to inject the scroll-view position into the shader.
518   Property::Value value = Vector3( 0.0f, 0.0f, 0.0f );
519   Property::Index propertyIndex = focusableTile.RegisterProperty( "uCustomPosition", value );
520
521   // We create a constraint to perform a precalculation on the scroll-view X offset
522   // and pass it to the shader uniform, along with the tile's position.
523   Constraint shaderPosition = Constraint::New < Vector3 > ( focusableTile, propertyIndex, TileShaderPositionConstraint( mPageWidth, position.x ) );
524   shaderPosition.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
525   shaderPosition.SetRemoveAction( Constraint::Discard );
526   shaderPosition.Apply();
527   //focusableTile.Add( tileContent );
528
529   // Create an ImageView for the 9-patch border around the tile.
530   ImageView borderImage = ImageView::New();
531   borderImage.SetStyleName("DemoTileBorder");
532   borderImage.SetAnchorPoint( AnchorPoint::CENTER );
533   borderImage.SetParentOrigin( ParentOrigin::CENTER );
534   borderImage.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
535   borderImage.SetOpacity( 0.8f );
536   focusableTile.Add( borderImage );
537
538   TextLabel label = TextLabel::New();
539   label.SetAnchorPoint( AnchorPoint::CENTER );
540   label.SetParentOrigin( ParentOrigin::CENTER );
541   label.SetStyleName( "LauncherLabel" );
542   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
543   label.SetProperty( TextLabel::Property::TEXT, title );
544   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
545   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
546   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
547
548   // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
549   label.SetPadding( Padding( TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING ) );
550   focusableTile.Add( label );
551
552   // Connect to the touch events
553   focusableTile.TouchSignal().Connect( this, &DaliTableView::OnTilePressed );
554   focusableTile.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
555
556   return focusableTile;
557 }
558
559 bool DaliTableView::OnTilePressed( Actor actor, const TouchData& event )
560 {
561   return DoTilePress( actor, event.GetState( 0 ) );
562 }
563
564 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
565 {
566   bool consumed = false;
567
568   if( PointState::DOWN == pointState )
569   {
570     mPressedActor = actor;
571     consumed = true;
572   }
573
574   // A button press is only valid if the Down & Up events
575   // both occurred within the button.
576   if( ( PointState::UP == pointState ) &&
577       ( mPressedActor == actor ) )
578   {
579     // ignore Example button presses when scrolling or button animating.
580     if( ( !mScrolling ) && ( !mPressedAnimation ) )
581     {
582       std::string name = actor.GetName();
583       const ExampleListIter end = mExampleList.end();
584       for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
585       {
586         if( (*iter).name == name )
587         {
588           // do nothing, until pressed animation finished.
589           consumed = true;
590           break;
591         }
592       }
593     }
594
595     if( consumed )
596     {
597       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
598       mPressedAnimation.SetEndAction( Animation::Discard );
599
600       // scale the content actor within the Tile, as to not affect the placement within the Table.
601       Actor content = actor.GetChildAt(0);
602       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.7f, 0.7f, 1.0f ), AlphaFunction::EASE_IN_OUT,
603                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
604       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
605                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
606
607       // Rotate button on the Y axis when pressed.
608       mPressedAnimation.AnimateBy( Property( content, Actor::Property::ORIENTATION ), Quaternion( Degree( 0.0f ), Degree( 180.0f ), Degree( 0.0f ) ) );
609
610       mPressedAnimation.Play();
611       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
612     }
613   }
614   return consumed;
615 }
616
617 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
618 {
619   mPressedAnimation.Reset();
620   if( mPressedActor )
621   {
622     std::string name = mPressedActor.GetName();
623
624     std::stringstream stream;
625     stream << DEMO_EXAMPLE_BIN << name.c_str();
626     pid_t pid = fork();
627     if( pid == 0)
628     {
629       execlp( stream.str().c_str(), name.c_str(), NULL );
630       DALI_ASSERT_ALWAYS(false && "exec failed!");
631     }
632     mPressedActor.Reset();
633   }
634 }
635
636 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
637 {
638   mScrolling = true;
639
640   PlayAnimation();
641 }
642
643 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
644 {
645   mScrolling = false;
646
647   // move focus to 1st item of new page
648   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
649   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
650 }
651
652 bool DaliTableView::OnScrollTouched( Actor actor, const TouchData& event )
653 {
654   if( PointState::DOWN == event.GetState( 0 ) )
655   {
656     mPressedActor = actor;
657   }
658
659   return false;
660 }
661
662 void DaliTableView::ApplyScrollViewEffect()
663 {
664   // Remove old effect if exists.
665
666   if( mScrollViewEffect )
667   {
668     mScrollView.RemoveEffect( mScrollViewEffect );
669   }
670
671   // Just one effect for now
672   SetupInnerPageCubeEffect();
673
674   mScrollView.ApplyEffect( mScrollViewEffect );
675 }
676
677 void DaliTableView::SetupInnerPageCubeEffect()
678 {
679   const Vector2 stageSize = Stage::GetCurrent().GetSize();
680
681   Dali::Path path = Dali::Path::New();
682   Dali::Property::Array points;
683   points.Resize(3);
684   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
685   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
686   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
687   path.SetProperty( Path::Property::POINTS, points );
688
689   Dali::Property::Array controlPoints;
690   controlPoints.Resize(4);
691   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
692   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
693   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
694   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
695   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
696
697
698   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
699                                                     Vector3(-1.0f,0.0f,0.0f),
700                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
701                                                     Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
702 }
703
704 void DaliTableView::OnKeyEvent( const KeyEvent& event )
705 {
706   if( event.state == KeyEvent::Down )
707   {
708     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
709     {
710       // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
711       // Otherwise quit.
712       if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
713       {
714         mVersionPopup.SetDisplayState( Popup::HIDDEN );
715       }
716       else
717       {
718         mApplication.Quit();
719       }
720     }
721   }
722 }
723
724 void DaliTableView::SetupBackground( Actor bubbleContainer )
725 {
726   // Add bubbles to the bubbleContainer.
727   // Note: The bubbleContainer is parented externally to this function.
728   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES );
729 }
730
731 void DaliTableView::InitialiseBackgroundActors( Actor actor )
732 {
733   // Delete current animations
734   mBackgroundAnimations.clear();
735
736   // Create new animations
737   const Vector3 size = actor.GetTargetSize();
738
739   for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
740   {
741     Actor child = actor.GetChildAt( i );
742
743     // Calculate a random position
744     Vector3 childPos( Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.85f * BACKGROUND_SPREAD_SCALE ),
745                       Random::Range( -size.y, size.y ),
746                       Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
747
748     child.SetPosition( childPos );
749
750     // Define bubble horizontal parallax and vertical wrapping
751     Constraint animConstraint = Constraint::New < Vector3 > ( child, Actor::Property::POSITION, AnimateBubbleConstraint( childPos, Random::Range( -0.85f, 0.25f ) ) );
752     animConstraint.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
753     animConstraint.AddSource( Dali::ParentSource( Dali::Actor::Property::SIZE ) );
754     animConstraint.AddSource( Dali::LocalSource( Dali::Actor::Property::SIZE ) );
755     animConstraint.SetRemoveAction( Constraint::Discard );
756     animConstraint.Apply();
757
758     // Kickoff animation
759     Animation animation = Animation::New( Random::Range( 30.0f, 160.0f ) );
760     animation.AnimateBy( Property( child, Actor::Property::POSITION ), Vector3( 0.0f, -2000.0f, 0.0f ), AlphaFunction::LINEAR );
761     animation.SetLooping( true );
762     animation.Play();
763     mBackgroundAnimations.push_back( animation );
764   }
765 }
766
767 void DaliTableView::AddBackgroundActors( Actor layer, int count )
768 {
769   for( int i = 0; i < count; ++i )
770   {
771     float randSize = Random::Range( 10.0f, 400.0f );
772     int shapeType = static_cast<int>( Random::Range( 0.0f, NUMBER_OF_SHAPE_IMAGES - 1 ) + 0.5f );
773
774     ImageView dfActor = ImageView::New();
775     dfActor.SetSize( Vector2( randSize, randSize ) );
776     dfActor.SetParentOrigin( ParentOrigin::CENTER );
777
778     // Set the Image URL and the custom shader at the same time
779     Dali::Property::Map effect = Toolkit::CreateDistanceFieldEffect();
780     Property::Map imageMap;
781     imageMap.Add( ImageVisual::Property::URL, SHAPE_IMAGE_TABLE[ shapeType ] );
782     imageMap.Add( Toolkit::Visual::Property::SHADER, effect );
783     dfActor.SetProperty( Toolkit::ImageView::Property::IMAGE, imageMap );
784
785     dfActor.SetStyleName( BUBBLE_COLOR_STYLE_NAME[ i%NUMBER_OF_BUBBLE_COLORS ] );
786
787     layer.Add( dfActor );
788   }
789
790   // Positioning will occur when the layer is relaid out
791   layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
792 }
793
794 bool DaliTableView::PauseBackgroundAnimation()
795 {
796   PauseAnimation();
797
798   return false;
799 }
800
801 void DaliTableView::PauseAnimation()
802 {
803   if( mBackgroundAnimsPlaying )
804   {
805     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
806     {
807       Animation anim = *animIter;
808
809       anim.Stop();
810     }
811
812     mBackgroundAnimsPlaying = false;
813   }
814 }
815
816 void DaliTableView::PlayAnimation()
817 {
818   if ( !mBackgroundAnimsPlaying )
819   {
820     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
821     {
822       Animation anim = *animIter;
823
824       anim.Play();
825     }
826
827     mBackgroundAnimsPlaying = true;
828   }
829
830   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
831 }
832
833 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
834 {
835   Actor nextFocusActor = proposed;
836
837   if( !current && !proposed  )
838   {
839     // Set the initial focus to the first tile in the current page should be focused.
840     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
841   }
842   else if( !proposed )
843   {
844     // ScrollView is being focused but nothing in the current page can be focused further
845     // in the given direction. We should work out which page to scroll to next.
846     int currentPage = mScrollView.GetCurrentPage();
847     int newPage = currentPage;
848     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
849     {
850       newPage--;
851     }
852     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
853     {
854       newPage++;
855     }
856
857     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
858     if( newPage == currentPage )
859     {
860       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
861       {
862         newPage = mTotalPages - 1;
863       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
864       {
865         newPage = 0;
866       }
867     }
868
869     // Scroll to the page in the given direction
870     mScrollView.ScrollTo(newPage);
871
872     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
873     {
874       // Work out the cell position for the last tile
875       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
876       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
877       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
878
879       // Move the focus to the last tile in the new page.
880       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
881     }
882     else
883     {
884       // Move the focus to the first tile in the new page.
885       nextFocusActor = mPages[newPage].GetChildAt(0);
886     }
887   }
888
889   return nextFocusActor;
890 }
891
892 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
893 {
894   if(activatedActor)
895   {
896     mPressedActor = activatedActor;
897
898     // Activate the current focused actor;
899     DoTilePress( mPressedActor, PointState::UP );
900   }
901 }
902
903 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
904 {
905   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
906   return true;
907 }
908
909 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
910 {
911   // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
912   if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
913   {
914     if ( !mVersionPopup )
915     {
916       std::ostringstream stream;
917       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
918       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
919       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
920       stream << "DALi Demo:" << "\n(" << DEMO_BUILD_DATE << ")\n";
921
922       mVersionPopup = Dali::Toolkit::Popup::New();
923
924       Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
925       titleActor.SetName( "titleActor" );
926       titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER );
927       titleActor.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE );
928
929       Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
930       contentActor.SetName( "contentActor" );
931       contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
932       contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER );
933       contentActor.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE );
934       contentActor.SetPadding( Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
935
936       mVersionPopup.SetTitle( titleActor );
937       mVersionPopup.SetContent( contentActor );
938
939       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
940       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
941       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
942
943       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
944       Stage::GetCurrent().Add( mVersionPopup );
945     }
946
947     mVersionPopup.SetDisplayState( Popup::SHOWN );
948   }
949 }
950
951 void DaliTableView::HideVersionPopup()
952 {
953   // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
954   if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
955   {
956     mVersionPopup.SetDisplayState( Popup::HIDDEN );
957   }
958 }