Spliting dali-demo into examples-reel and demo
[platform/core/uifw/dali-demo.git] / shared / 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   uniform lowp    vec4  uColor;
112   uniform sampler2D     sTexture;
113   uniform mediump vec3  uCustomPosition;
114
115   void main()
116   {
117     if( texture2D( sTexture, vTexCoord ).a <= 0.0001 )
118     {
119       discard;
120     }
121
122     mediump vec2 wrapTexCoord = vec2( ( vTexCoord.x / 4.0 ) + ( uCustomPosition.x / 4.0 ) + ( uCustomPosition.z / 2.0 ), vTexCoord.y / 4.0 );
123     mediump vec4 color = texture2D( sTexture, wrapTexCoord );
124     mediump float positionWeight = ( uCustomPosition.y + 0.3 ) * color.r * 2.0;
125
126     gl_FragColor = vec4( positionWeight, positionWeight, positionWeight, 0.9 ) * uColor + vec4( uColor.xyz, 0.0 );
127   }
128 );
129
130 /**
131  * Creates the background image
132  */
133 Control CreateBackground( std::string stylename )
134 {
135   Control background = Control::New();
136   Stage::GetCurrent().Add( background );
137   background.SetStyleName( stylename );
138   background.SetName( "BACKGROUND" );
139   background.SetAnchorPoint( AnchorPoint::CENTER );
140   background.SetParentOrigin( ParentOrigin::CENTER );
141   background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
142   return background;
143 }
144
145 /**
146  * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
147  */
148 struct AnimateBubbleConstraint
149 {
150 public:
151   AnimateBubbleConstraint( const Vector3& initialPos, float scale )
152   : mInitialX( initialPos.x ),
153     mScale( scale )
154   {
155   }
156
157   void operator()( Vector3& position, const PropertyInputContainer& inputs )
158   {
159     const Vector3& parentSize = inputs[1]->GetVector3();
160     const Vector3& childSize = inputs[2]->GetVector3();
161
162     // Wrap bubbles vertically.
163     float range = parentSize.y + childSize.y;
164     // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
165     position.y -= range * ( floor( position.y / range ) + 0.5f );
166
167     // Bubbles X position moves parallax to horizontal
168     // panning by a scale factor unique to each bubble.
169     position.x = mInitialX + ( inputs[0]->GetVector2().x * mScale );
170   }
171
172 private:
173   float mInitialX;
174   float mScale;
175 };
176
177 /**
178  * Constraint to precalculate values from the scroll-view
179  * and tile positions to pass to the tile shader.
180  */
181 struct TileShaderPositionConstraint
182 {
183   TileShaderPositionConstraint( float pageWidth, float tileXOffset )
184   : mPageWidth( pageWidth ),
185     mTileXOffset( tileXOffset )
186   {
187   }
188
189   void operator()( Vector3& position, const PropertyInputContainer& inputs )
190   {
191     // Set up position.x as the tiles X offset (0.0 -> 1.0).
192     position.x = mTileXOffset;
193     // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
194     position.z = 1.0f * ( -fmod( inputs[0]->GetVector2().x, mPageWidth ) / mPageWidth );
195     // Set up position.y as a rectified version of the scroll-views X offset.
196     // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
197     if( position.z > 0.5f )
198     {
199       position.y = 1.0f - position.z;
200     }
201     else
202     {
203       position.y = position.z;
204     }
205   }
206
207 private:
208   float mPageWidth;
209   float mTileXOffset;
210 };
211
212 bool CompareByTitle( const Example& lhs, const Example& rhs )
213 {
214   return lhs.title < rhs.title;
215 }
216
217 } // namespace
218
219 DaliTableView::DaliTableView( Application& application )
220 : mApplication( application ),
221   mRootActor(),
222   mRotateAnimation(),
223   mPressedAnimation(),
224   mScrollView(),
225   mScrollViewEffect(),
226   mScrollRulerX(),
227   mScrollRulerY(),
228   mPressedActor(),
229   mAnimationTimer(),
230   mLogoTapDetector(),
231   mVersionPopup(),
232   mPages(),
233   mBackgroundAnimations(),
234   mExampleList(),
235   mPageWidth( 0.0f ),
236   mTotalPages(),
237   mScrolling( false ),
238   mSortAlphabetically( false ),
239   mBackgroundAnimsPlaying( false )
240 {
241   application.InitSignal().Connect( this, &DaliTableView::Initialize );
242 }
243
244 DaliTableView::~DaliTableView()
245 {
246 }
247
248 void DaliTableView::AddExample( Example example )
249 {
250   mExampleList.push_back( example );
251 }
252
253 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
254 {
255   mSortAlphabetically = sortAlphabetically;
256 }
257
258 void DaliTableView::Initialize( Application& application )
259 {
260   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
261   const Vector2 stageSize = Stage::GetCurrent().GetSize();
262
263   // Background
264   mRootActor = CreateBackground( "LauncherBackground" );
265   Stage::GetCurrent().Add( mRootActor );
266
267   // Add logo
268   ImageView logo = ImageView::New( LOGO_PATH );
269   logo.SetName( "LOGO_IMAGE" );
270   logo.SetAnchorPoint( AnchorPoint::TOP_CENTER );
271   logo.SetParentOrigin( Vector3( 0.5f, 0.1f, 0.5f ) );
272   logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
273   // The logo should appear on top of everything.
274   logo.SetDrawMode( DrawMode::OVERLAY_2D );
275   mRootActor.Add( logo );
276
277   // Show version in a popup when log is tapped
278   mLogoTapDetector = TapGestureDetector::New();
279   mLogoTapDetector.Attach( logo );
280   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
281
282   // Scrollview occupying the majority of the screen
283   mScrollView = ScrollView::New();
284   mScrollView.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
285   mScrollView.SetParentOrigin( Vector3( 0.5f, 1.0f - 0.05f, 0.5f ) );
286   mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
287   mScrollView.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT );
288   mScrollView.SetSizeModeFactor( Vector3( 0.0f, 0.6f, 0.0f ) );
289
290   const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * stageSize.width;
291   mScrollView.SetPadding( Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
292
293   mScrollView.SetAxisAutoLock( true );
294   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
295   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
296   mScrollView.TouchSignal().Connect( this, &DaliTableView::OnScrollTouched );
297
298   mPageWidth = stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f;
299
300   // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
301   Actor bubbleContainer = Actor::New();
302   bubbleContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
303   bubbleContainer.SetAnchorPoint( AnchorPoint::CENTER );
304   bubbleContainer.SetParentOrigin( ParentOrigin::CENTER );
305   SetupBackground( bubbleContainer );
306
307   mRootActor.Add( bubbleContainer );
308   mRootActor.Add( mScrollView );
309
310   // Add scroll view effect and setup constraints on pages
311   ApplyScrollViewEffect();
312
313   // Add pages and tiles
314   Populate();
315
316   // Remove constraints for inner cube effect
317   ApplyCubeEffectToPages();
318
319   Dali::Window winHandle = application.GetWindow();
320   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
321   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
322   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
323   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
324
325   // Set initial orientation
326   unsigned int degrees = 0;
327   Rotate( degrees );
328
329   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
330
331   // Background animation
332   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
333   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
334   mAnimationTimer.Start();
335   mBackgroundAnimsPlaying = true;
336
337   CreateFocusEffect();
338 }
339
340 void DaliTableView::CreateFocusEffect()
341 {
342   // Hook the required signals to manage the focus.
343   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
344   KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
345   AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
346
347   // Loop to create both actors for the focus highlight effect.
348   for( unsigned int i = 0; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i )
349   {
350     mFocusEffect[i].actor = ImageView::New( TILE_FOCUS );
351     mFocusEffect[i].actor.SetParentOrigin( ParentOrigin::CENTER );
352     mFocusEffect[i].actor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
353     mFocusEffect[i].actor.SetInheritScale( false );
354     mFocusEffect[i].actor.SetColorMode( USE_OWN_COLOR );
355
356     // Setup initial values pre-animation.
357     mFocusEffect[i].actor.SetScale( startScale );
358     mFocusEffect[i].actor.SetOpacity( 0.0f );
359
360     // Create and setup the animation to do the following:
361     //   1) Initial fade in over short period of time
362     //   2) Zoom in (larger) and fade out simultaneously over longer period of time.
363     mFocusEffect[i].animation = Animation::New( KEYBOARD_FOCUS_ANIMATION_DURATION );
364
365     mFocusEffect[i].animation.AnimateTo( Property( mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA ), KEYBOARD_FOCUS_END_ALPHA, AlphaFunction::LINEAR, TimePeriod( 0.0f, initialFadeDuration ) );
366     mFocusEffect[i].animation.AnimateTo( Property( mFocusEffect[i].actor, Actor::Property::SCALE ), endScale, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
367     mFocusEffect[i].animation.AnimateTo( Property( mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
368
369     mFocusEffect[i].animation.SetLooping( true );
370   }
371
372   // Parent the secondary effect from the primary.
373   mFocusEffect[0].actor.Add( mFocusEffect[1].actor );
374
375   // Play the animation on the 1st glow object.
376   mFocusEffect[0].animation.Play();
377   // Stagger the animation on the 2st glow object half way through.
378   mFocusEffect[1].animation.PlayFrom( KEYBOARD_FOCUS_ANIMATION_DURATION / 2.0f );
379
380   KeyboardFocusManager::Get().SetFocusIndicatorActor( mFocusEffect[0].actor );
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   Actor focusableTile = Actor::New();
506   focusableTile.SetParentOrigin( ParentOrigin::CENTER );
507   focusableTile.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
508   focusableTile.SetSizeModeFactor( sizeMultiplier );
509   focusableTile.SetName( name );
510
511   // Set the tile to be keyboard focusable
512   focusableTile.SetKeyboardFocusable( true );
513
514   Toolkit::ImageView tileContent = ImageView::New();
515   tileContent.SetParentOrigin( ParentOrigin::CENTER );
516   tileContent.SetAnchorPoint( AnchorPoint::CENTER );
517   tileContent.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
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 = tileContent.RegisterProperty( "uCustomPosition", value );
522
523   // Add a shader to the image (details in shader source).
524   Property::Map customShader;
525   customShader[ Visual::Shader::Property::FRAGMENT_SHADER ] = FRAGMENT_SHADER_TEXTURED;
526
527   // Set the Image URL and the custom shader
528   Property::Map imageMap;
529   imageMap.Add( ImageVisual::Property::URL, TILE_BACKGROUND_ALPHA );
530   imageMap.Add( Visual::Property::SHADER, customShader );
531   tileContent.SetProperty( Toolkit::ImageView::Property::IMAGE, imageMap );
532
533   tileContent.SetColor( TILE_COLOR );
534
535   // We create a constraint to perform a precalculation on the scroll-view X offset
536   // and pass it to the shader uniform, along with the tile's position.
537   Constraint shaderPosition = Constraint::New < Vector3 > ( tileContent, propertyIndex, TileShaderPositionConstraint( mPageWidth, position.x ) );
538   shaderPosition.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
539   shaderPosition.SetRemoveAction( Constraint::Discard );
540   shaderPosition.Apply();
541   focusableTile.Add( tileContent );
542
543   // Create an ImageView for the 9-patch border around the tile.
544   ImageView borderImage = ImageView::New( TILE_BACKGROUND );
545   borderImage.SetAnchorPoint( AnchorPoint::CENTER );
546   borderImage.SetParentOrigin( ParentOrigin::CENTER );
547   borderImage.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
548   borderImage.SetOpacity( 0.8f );
549   tileContent.Add( borderImage );
550
551   TextLabel label = TextLabel::New();
552   label.SetAnchorPoint( AnchorPoint::CENTER );
553   label.SetParentOrigin( ParentOrigin::CENTER );
554   label.SetStyleName( "LauncherLabel" );
555   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
556   label.SetProperty( TextLabel::Property::TEXT, title );
557   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
558   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
559   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
560
561   // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
562   label.SetPadding( Padding( TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING ) );
563   focusableTile.Add( label );
564
565   // Connect to the touch events
566   focusableTile.TouchSignal().Connect( this, &DaliTableView::OnTilePressed );
567   focusableTile.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
568
569   return focusableTile;
570 }
571
572 bool DaliTableView::OnTilePressed( Actor actor, const TouchData& event )
573 {
574   return DoTilePress( actor, event.GetState( 0 ) );
575 }
576
577 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
578 {
579   bool consumed = false;
580
581   if( PointState::DOWN == pointState )
582   {
583     mPressedActor = actor;
584     consumed = true;
585   }
586
587   // A button press is only valid if the Down & Up events
588   // both occurred within the button.
589   if( ( PointState::UP == pointState ) &&
590       ( mPressedActor == actor ) )
591   {
592     // ignore Example button presses when scrolling or button animating.
593     if( ( !mScrolling ) && ( !mPressedAnimation ) )
594     {
595       std::string name = actor.GetName();
596       const ExampleListIter end = mExampleList.end();
597       for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
598       {
599         if( (*iter).name == name )
600         {
601           // do nothing, until pressed animation finished.
602           consumed = true;
603           break;
604         }
605       }
606     }
607
608     if( consumed )
609     {
610       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
611       mPressedAnimation.SetEndAction( Animation::Discard );
612
613       // scale the content actor within the Tile, as to not affect the placement within the Table.
614       Actor content = actor.GetChildAt(0);
615       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.7f, 0.7f, 1.0f ), AlphaFunction::EASE_IN_OUT,
616                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
617       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
618                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
619
620       // Rotate button on the Y axis when pressed.
621       mPressedAnimation.AnimateBy( Property( content, Actor::Property::ORIENTATION ), Quaternion( Degree( 0.0f ), Degree( 180.0f ), Degree( 0.0f ) ) );
622
623       mPressedAnimation.Play();
624       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
625     }
626   }
627   return consumed;
628 }
629
630 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
631 {
632   mPressedAnimation.Reset();
633   if( mPressedActor )
634   {
635     std::string name = mPressedActor.GetName();
636
637     std::stringstream stream;
638     stream << DEMO_EXAMPLE_BIN << name.c_str();
639     pid_t pid = fork();
640     if( pid == 0)
641     {
642       execlp( stream.str().c_str(), name.c_str(), NULL );
643       DALI_ASSERT_ALWAYS(false && "exec failed!");
644     }
645     mPressedActor.Reset();
646   }
647 }
648
649 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
650 {
651   mScrolling = true;
652
653   PlayAnimation();
654 }
655
656 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
657 {
658   mScrolling = false;
659
660   // move focus to 1st item of new page
661   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
662   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
663 }
664
665 bool DaliTableView::OnScrollTouched( Actor actor, const TouchData& event )
666 {
667   if( PointState::DOWN == event.GetState( 0 ) )
668   {
669     mPressedActor = actor;
670   }
671
672   return false;
673 }
674
675 void DaliTableView::ApplyScrollViewEffect()
676 {
677   // Remove old effect if exists.
678
679   if( mScrollViewEffect )
680   {
681     mScrollView.RemoveEffect( mScrollViewEffect );
682   }
683
684   // Just one effect for now
685   SetupInnerPageCubeEffect();
686
687   mScrollView.ApplyEffect( mScrollViewEffect );
688 }
689
690 void DaliTableView::SetupInnerPageCubeEffect()
691 {
692   const Vector2 stageSize = Stage::GetCurrent().GetSize();
693
694   Dali::Path path = Dali::Path::New();
695   Dali::Property::Array points;
696   points.Resize(3);
697   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
698   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
699   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
700   path.SetProperty( Path::Property::POINTS, points );
701
702   Dali::Property::Array controlPoints;
703   controlPoints.Resize(4);
704   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
705   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
706   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
707   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
708   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
709
710
711   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
712                                                     Vector3(-1.0f,0.0f,0.0f),
713                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
714                                                     Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
715 }
716
717 void DaliTableView::OnKeyEvent( const KeyEvent& event )
718 {
719   if( event.state == KeyEvent::Down )
720   {
721     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
722     {
723       // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
724       // Otherwise quit.
725       if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
726       {
727         mVersionPopup.SetDisplayState( Popup::HIDDEN );
728       }
729       else
730       {
731         mApplication.Quit();
732       }
733     }
734   }
735 }
736
737 void DaliTableView::SetupBackground( Actor bubbleContainer )
738 {
739   // Add bubbles to the bubbleContainer.
740   // Note: The bubbleContainer is parented externally to this function.
741   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES );
742 }
743
744 void DaliTableView::InitialiseBackgroundActors( Actor actor )
745 {
746   // Delete current animations
747   mBackgroundAnimations.clear();
748
749   // Create new animations
750   const Vector3 size = actor.GetTargetSize();
751
752   for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
753   {
754     Actor child = actor.GetChildAt( i );
755
756     // Calculate a random position
757     Vector3 childPos( Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.85f * BACKGROUND_SPREAD_SCALE ),
758                       Random::Range( -size.y, size.y ),
759                       Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
760
761     child.SetPosition( childPos );
762
763     // Define bubble horizontal parallax and vertical wrapping
764     Constraint animConstraint = Constraint::New < Vector3 > ( child, Actor::Property::POSITION, AnimateBubbleConstraint( childPos, Random::Range( -0.85f, 0.25f ) ) );
765     animConstraint.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
766     animConstraint.AddSource( Dali::ParentSource( Dali::Actor::Property::SIZE ) );
767     animConstraint.AddSource( Dali::LocalSource( Dali::Actor::Property::SIZE ) );
768     animConstraint.SetRemoveAction( Constraint::Discard );
769     animConstraint.Apply();
770
771     // Kickoff animation
772     Animation animation = Animation::New( Random::Range( 30.0f, 160.0f ) );
773     animation.AnimateBy( Property( child, Actor::Property::POSITION ), Vector3( 0.0f, -2000.0f, 0.0f ), AlphaFunction::LINEAR );
774     animation.SetLooping( true );
775     animation.Play();
776     mBackgroundAnimations.push_back( animation );
777   }
778 }
779
780 void DaliTableView::AddBackgroundActors( Actor layer, int count )
781 {
782   for( int i = 0; i < count; ++i )
783   {
784     float randSize = Random::Range( 10.0f, 400.0f );
785     int shapeType = static_cast<int>( Random::Range( 0.0f, NUMBER_OF_SHAPE_IMAGES - 1 ) + 0.5f );
786
787     ImageView dfActor = ImageView::New();
788     dfActor.SetSize( Vector2( randSize, randSize ) );
789     dfActor.SetParentOrigin( ParentOrigin::CENTER );
790
791     // Set the Image URL and the custom shader at the same time
792     Dali::Property::Map effect = Toolkit::CreateDistanceFieldEffect();
793     Property::Map imageMap;
794     imageMap.Add( ImageVisual::Property::URL, SHAPE_IMAGE_TABLE[ shapeType ] );
795     imageMap.Add( Visual::Property::SHADER, effect );
796     dfActor.SetProperty( Toolkit::ImageView::Property::IMAGE, imageMap );
797
798     dfActor.SetColor( BUBBLE_COLOR[ i%NUMBER_OF_BUBBLE_COLOR ] );
799
800     layer.Add( dfActor );
801   }
802
803   // Positioning will occur when the layer is relaid out
804   layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
805 }
806
807 bool DaliTableView::PauseBackgroundAnimation()
808 {
809   PauseAnimation();
810
811   return false;
812 }
813
814 void DaliTableView::PauseAnimation()
815 {
816   if( mBackgroundAnimsPlaying )
817   {
818     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
819     {
820       Animation anim = *animIter;
821
822       anim.Stop();
823     }
824
825     mBackgroundAnimsPlaying = false;
826   }
827 }
828
829 void DaliTableView::PlayAnimation()
830 {
831   if ( !mBackgroundAnimsPlaying )
832   {
833     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
834     {
835       Animation anim = *animIter;
836
837       anim.Play();
838     }
839
840     mBackgroundAnimsPlaying = true;
841   }
842
843   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
844 }
845
846 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
847 {
848   Actor nextFocusActor = proposed;
849
850   if( !current && !proposed  )
851   {
852     // Set the initial focus to the first tile in the current page should be focused.
853     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
854   }
855   else if( !proposed )
856   {
857     // ScrollView is being focused but nothing in the current page can be focused further
858     // in the given direction. We should work out which page to scroll to next.
859     int currentPage = mScrollView.GetCurrentPage();
860     int newPage = currentPage;
861     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
862     {
863       newPage--;
864     }
865     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
866     {
867       newPage++;
868     }
869
870     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
871     if( newPage == currentPage )
872     {
873       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
874       {
875         newPage = mTotalPages - 1;
876       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
877       {
878         newPage = 0;
879       }
880     }
881
882     // Scroll to the page in the given direction
883     mScrollView.ScrollTo(newPage);
884
885     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
886     {
887       // Work out the cell position for the last tile
888       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
889       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
890       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
891
892       // Move the focus to the last tile in the new page.
893       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
894     }
895     else
896     {
897       // Move the focus to the first tile in the new page.
898       nextFocusActor = mPages[newPage].GetChildAt(0);
899     }
900   }
901
902   return nextFocusActor;
903 }
904
905 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
906 {
907   if(activatedActor)
908   {
909     mPressedActor = activatedActor;
910
911     // Activate the current focused actor;
912     DoTilePress( mPressedActor, PointState::UP );
913   }
914 }
915
916 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
917 {
918   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
919   return true;
920 }
921
922 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
923 {
924   // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
925   if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
926   {
927     if ( !mVersionPopup )
928     {
929       std::ostringstream stream;
930       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
931       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
932       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
933
934       mVersionPopup = Dali::Toolkit::Popup::New();
935
936       Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
937       titleActor.SetName( "titleActor" );
938       titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
939
940       Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
941       contentActor.SetName( "contentActor" );
942       contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
943       contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
944       contentActor.SetPadding( Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
945
946       mVersionPopup.SetTitle( titleActor );
947       mVersionPopup.SetContent( contentActor );
948
949       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
950       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
951       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
952
953       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
954       Stage::GetCurrent().Add( mVersionPopup );
955     }
956
957     mVersionPopup.SetDisplayState( Popup::SHOWN );
958   }
959 }
960
961 void DaliTableView::HideVersionPopup()
962 {
963   // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
964   if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
965   {
966     mVersionPopup.SetDisplayState( Popup::HIDDEN );
967   }
968 }