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