Changes after TouchedSignal changes
[platform/core/uifw/dali-demo.git] / shared / dali-table-view.cpp
1 /*
2  * Copyright (c) 2020 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 <dali/devel-api/actors/actor-devel.h>
24 #include <dali/devel-api/images/distance-field.h>
25 #include <dali-toolkit/devel-api/shader-effects/alpha-discard-effect.h>
26 #include <dali-toolkit/devel-api/shader-effects/distance-field-effect.h>
27 #include <dali-toolkit/dali-toolkit.h>
28 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
29 #include <dali-toolkit/devel-api/accessibility-manager/accessibility-manager.h>
30 #include <dali-toolkit/devel-api/controls/table-view/table-view.h>
31
32 // INTERNAL INCLUDES
33 #include "shared/execute-process.h"
34 #include "shared/utility.h"
35 #include "shared/view.h"
36
37 using namespace Dali;
38 using namespace Dali::Toolkit;
39
40 ///////////////////////////////////////////////////////////////////////////////
41
42 namespace
43 {
44
45 const std::string LOGO_PATH( DEMO_IMAGE_DIR "Logo-for-demo.png" );
46
47 // Keyboard focus effect constants.
48 const float KEYBOARD_FOCUS_ANIMATION_DURATION = 1.0f;           ///< The total duration of the keyboard focus animation
49 const int32_t KEYBOARD_FOCUS_ANIMATION_LOOP_COUNT = 5;          ///< The number of loops for the keyboard focus animation
50 const float KEYBOARD_FOCUS_FINAL_SCALE_FLOAT = 1.05f;           ///< The final scale of the focus highlight
51 const float KEYBOARD_FOCUS_ANIMATED_SCALE_FLOAT = 1.18f;        ///< The scale of the focus highlight during the animation
52 const float KEYBOARD_FOCUS_FINAL_ALPHA = 0.7f;                  ///< The final alpha of the focus highlight
53 const float KEYBOARD_FOCUS_ANIMATING_ALPHA = 0.0f;              ///< The alpha of the focus highlight during the animation
54 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
55 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
56 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
57 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
58
59 const float TILE_LABEL_PADDING = 8.0f;                          ///< Border between edge of tile and the example text
60 const float BUTTON_PRESS_ANIMATION_TIME = 0.35f;                ///< Time to perform button scale effect.
61 const float ROTATE_ANIMATION_TIME = 0.5f;                       ///< Time to perform rotate effect.
62 const int MAX_PAGES = 256;                                      ///< Maximum pages (arbitrary safety limit)
63 const int EXAMPLES_PER_ROW = 3;
64 const int ROWS_PER_PAGE = 3;
65 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
66 const float LOGO_MARGIN_RATIO = 0.1f / 0.3f;
67 const float BOTTOM_PADDING_RATIO = 0.4f / 0.9f;
68 const Vector3 SCROLLVIEW_RELATIVE_SIZE(0.9f, 1.0f, 0.8f );      ///< ScrollView's relative size to its parent
69 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.
70 const float STENCIL_RELATIVE_SIZE = 1.0f;
71
72 const float EFFECT_SNAP_DURATION = 0.66f;                       ///< Scroll Snap Duration for Effects
73 const float EFFECT_FLICK_DURATION = 0.5f;                       ///< Scroll Flick Duration for Effects
74 const Vector3 ANGLE_CUBE_PAGE_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
75
76 const char * const BUBBLE_COLOR_STYLE_NAME[] =
77 {
78   "BubbleColor1",
79   "BubbleColor2",
80   "BubbleColor3",
81   "BubbleColor4"
82 };
83 const int NUMBER_OF_BUBBLE_COLORS( sizeof( BUBBLE_COLOR_STYLE_NAME ) / sizeof( BUBBLE_COLOR_STYLE_NAME[ 0 ] ) );
84
85 const char * const SHAPE_IMAGE_TABLE[] =
86 {
87   DEMO_IMAGE_DIR "shape-circle.png",
88   DEMO_IMAGE_DIR "shape-bubble.png"
89 };
90 const int NUMBER_OF_SHAPE_IMAGES( sizeof( SHAPE_IMAGE_TABLE ) / sizeof( SHAPE_IMAGE_TABLE[0] ) );
91
92 const int NUM_BACKGROUND_IMAGES = 18;
93 const float BACKGROUND_SPREAD_SCALE = 1.5f;
94
95 const unsigned int BACKGROUND_ANIMATION_DURATION = 15000; // 15 secs
96
97 const float BUBBLE_MIN_Z = -1.0;
98 const float BUBBLE_MAX_Z = 0.0f;
99
100 const char * const DEMO_BUILD_DATE    = __DATE__ " " __TIME__;
101
102 /**
103  * Creates the background image
104  */
105 Control CreateBackground( std::string stylename )
106 {
107   Control background = Control::New();
108   background.SetStyleName( stylename );
109   background.SetProperty( Actor::Property::NAME, "BACKGROUND" );
110   background.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
111   background.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
112   background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
113   return background;
114 }
115
116 /**
117  * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
118  */
119 struct AnimateBubbleConstraint
120 {
121 public:
122   AnimateBubbleConstraint( const Vector3& initialPos, float scale )
123   : mInitialX( initialPos.x ),
124     mScale( scale )
125   {
126   }
127
128   void operator()( Vector3& position, const PropertyInputContainer& inputs )
129   {
130     const Vector3& parentSize = inputs[1]->GetVector3();
131     const Vector3& childSize = inputs[2]->GetVector3();
132
133     // Wrap bubbles vertically.
134     float range = parentSize.y + childSize.y;
135     // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
136     position.y -= range * ( floor( position.y / range ) + 0.5f );
137
138     // Bubbles X position moves parallax to horizontal
139     // panning by a scale factor unique to each bubble.
140     position.x = mInitialX + ( inputs[0]->GetVector2().x * mScale );
141   }
142
143 private:
144   float mInitialX;
145   float mScale;
146 };
147
148 /**
149  * Constraint to precalculate values from the scroll-view
150  * and tile positions to pass to the tile shader.
151  */
152 struct TileShaderPositionConstraint
153 {
154   TileShaderPositionConstraint( float pageWidth, float tileXOffset )
155   : mPageWidth( pageWidth ),
156     mTileXOffset( tileXOffset )
157   {
158   }
159
160   void operator()( Vector3& position, const PropertyInputContainer& inputs )
161   {
162     // Set up position.x as the tiles X offset (0.0 -> 1.0).
163     position.x = mTileXOffset;
164     // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
165     position.z = 1.0f * ( -fmod( inputs[0]->GetVector2().x, mPageWidth ) / mPageWidth );
166     // Set up position.y as a rectified version of the scroll-views X offset.
167     // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
168     if( position.z > 0.5f )
169     {
170       position.y = 1.0f - position.z;
171     }
172     else
173     {
174       position.y = position.z;
175     }
176   }
177
178 private:
179   float mPageWidth;
180   float mTileXOffset;
181 };
182
183 bool CompareByTitle( const Example& lhs, const Example& rhs )
184 {
185   return lhs.title < rhs.title;
186 }
187
188 } // namespace
189
190 DaliTableView::DaliTableView( Application& application )
191 : mApplication( application ),
192   mRootActor(),
193   mRotateAnimation(),
194   mPressedAnimation(),
195   mScrollView(),
196   mScrollViewEffect(),
197   mScrollRulerX(),
198   mScrollRulerY(),
199   mPressedActor(),
200   mAnimationTimer(),
201   mLogoTapDetector(),
202   mVersionPopup(),
203   mPages(),
204   mBackgroundAnimations(),
205   mExampleList(),
206   mPageWidth( 0.0f ),
207   mTotalPages(),
208   mScrolling( false ),
209   mSortAlphabetically( false ),
210   mBackgroundAnimsPlaying( false )
211 {
212   application.InitSignal().Connect( this, &DaliTableView::Initialize );
213 }
214
215 DaliTableView::~DaliTableView()
216 {
217 }
218
219 void DaliTableView::AddExample( Example example )
220 {
221   mExampleList.push_back( example );
222 }
223
224 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
225 {
226   mSortAlphabetically = sortAlphabetically;
227 }
228
229 void DaliTableView::Initialize( Application& application )
230 {
231   Window window = application.GetWindow();
232   window.KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
233   const Window::WindowSize windowSize = window.GetSize();
234
235   // Background
236   mRootActor = CreateBackground( "LauncherBackground" );
237   window.Add( mRootActor );
238
239   // Add logo
240   ImageView logo = ImageView::New( LOGO_PATH );
241   logo.SetProperty( Actor::Property::NAME, "LOGO_IMAGE" );
242   logo.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER );
243   logo.SetProperty( Actor::Property::PARENT_ORIGIN, Vector3( 0.5f, 0.1f, 0.5f ) );
244   logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
245   // The logo should appear on top of everything.
246   logo.SetProperty( Actor::Property::DRAW_MODE, DrawMode::OVERLAY_2D );
247   mRootActor.Add( logo );
248
249   // Show version in a popup when log is tapped
250   mLogoTapDetector = TapGestureDetector::New();
251   mLogoTapDetector.Attach( logo );
252   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
253
254   // Scrollview occupying the majority of the screen
255   mScrollView = ScrollView::New();
256   mScrollView.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::BOTTOM_CENTER );
257   mScrollView.SetProperty( Actor::Property::PARENT_ORIGIN, Vector3( 0.5f, 1.0f - 0.05f, 0.5f ) );
258   mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
259   mScrollView.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT );
260   mScrollView.SetProperty( Actor::Property::SIZE_MODE_FACTOR, Vector3( 0.0f, 0.6f, 0.0f ) );
261
262   const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * windowSize.GetWidth();
263   mScrollView.SetProperty( Actor::Property::PADDING, Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
264
265   mScrollView.SetAxisAutoLock( true );
266   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
267   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
268   mScrollView.TouchedSignal().Connect( this, &DaliTableView::OnScrollTouched );
269
270   mPageWidth = windowSize.GetWidth() * TABLE_RELATIVE_SIZE.x * 0.5f;
271
272   // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
273   Actor bubbleContainer = Actor::New();
274   bubbleContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
275   bubbleContainer.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
276   bubbleContainer.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
277   SetupBackground( bubbleContainer );
278
279   mRootActor.Add( bubbleContainer );
280   mRootActor.Add( mScrollView );
281
282   // Add scroll view effect and setup constraints on pages
283   ApplyScrollViewEffect();
284
285   // Add pages and tiles
286   Populate();
287
288   // Remove constraints for inner cube effect
289   ApplyCubeEffectToPages();
290
291   Dali::Window winHandle = application.GetWindow();
292
293   if( windowSize.GetWidth() <= windowSize.GetHeight() )
294   {
295     winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
296     winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
297     winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
298     winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
299   }
300   else
301   {
302     winHandle.AddAvailableOrientation( Dali::Window::LANDSCAPE );
303     winHandle.RemoveAvailableOrientation( Dali::Window::PORTRAIT );
304     winHandle.AddAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
305     winHandle.RemoveAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
306   }
307
308   // Set initial orientation
309   unsigned int degrees = 0;
310   Rotate( degrees );
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.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
335     mFocusEffect[i].actor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
336     mFocusEffect[i].actor.SetProperty( Actor::Property::INHERIT_SCALE, false );
337     mFocusEffect[i].actor.SetProperty( Actor::Property::COLOR_MODE, 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 scene signals of the indicator which represents when it is enabled & disabled respectively
362   mFocusEffect[0].actor.OnSceneSignal().Connect( this, &DaliTableView::OnFocusIndicatorEnabled );
363   mFocusEffect[0].actor.OffSceneSignal().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 Window::WindowSize windowSize = mApplication.GetWindow().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.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
421       page.SetProperty( Actor::Property::PARENT_ORIGIN, 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.SetProperty( Actor::Property::PADDING, 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) * windowSize.GetWidth() * 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   const Window::WindowSize windowSize = mApplication.GetWindow().GetSize();
485   const Vector2 originalSize( windowSize.GetWidth(), windowSize.GetHeight() );
486   Vector3 targetSize( originalSize.x, originalSize.y, 1.0f );
487
488   if( degrees == 90 || degrees == 270 )
489   {
490     targetSize = Vector3( originalSize.y, originalSize.x, 1.0f );
491   }
492
493   if( mRotateAnimation )
494   {
495     mRotateAnimation.Stop();
496     mRotateAnimation.Clear();
497   }
498
499   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
500   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
501   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
502   mRotateAnimation.Play();
503 }
504
505 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position )
506 {
507   Toolkit::ImageView focusableTile = ImageView::New();
508
509   focusableTile.SetStyleName( "DemoTile" );
510   focusableTile.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
511   focusableTile.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
512   focusableTile.SetProperty( Actor::Property::SIZE_MODE_FACTOR, sizeMultiplier );
513   focusableTile.SetProperty( Actor::Property::NAME, name );
514
515   // Set the tile to be keyboard focusable
516   focusableTile.SetProperty( Actor::Property::KEYBOARD_FOCUSABLE, true );
517
518   // Register a property with the ImageView. This allows us to inject the scroll-view position into the shader.
519   Property::Value value = Vector3( 0.0f, 0.0f, 0.0f );
520   Property::Index propertyIndex = focusableTile.RegisterProperty( "uCustomPosition", value );
521
522   // We create a constraint to perform a precalculation on the scroll-view X offset
523   // and pass it to the shader uniform, along with the tile's position.
524   Constraint shaderPosition = Constraint::New < Vector3 > ( focusableTile, propertyIndex, TileShaderPositionConstraint( mPageWidth, position.x ) );
525   shaderPosition.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
526   shaderPosition.SetRemoveAction( Constraint::DISCARD );
527   shaderPosition.Apply();
528   //focusableTile.Add( tileContent );
529
530   // Create an ImageView for the 9-patch border around the tile.
531   ImageView borderImage = ImageView::New();
532   borderImage.SetStyleName("DemoTileBorder");
533   borderImage.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
534   borderImage.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
535   borderImage.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
536   borderImage.SetProperty( Actor::Property::OPACITY, 0.8f );
537   focusableTile.Add( borderImage );
538
539   TextLabel label = TextLabel::New();
540   label.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
541   label.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
542   label.SetStyleName( "LauncherLabel" );
543   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
544   label.SetProperty( TextLabel::Property::TEXT, title );
545   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
546   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
547   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
548
549   // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
550   label.SetProperty( Actor::Property::PADDING, Padding( TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING ) );
551   focusableTile.Add( label );
552
553   // Connect to the touch events
554   focusableTile.TouchedSignal().Connect( this, &DaliTableView::OnTilePressed );
555   focusableTile.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
556
557   return focusableTile;
558 }
559
560 bool DaliTableView::OnTilePressed( Actor actor, const TouchEvent& event )
561 {
562   return DoTilePress( actor, event.GetState( 0 ) );
563 }
564
565 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
566 {
567   bool consumed = false;
568
569   if( PointState::DOWN == pointState )
570   {
571     mPressedActor = actor;
572     consumed = true;
573   }
574
575   // A button press is only valid if the Down & Up events
576   // both occurred within the button.
577   if( ( PointState::UP == pointState ) &&
578       ( mPressedActor == actor ) )
579   {
580     // ignore Example button presses when scrolling or button animating.
581     if( ( !mScrolling ) && ( !mPressedAnimation ) )
582     {
583       std::string name = actor.GetProperty< std::string >( Dali::Actor::Property::NAME );
584       const ExampleListIter end = mExampleList.end();
585       for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
586       {
587         if( (*iter).name == name )
588         {
589           // do nothing, until pressed animation finished.
590           consumed = true;
591           break;
592         }
593       }
594     }
595
596     if( consumed )
597     {
598       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
599       mPressedAnimation.SetEndAction( Animation::DISCARD );
600
601       // scale the content actor within the Tile, as to not affect the placement within the Table.
602       Actor content = actor.GetChildAt(0);
603       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.7f, 0.7f, 1.0f ), AlphaFunction::EASE_IN_OUT,
604                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
605       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
606                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
607
608       // Rotate button on the Y axis when pressed.
609       mPressedAnimation.AnimateBy( Property( content, Actor::Property::ORIENTATION ), Quaternion( Degree( 0.0f ), Degree( 180.0f ), Degree( 0.0f ) ) );
610
611       mPressedAnimation.Play();
612       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
613     }
614   }
615   return consumed;
616 }
617
618 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
619 {
620   mPressedAnimation.Reset();
621   if( mPressedActor )
622   {
623     std::string name = mPressedActor.GetProperty< std::string >( Dali::Actor::Property::NAME );
624
625     ExecuteProcess( name, mApplication );
626
627     mPressedActor.Reset();
628   }
629 }
630
631 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
632 {
633   mScrolling = true;
634
635   PlayAnimation();
636 }
637
638 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
639 {
640   mScrolling = false;
641
642   // move focus to 1st item of new page
643   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
644   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
645 }
646
647 bool DaliTableView::OnScrollTouched( Actor actor, const TouchEvent& event )
648 {
649   if( PointState::DOWN == event.GetState( 0 ) )
650   {
651     mPressedActor = actor;
652   }
653
654   return false;
655 }
656
657 void DaliTableView::ApplyScrollViewEffect()
658 {
659   // Remove old effect if exists.
660
661   if( mScrollViewEffect )
662   {
663     mScrollView.RemoveEffect( mScrollViewEffect );
664   }
665
666   // Just one effect for now
667   SetupInnerPageCubeEffect();
668
669   mScrollView.ApplyEffect( mScrollViewEffect );
670 }
671
672 void DaliTableView::SetupInnerPageCubeEffect()
673 {
674   const Window::WindowSize windowDimensions = mApplication.GetWindow().GetSize();
675   const Vector2 windowSize(windowDimensions.GetWidth(), windowDimensions.GetHeight());
676
677   Dali::Path path = Dali::Path::New();
678   Dali::Property::Array points;
679   points.Resize(3);
680   points[0] = Vector3( windowSize.x*0.5, 0.0f,  windowSize.x*0.5f);
681   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
682   points[2] = Vector3( -windowSize.x*0.5f, 0.0f, windowSize.x*0.5f);
683   path.SetProperty( Path::Property::POINTS, points );
684
685   Dali::Property::Array controlPoints;
686   controlPoints.Resize(4);
687   controlPoints[0] = Vector3( windowSize.x*0.5f, 0.0f, windowSize.x*0.3f );
688   controlPoints[1] = Vector3( windowSize.x*0.3f, 0.0f, 0.0f );
689   controlPoints[2] = Vector3(-windowSize.x*0.3f, 0.0f, 0.0f );
690   controlPoints[3] = Vector3(-windowSize.x*0.5f, 0.0f,  windowSize.x*0.3f );
691   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
692
693
694   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
695                                                     Vector3(-1.0f,0.0f,0.0f),
696                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
697                                                     Vector3(windowSize.x*TABLE_RELATIVE_SIZE.x,windowSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
698 }
699
700 void DaliTableView::OnKeyEvent( const KeyEvent& event )
701 {
702   if( event.GetState() == KeyEvent::DOWN )
703   {
704     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
705     {
706       // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
707       // Otherwise quit.
708       if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
709       {
710         mVersionPopup.SetDisplayState( Popup::HIDDEN );
711       }
712       else
713       {
714         mApplication.Quit();
715       }
716     }
717   }
718 }
719
720 void DaliTableView::SetupBackground( Actor bubbleContainer )
721 {
722   // Add bubbles to the bubbleContainer.
723   // Note: The bubbleContainer is parented externally to this function.
724   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES );
725 }
726
727 void DaliTableView::InitialiseBackgroundActors( Actor actor )
728 {
729   // Delete current animations
730   mBackgroundAnimations.clear();
731
732   // Create new animations
733   const Vector3 size = actor.GetTargetSize();
734
735   for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
736   {
737     Actor child = actor.GetChildAt( i );
738
739     // Calculate a random position
740     Vector3 childPos( Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.85f * BACKGROUND_SPREAD_SCALE ),
741                       Random::Range( -size.y, size.y ),
742                       Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
743
744     child.SetProperty( Actor::Property::POSITION, childPos );
745
746     // Define bubble horizontal parallax and vertical wrapping
747     Constraint animConstraint = Constraint::New < Vector3 > ( child, Actor::Property::POSITION, AnimateBubbleConstraint( childPos, Random::Range( -0.85f, 0.25f ) ) );
748     animConstraint.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
749     animConstraint.AddSource( Dali::ParentSource( Dali::Actor::Property::SIZE ) );
750     animConstraint.AddSource( Dali::LocalSource( Dali::Actor::Property::SIZE ) );
751     animConstraint.SetRemoveAction( Constraint::DISCARD );
752     animConstraint.Apply();
753
754     // Kickoff animation
755     Animation animation = Animation::New( Random::Range( 30.0f, 160.0f ) );
756     animation.AnimateBy( Property( child, Actor::Property::POSITION ), Vector3( 0.0f, -2000.0f, 0.0f ), AlphaFunction::LINEAR );
757     animation.SetLooping( true );
758     animation.Play();
759     mBackgroundAnimations.push_back( animation );
760   }
761 }
762
763 void DaliTableView::AddBackgroundActors( Actor layer, int count )
764 {
765   for( int i = 0; i < count; ++i )
766   {
767     float randSize = Random::Range( 10.0f, 400.0f );
768     int shapeType = static_cast<int>( Random::Range( 0.0f, NUMBER_OF_SHAPE_IMAGES - 1 ) + 0.5f );
769
770     ImageView dfActor = ImageView::New();
771     dfActor.SetProperty( Actor::Property::SIZE, Vector2( randSize, randSize ) );
772     dfActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
773
774     // Set the Image URL and the custom shader at the same time
775     Dali::Property::Map effect = Toolkit::CreateDistanceFieldEffect();
776     Property::Map imageMap;
777     imageMap.Add( ImageVisual::Property::URL, SHAPE_IMAGE_TABLE[ shapeType ] );
778     imageMap.Add( Toolkit::Visual::Property::SHADER, effect );
779     dfActor.SetProperty( Toolkit::ImageView::Property::IMAGE, imageMap );
780
781     dfActor.SetStyleName( BUBBLE_COLOR_STYLE_NAME[ i%NUMBER_OF_BUBBLE_COLORS ] );
782
783     layer.Add( dfActor );
784   }
785
786   // Positioning will occur when the layer is relaid out
787   layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
788 }
789
790 bool DaliTableView::PauseBackgroundAnimation()
791 {
792   PauseAnimation();
793
794   return false;
795 }
796
797 void DaliTableView::PauseAnimation()
798 {
799   if( mBackgroundAnimsPlaying )
800   {
801     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
802     {
803       Animation anim = *animIter;
804
805       anim.Stop();
806     }
807
808     mBackgroundAnimsPlaying = false;
809   }
810 }
811
812 void DaliTableView::PlayAnimation()
813 {
814   if ( !mBackgroundAnimsPlaying )
815   {
816     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
817     {
818       Animation anim = *animIter;
819
820       anim.Play();
821     }
822
823     mBackgroundAnimsPlaying = true;
824   }
825
826   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
827 }
828
829 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
830 {
831   Actor nextFocusActor = proposed;
832
833   if( !current && !proposed  )
834   {
835     // Set the initial focus to the first tile in the current page should be focused.
836     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
837   }
838   else if( !proposed )
839   {
840     // ScrollView is being focused but nothing in the current page can be focused further
841     // in the given direction. We should work out which page to scroll to next.
842     int currentPage = mScrollView.GetCurrentPage();
843     int newPage = currentPage;
844     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
845     {
846       newPage--;
847     }
848     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
849     {
850       newPage++;
851     }
852
853     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
854     if( newPage == currentPage )
855     {
856       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
857       {
858         newPage = mTotalPages - 1;
859       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
860       {
861         newPage = 0;
862       }
863     }
864
865     // Scroll to the page in the given direction
866     mScrollView.ScrollTo(newPage);
867
868     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
869     {
870       // Work out the cell position for the last tile
871       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
872       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
873       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
874
875       // Move the focus to the last tile in the new page.
876       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
877     }
878     else
879     {
880       // Move the focus to the first tile in the new page.
881       nextFocusActor = mPages[newPage].GetChildAt(0);
882     }
883   }
884
885   return nextFocusActor;
886 }
887
888 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
889 {
890   if(activatedActor)
891   {
892     mPressedActor = activatedActor;
893
894     // Activate the current focused actor;
895     DoTilePress( mPressedActor, PointState::UP );
896   }
897 }
898
899 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
900 {
901   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
902   return true;
903 }
904
905 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
906 {
907   // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
908   if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
909   {
910     if ( !mVersionPopup )
911     {
912       std::ostringstream stream;
913       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
914       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
915       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
916       stream << "DALi Demo:" << "\n(" << DEMO_BUILD_DATE << ")\n";
917
918       mVersionPopup = Dali::Toolkit::Popup::New();
919
920       Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
921       titleActor.SetProperty( Actor::Property::NAME, "titleActor" );
922       titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER );
923       titleActor.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE );
924
925       Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
926       contentActor.SetProperty( Actor::Property::NAME, "contentActor" );
927       contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
928       contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, HorizontalAlignment::CENTER );
929       contentActor.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, Color::WHITE );
930       contentActor.SetProperty( Actor::Property::PADDING, Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
931
932       mVersionPopup.SetTitle( titleActor );
933       mVersionPopup.SetContent( contentActor );
934
935       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
936       mVersionPopup.SetProperty( Actor::Property::SIZE_MODE_FACTOR, Vector3( 0.75f, 1.0f, 1.0f ) );
937       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
938
939       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
940       mApplication.GetWindow().Add( mVersionPopup );
941     }
942
943     mVersionPopup.SetDisplayState( Popup::SHOWN );
944   }
945 }
946
947 void DaliTableView::HideVersionPopup()
948 {
949   // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
950   if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
951   {
952     mVersionPopup.SetDisplayState( Popup::HIDDEN );
953   }
954 }