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