Move some public apis in toolkit to 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 <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   Stage::GetCurrent().Add( background );
109   background.SetStyleName( stylename );
110   background.SetProperty( Actor::Property::NAME, "BACKGROUND" );
111   background.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
112   background.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
113   background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
114   return background;
115 }
116
117 /**
118  * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
119  */
120 struct AnimateBubbleConstraint
121 {
122 public:
123   AnimateBubbleConstraint( const Vector3& initialPos, float scale )
124   : mInitialX( initialPos.x ),
125     mScale( scale )
126   {
127   }
128
129   void operator()( Vector3& position, const PropertyInputContainer& inputs )
130   {
131     const Vector3& parentSize = inputs[1]->GetVector3();
132     const Vector3& childSize = inputs[2]->GetVector3();
133
134     // Wrap bubbles vertically.
135     float range = parentSize.y + childSize.y;
136     // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
137     position.y -= range * ( floor( position.y / range ) + 0.5f );
138
139     // Bubbles X position moves parallax to horizontal
140     // panning by a scale factor unique to each bubble.
141     position.x = mInitialX + ( inputs[0]->GetVector2().x * mScale );
142   }
143
144 private:
145   float mInitialX;
146   float mScale;
147 };
148
149 /**
150  * Constraint to precalculate values from the scroll-view
151  * and tile positions to pass to the tile shader.
152  */
153 struct TileShaderPositionConstraint
154 {
155   TileShaderPositionConstraint( float pageWidth, float tileXOffset )
156   : mPageWidth( pageWidth ),
157     mTileXOffset( tileXOffset )
158   {
159   }
160
161   void operator()( Vector3& position, const PropertyInputContainer& inputs )
162   {
163     // Set up position.x as the tiles X offset (0.0 -> 1.0).
164     position.x = mTileXOffset;
165     // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
166     position.z = 1.0f * ( -fmod( inputs[0]->GetVector2().x, mPageWidth ) / mPageWidth );
167     // Set up position.y as a rectified version of the scroll-views X offset.
168     // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
169     if( position.z > 0.5f )
170     {
171       position.y = 1.0f - position.z;
172     }
173     else
174     {
175       position.y = position.z;
176     }
177   }
178
179 private:
180   float mPageWidth;
181   float mTileXOffset;
182 };
183
184 bool CompareByTitle( const Example& lhs, const Example& rhs )
185 {
186   return lhs.title < rhs.title;
187 }
188
189 } // namespace
190
191 DaliTableView::DaliTableView( Application& application )
192 : mApplication( application ),
193   mRootActor(),
194   mRotateAnimation(),
195   mPressedAnimation(),
196   mScrollView(),
197   mScrollViewEffect(),
198   mScrollRulerX(),
199   mScrollRulerY(),
200   mPressedActor(),
201   mAnimationTimer(),
202   mLogoTapDetector(),
203   mVersionPopup(),
204   mPages(),
205   mBackgroundAnimations(),
206   mExampleList(),
207   mPageWidth( 0.0f ),
208   mTotalPages(),
209   mScrolling( false ),
210   mSortAlphabetically( false ),
211   mBackgroundAnimsPlaying( false )
212 {
213   application.InitSignal().Connect( this, &DaliTableView::Initialize );
214 }
215
216 DaliTableView::~DaliTableView()
217 {
218 }
219
220 void DaliTableView::AddExample( Example example )
221 {
222   mExampleList.push_back( example );
223 }
224
225 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
226 {
227   mSortAlphabetically = sortAlphabetically;
228 }
229
230 void DaliTableView::Initialize( Application& application )
231 {
232   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
233   const Vector2 stageSize = Stage::GetCurrent().GetSize();
234
235   // Background
236   mRootActor = CreateBackground( "LauncherBackground" );
237   Stage::GetCurrent().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 * stageSize.width;
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.TouchSignal().Connect( this, &DaliTableView::OnScrollTouched );
269
270   mPageWidth = stageSize.width * 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( stageSize.width <= stageSize.height )
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   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
313
314   // Background animation
315   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
316   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
317   mAnimationTimer.Start();
318   mBackgroundAnimsPlaying = true;
319
320   CreateFocusEffect();
321 }
322
323 void DaliTableView::CreateFocusEffect()
324 {
325   // Hook the required signals to manage the focus.
326   auto keyboardFocusManager = KeyboardFocusManager::Get();
327   keyboardFocusManager.PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
328   keyboardFocusManager.FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
329   AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
330
331   // Loop to create both actors for the focus highlight effect.
332   for( unsigned int i = 0; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i )
333   {
334     mFocusEffect[i].actor = ImageView::New();
335     mFocusEffect[i].actor.SetStyleName( "FocusActor" );
336     mFocusEffect[i].actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
337     mFocusEffect[i].actor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
338     mFocusEffect[i].actor.SetProperty( Actor::Property::INHERIT_SCALE, false );
339     mFocusEffect[i].actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_COLOR );
340
341     KeyFrames alphaKeyFrames = KeyFrames::New();
342     alphaKeyFrames.Add( 0.0f, KEYBOARD_FOCUS_FINAL_ALPHA );
343     alphaKeyFrames.Add( KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, KEYBOARD_FOCUS_ANIMATING_ALPHA );
344     alphaKeyFrames.Add( KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_ALPHA );
345
346     KeyFrames scaleKeyFrames = KeyFrames::New();
347     scaleKeyFrames.Add( 0.0f, KEYBOARD_FOCUS_FINAL_SCALE );
348     scaleKeyFrames.Add( KEYBOARD_FOCUS_MID_KEY_FRAME_TIME, FOCUS_INDICATOR_ANIMATING_SCALE );
349     scaleKeyFrames.Add( KEYBOARD_FOCUS_ANIMATION_DURATION, KEYBOARD_FOCUS_FINAL_SCALE );
350
351     mFocusEffect[i].animation = Animation::New( KEYBOARD_FOCUS_ANIMATION_DURATION );
352     mFocusEffect[i].animation.AnimateBetween( Property( mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA ), alphaKeyFrames );
353     mFocusEffect[i].animation.AnimateBetween( Property( mFocusEffect[i].actor, Actor::Property::SCALE ), scaleKeyFrames );
354
355     mFocusEffect[i].animation.SetLoopCount( KEYBOARD_FOCUS_ANIMATION_LOOP_COUNT );
356   }
357
358   // Parent the secondary effect from the primary.
359   mFocusEffect[0].actor.Add( mFocusEffect[1].actor );
360
361   keyboardFocusManager.SetFocusIndicatorActor( mFocusEffect[0].actor );
362
363   // Connect to the on & off stage signals of the indicator which represents when it is enabled & disabled respectively
364   mFocusEffect[0].actor.OnStageSignal().Connect( this, &DaliTableView::OnFocusIndicatorEnabled );
365   mFocusEffect[0].actor.OffStageSignal().Connect( this, &DaliTableView::OnFocusIndicatorDisabled );
366 }
367
368 void DaliTableView::OnFocusIndicatorEnabled( Actor /* actor */ )
369 {
370   // Play the animation on the 1st glow object.
371   mFocusEffect[0].animation.Play();
372
373   // Stagger the animation on the 2nd glow object half way through.
374   mFocusEffect[1].animation.PlayFrom( KEYBOARD_FOCUS_ANIMATION_DURATION / 2.0f );
375 }
376
377 void DaliTableView::OnFocusIndicatorDisabled( Dali::Actor /* actor */ )
378 {
379   // Stop the focus effect animations
380   for( auto i = 0u; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i )
381   {
382     mFocusEffect[ i ].animation.Stop();
383   }
384 }
385
386 void DaliTableView::ApplyCubeEffectToPages()
387 {
388   ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
389   unsigned int pageCount(0);
390   for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
391   {
392     Actor page = *pageIter;
393     effect.ApplyToPage( page, pageCount++ );
394   }
395 }
396
397 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
398 {
399 }
400
401 void DaliTableView::Populate()
402 {
403   const Vector2 stageSize = Stage::GetCurrent().GetSize();
404
405   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
406
407   // Populate ScrollView.
408   if( mExampleList.size() > 0 )
409   {
410     if( mSortAlphabetically )
411     {
412       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
413     }
414
415     unsigned int exampleCount = 0;
416     ExampleListConstIter iter = mExampleList.begin();
417
418     for( int t = 0; t < mTotalPages; t++ )
419     {
420       // Create Table
421       TableView page = TableView::New( ROWS_PER_PAGE, EXAMPLES_PER_ROW );
422       page.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
423       page.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
424       page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
425       mScrollView.Add( page );
426
427       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
428       const float margin = 2.0f;
429       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
430
431       for(int row = 0; row < ROWS_PER_PAGE; row++)
432       {
433         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
434         {
435           const Example& example = ( *iter );
436
437           // Calculate the tiles relative position on the page (between 0 & 1 in each dimension).
438           Vector2 position( static_cast<float>( column ) / ( EXAMPLES_PER_ROW - 1.0f ), static_cast<float>( row ) / ( EXAMPLES_PER_ROW - 1.0f ) );
439           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), position );
440           AccessibilityManager accessibilityManager = AccessibilityManager::Get();
441           accessibilityManager.SetFocusOrder( tile, ++exampleCount );
442           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
443                                                   example.title );
444           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
445           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT,
446                                                   "You can run this example" );
447
448           tile.SetProperty( Actor::Property::PADDING, Padding( margin, margin, margin, margin ) );
449           page.AddChild( tile, TableView::CellPosition( row, column ) );
450
451           iter++;
452
453           if( iter == mExampleList.end() )
454           {
455             break;
456           }
457         }
458
459         if( iter == mExampleList.end() )
460         {
461           break;
462         }
463       }
464
465       mPages.push_back( page );
466
467       if( iter == mExampleList.end() )
468       {
469         break;
470       }
471     }
472   }
473
474   // Update Ruler info.
475   mScrollRulerX = new FixedRuler( mPageWidth );
476   mScrollRulerY = new DefaultRuler();
477   mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
478   mScrollRulerY->Disable();
479   mScrollView.SetRulerX( mScrollRulerX );
480   mScrollView.SetRulerY( mScrollRulerY );
481 }
482
483 void DaliTableView::Rotate( unsigned int degrees )
484 {
485   // Resize the root actor
486   Vector2 stageSize = Stage::GetCurrent().GetSize();
487   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
488
489   if( degrees == 90 || degrees == 270 )
490   {
491     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
492   }
493
494   if( mRotateAnimation )
495   {
496     mRotateAnimation.Stop();
497     mRotateAnimation.Clear();
498   }
499
500   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
501   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
502   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
503   mRotateAnimation.Play();
504 }
505
506 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position )
507 {
508   Toolkit::ImageView focusableTile = ImageView::New();
509
510   focusableTile.SetStyleName( "DemoTile" );
511   focusableTile.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
512   focusableTile.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
513   focusableTile.SetProperty( Actor::Property::SIZE_MODE_FACTOR, sizeMultiplier );
514   focusableTile.SetProperty( Actor::Property::NAME, name );
515
516   // Set the tile to be keyboard focusable
517   focusableTile.SetProperty( Actor::Property::KEYBOARD_FOCUSABLE, true );
518
519   // Register a property with the ImageView. This allows us to inject the scroll-view position into the shader.
520   Property::Value value = Vector3( 0.0f, 0.0f, 0.0f );
521   Property::Index propertyIndex = focusableTile.RegisterProperty( "uCustomPosition", value );
522
523   // We create a constraint to perform a precalculation on the scroll-view X offset
524   // and pass it to the shader uniform, along with the tile's position.
525   Constraint shaderPosition = Constraint::New < Vector3 > ( focusableTile, propertyIndex, TileShaderPositionConstraint( mPageWidth, position.x ) );
526   shaderPosition.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
527   shaderPosition.SetRemoveAction( Constraint::Discard );
528   shaderPosition.Apply();
529   //focusableTile.Add( tileContent );
530
531   // Create an ImageView for the 9-patch border around the tile.
532   ImageView borderImage = ImageView::New();
533   borderImage.SetStyleName("DemoTileBorder");
534   borderImage.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
535   borderImage.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
536   borderImage.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
537   borderImage.SetProperty( Actor::Property::OPACITY, 0.8f );
538   focusableTile.Add( borderImage );
539
540   TextLabel label = TextLabel::New();
541   label.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER );
542   label.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
543   label.SetStyleName( "LauncherLabel" );
544   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
545   label.SetProperty( TextLabel::Property::TEXT, title );
546   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
547   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
548   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
549
550   // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
551   label.SetProperty( Actor::Property::PADDING, Padding( TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING ) );
552   focusableTile.Add( label );
553
554   // Connect to the touch events
555   focusableTile.TouchSignal().Connect( this, &DaliTableView::OnTilePressed );
556   focusableTile.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
557
558   return focusableTile;
559 }
560
561 bool DaliTableView::OnTilePressed( Actor actor, const TouchData& event )
562 {
563   return DoTilePress( actor, event.GetState( 0 ) );
564 }
565
566 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
567 {
568   bool consumed = false;
569
570   if( PointState::DOWN == pointState )
571   {
572     mPressedActor = actor;
573     consumed = true;
574   }
575
576   // A button press is only valid if the Down & Up events
577   // both occurred within the button.
578   if( ( PointState::UP == pointState ) &&
579       ( mPressedActor == actor ) )
580   {
581     // ignore Example button presses when scrolling or button animating.
582     if( ( !mScrolling ) && ( !mPressedAnimation ) )
583     {
584       std::string name = actor.GetProperty< std::string >( Dali::Actor::Property::NAME );
585       const ExampleListIter end = mExampleList.end();
586       for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
587       {
588         if( (*iter).name == name )
589         {
590           // do nothing, until pressed animation finished.
591           consumed = true;
592           break;
593         }
594       }
595     }
596
597     if( consumed )
598     {
599       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
600       mPressedAnimation.SetEndAction( Animation::Discard );
601
602       // scale the content actor within the Tile, as to not affect the placement within the Table.
603       Actor content = actor.GetChildAt(0);
604       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.7f, 0.7f, 1.0f ), AlphaFunction::EASE_IN_OUT,
605                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
606       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
607                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
608
609       // Rotate button on the Y axis when pressed.
610       mPressedAnimation.AnimateBy( Property( content, Actor::Property::ORIENTATION ), Quaternion( Degree( 0.0f ), Degree( 180.0f ), Degree( 0.0f ) ) );
611
612       mPressedAnimation.Play();
613       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
614     }
615   }
616   return consumed;
617 }
618
619 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
620 {
621   mPressedAnimation.Reset();
622   if( mPressedActor )
623   {
624     std::string name = mPressedActor.GetProperty< std::string >( Dali::Actor::Property::NAME );
625
626     ExecuteProcess( name, mApplication );
627
628     mPressedActor.Reset();
629   }
630 }
631
632 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
633 {
634   mScrolling = true;
635
636   PlayAnimation();
637 }
638
639 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
640 {
641   mScrolling = false;
642
643   // move focus to 1st item of new page
644   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
645   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
646 }
647
648 bool DaliTableView::OnScrollTouched( Actor actor, const TouchData& event )
649 {
650   if( PointState::DOWN == event.GetState( 0 ) )
651   {
652     mPressedActor = actor;
653   }
654
655   return false;
656 }
657
658 void DaliTableView::ApplyScrollViewEffect()
659 {
660   // Remove old effect if exists.
661
662   if( mScrollViewEffect )
663   {
664     mScrollView.RemoveEffect( mScrollViewEffect );
665   }
666
667   // Just one effect for now
668   SetupInnerPageCubeEffect();
669
670   mScrollView.ApplyEffect( mScrollViewEffect );
671 }
672
673 void DaliTableView::SetupInnerPageCubeEffect()
674 {
675   const Vector2 stageSize = Stage::GetCurrent().GetSize();
676
677   Dali::Path path = Dali::Path::New();
678   Dali::Property::Array points;
679   points.Resize(3);
680   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
681   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
682   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
683   path.SetProperty( Path::Property::POINTS, points );
684
685   Dali::Property::Array controlPoints;
686   controlPoints.Resize(4);
687   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
688   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
689   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
690   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.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(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
698 }
699
700 void DaliTableView::OnKeyEvent( const KeyEvent& event )
701 {
702   if( event.state == 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       Stage::GetCurrent().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 }