09a034388eb60ded86b658cab71dece739366866
[platform/core/uifw/dali-demo.git] / demo / dali-table-view.cpp
1 /*
2  * Copyright (c) 2016 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 int NUM_BACKGROUND_IMAGES = 18;
86 const float BACKGROUND_SWIPE_SCALE = 0.025f;
87 const float BACKGROUND_SPREAD_SCALE = 1.5f;
88 const float SCALE_MOD = 1000.0f * Math::PI * 2.0f;
89 const float SCALE_SPEED = 10.0f;
90 const float SCALE_SPEED_SIN = 0.1f;
91
92 const unsigned int BACKGROUND_ANIMATION_DURATION = 15000; // 15 secs
93
94 const Vector4 BACKGROUND_COLOR( 0.3569f, 0.5451f, 0.7294f, 1.0f );
95
96 const float BUBBLE_MIN_Z = -1.0;
97 const float BUBBLE_MAX_Z = 0.0f;
98
99 // This shader takes a texture.
100 // An alpha discard is performed.
101 // The shader uses the tiles position within the scroll-view page and the scroll-views rotation position to create a parallax effect.
102 const char* FRAGMENT_SHADER_TEXTURED = DALI_COMPOSE_SHADER(
103   varying mediump vec2  vTexCoord;
104   varying mediump vec3  vIllumination;
105   uniform lowp    vec4  uColor;
106   uniform sampler2D     sTexture;
107   uniform mediump vec3  uCustomPosition;
108
109   void main()
110   {
111     if( texture2D( sTexture, vTexCoord ).a <= 0.0001 )
112     {
113       discard;
114     }
115
116     mediump vec2 wrapTexCoord = vec2( ( vTexCoord.x / 4.0 ) + ( uCustomPosition.x / 4.0 ) + ( uCustomPosition.z / 2.0 ), vTexCoord.y / 4.0 );
117     mediump vec4 color = texture2D( sTexture, wrapTexCoord );
118     mediump float positionWeight = ( uCustomPosition.y + 0.3 ) * color.r * 2.0;
119
120     gl_FragColor = vec4( positionWeight, positionWeight, positionWeight, 0.9 ) * uColor + vec4( uColor.xyz, 0.0 );
121   }
122 );
123
124 /**
125  * Creates the background image
126  */
127 Control CreateBackground( std::string stylename )
128 {
129   Control background = Control::New();
130   Stage::GetCurrent().Add( background );
131   background.SetStyleName( stylename );
132   background.SetName( "BACKGROUND" );
133   background.SetAnchorPoint( AnchorPoint::CENTER );
134   background.SetParentOrigin( ParentOrigin::CENTER );
135   background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
136   return background;
137 }
138
139 /**
140  * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
141  */
142 struct AnimateBubbleConstraint
143 {
144 public:
145   AnimateBubbleConstraint( const Vector3& initialPos, float scale )
146   : mInitialX( initialPos.x ),
147     mScale( scale )
148   {
149   }
150
151   void operator()( Vector3& position, const PropertyInputContainer& inputs )
152   {
153     const Vector3& parentSize = inputs[1]->GetVector3();
154     const Vector3& childSize = inputs[2]->GetVector3();
155
156     // Wrap bubbles vertically.
157     float range = parentSize.y + childSize.y;
158     // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
159     position.y -= range * ( floor( position.y / range ) + 0.5f );
160
161     // Bubbles X position moves parallax to horizontal
162     // panning by a scale factor unique to each bubble.
163     position.x = mInitialX + ( inputs[0]->GetVector2().x * mScale );
164   }
165
166 private:
167   float mInitialX;
168   float mScale;
169 };
170
171 /**
172  * Constraint to precalculate values from the scroll-view
173  * and tile positions to pass to the tile shader.
174  */
175 struct TileShaderPositionConstraint
176 {
177   TileShaderPositionConstraint( float pageWidth, float tileXOffset )
178   : mPageWidth( pageWidth ),
179     mTileXOffset( tileXOffset )
180   {
181   }
182
183   void operator()( Vector3& position, const PropertyInputContainer& inputs )
184   {
185     // Set up position.x as the tiles X offset (0.0 -> 1.0).
186     position.x = mTileXOffset;
187     // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
188     position.z = 1.0f * ( -fmod( inputs[0]->GetVector2().x, mPageWidth ) / mPageWidth );
189     // Set up position.y as a rectified version of the scroll-views X offset.
190     // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
191     if( position.z > 0.5f )
192     {
193       position.y = 1.0f - position.z;
194     }
195     else
196     {
197       position.y = position.z;
198     }
199   }
200
201 private:
202   float mPageWidth;
203   float mTileXOffset;
204 };
205
206 bool CompareByTitle( const Example& lhs, const Example& rhs )
207 {
208   return lhs.title < rhs.title;
209 }
210
211 } // namespace
212
213 DaliTableView::DaliTableView( Application& application )
214 : mApplication( application ),
215   mRootActor(),
216   mRotateAnimation(),
217   mPressedAnimation(),
218   mScrollView(),
219   mScrollViewEffect(),
220   mScrollRulerX(),
221   mScrollRulerY(),
222   mPressedActor(),
223   mAnimationTimer(),
224   mLogoTapDetector(),
225   mVersionPopup(),
226   mPages(),
227   mBackgroundAnimations(),
228   mExampleList(),
229   mPageWidth( 0.0f ),
230   mTotalPages(),
231   mScrolling( false ),
232   mSortAlphabetically( false ),
233   mBackgroundAnimsPlaying( false )
234 {
235   application.InitSignal().Connect( this, &DaliTableView::Initialize );
236 }
237
238 DaliTableView::~DaliTableView()
239 {
240 }
241
242 void DaliTableView::AddExample( Example example )
243 {
244   mExampleList.push_back( example );
245 }
246
247 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
248 {
249   mSortAlphabetically = sortAlphabetically;
250 }
251
252 void DaliTableView::Initialize( Application& application )
253 {
254   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
255   const Vector2 stageSize = Stage::GetCurrent().GetSize();
256
257   // Background
258   mRootActor = CreateBackground( "LauncherBackground" );
259   Stage::GetCurrent().Add( mRootActor );
260
261   // Add logo
262   ImageView logo = CreateLogo( LOGO_PATH );
263   logo.SetName( "LOGO_IMAGE" );
264   logo.SetAnchorPoint( AnchorPoint::TOP_CENTER );
265   logo.SetParentOrigin( Vector3( 0.5f, 0.1f, 0.5f ) );
266   logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
267   // The logo should appear on top of everything.
268   logo.SetDrawMode( DrawMode::OVERLAY_2D );
269   mRootActor.Add( logo );
270
271   // Show version in a popup when log is tapped
272   mLogoTapDetector = TapGestureDetector::New();
273   mLogoTapDetector.Attach( logo );
274   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
275
276   // Scrollview occupying the majority of the screen
277   mScrollView = ScrollView::New();
278   mScrollView.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
279   mScrollView.SetParentOrigin( Vector3( 0.5f, 1.0f - 0.05f, 0.5f ) );
280   mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
281   mScrollView.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT );
282   mScrollView.SetSizeModeFactor( Vector3( 0.0f, 0.6f, 0.0f ) );
283
284   const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * stageSize.width;
285   mScrollView.SetPadding( Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
286
287   mScrollView.SetAxisAutoLock( true );
288   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
289   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
290   mScrollView.TouchSignal().Connect( this, &DaliTableView::OnScrollTouched );
291
292   mPageWidth = stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f;
293
294   // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
295   Actor bubbleContainer = Actor::New();
296   bubbleContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
297   bubbleContainer.SetAnchorPoint( AnchorPoint::CENTER );
298   bubbleContainer.SetParentOrigin( ParentOrigin::CENTER );
299   SetupBackground( bubbleContainer );
300
301   mRootActor.Add( bubbleContainer );
302   mRootActor.Add( mScrollView );
303
304   // Add scroll view effect and setup constraints on pages
305   ApplyScrollViewEffect();
306
307   // Add pages and tiles
308   Populate();
309
310   // Remove constraints for inner cube effect
311   ApplyCubeEffectToPages();
312
313   Dali::Window winHandle = application.GetWindow();
314   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
315   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
316   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
317   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
318
319   // Set initial orientation
320   unsigned int degrees = 0;
321   Rotate( degrees );
322
323   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
324
325   // Background animation
326   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
327   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
328   mAnimationTimer.Start();
329   mBackgroundAnimsPlaying = true;
330
331   CreateFocusEffect();
332 }
333
334 void DaliTableView::CreateFocusEffect()
335 {
336   // Hook the required signals to manage the focus.
337   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
338   KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
339   AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
340
341   // Loop to create both actors for the focus highlight effect.
342   for( unsigned int i = 0; i < FOCUS_ANIMATION_ACTOR_NUMBER; ++i )
343   {
344     mFocusEffect[i].actor = ImageView::New( TILE_FOCUS );
345     mFocusEffect[i].actor.SetParentOrigin( ParentOrigin::CENTER );
346     mFocusEffect[i].actor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
347     mFocusEffect[i].actor.SetInheritScale( false );
348     mFocusEffect[i].actor.SetColorMode( USE_OWN_COLOR );
349
350     // Setup initial values pre-animation.
351     mFocusEffect[i].actor.SetScale( startScale );
352     mFocusEffect[i].actor.SetOpacity( 0.0f );
353
354     // Create and setup the animation to do the following:
355     //   1) Initial fade in over short period of time
356     //   2) Zoom in (larger) and fade out simultaneously over longer period of time.
357     mFocusEffect[i].animation = Animation::New( KEYBOARD_FOCUS_ANIMATION_DURATION );
358
359     mFocusEffect[i].animation.AnimateTo( Property( mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA ), KEYBOARD_FOCUS_END_ALPHA, AlphaFunction::LINEAR, TimePeriod( 0.0f, initialFadeDuration ) );
360     mFocusEffect[i].animation.AnimateTo( Property( mFocusEffect[i].actor, Actor::Property::SCALE ), endScale, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
361     mFocusEffect[i].animation.AnimateTo( Property( mFocusEffect[i].actor, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
362
363     mFocusEffect[i].animation.SetLooping( true );
364   }
365
366   // Parent the secondary effect from the primary.
367   mFocusEffect[0].actor.Add( mFocusEffect[1].actor );
368
369   // Play the animation on the 1st glow object.
370   mFocusEffect[0].animation.Play();
371   // Stagger the animation on the 2st glow object half way through.
372   mFocusEffect[1].animation.PlayFrom( KEYBOARD_FOCUS_ANIMATION_DURATION / 2.0f );
373
374   KeyboardFocusManager::Get().SetFocusIndicatorActor( mFocusEffect[0].actor );
375 }
376
377 void DaliTableView::ApplyCubeEffectToPages()
378 {
379   ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
380   unsigned int pageCount(0);
381   for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
382   {
383     Actor page = *pageIter;
384     effect.ApplyToPage( page, pageCount++ );
385   }
386 }
387
388 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
389 {
390 }
391
392 void DaliTableView::Populate()
393 {
394   const Vector2 stageSize = Stage::GetCurrent().GetSize();
395
396   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
397
398   // Populate ScrollView.
399   if( mExampleList.size() > 0 )
400   {
401     if( mSortAlphabetically )
402     {
403       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
404     }
405
406     unsigned int exampleCount = 0;
407     ExampleListConstIter iter = mExampleList.begin();
408
409     for( int t = 0; t < mTotalPages; t++ )
410     {
411       // Create Table
412       TableView page = TableView::New( ROWS_PER_PAGE, EXAMPLES_PER_ROW );
413       page.SetAnchorPoint( AnchorPoint::CENTER );
414       page.SetParentOrigin( ParentOrigin::CENTER );
415       page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
416       mScrollView.Add( page );
417
418       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
419       const float margin = 2.0f;
420       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
421
422       for(int row = 0; row < ROWS_PER_PAGE; row++)
423       {
424         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
425         {
426           const Example& example = ( *iter );
427
428           // Calculate the tiles relative position on the page (between 0 & 1 in each dimension).
429           Vector2 position( static_cast<float>( column ) / ( EXAMPLES_PER_ROW - 1.0f ), static_cast<float>( row ) / ( EXAMPLES_PER_ROW - 1.0f ) );
430           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), position );
431           AccessibilityManager accessibilityManager = AccessibilityManager::Get();
432           accessibilityManager.SetFocusOrder( tile, ++exampleCount );
433           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
434                                                   example.title );
435           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
436           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT,
437                                                   "You can run this example" );
438
439           tile.SetPadding( Padding( margin, margin, margin, margin ) );
440           page.AddChild( tile, TableView::CellPosition( row, column ) );
441
442           iter++;
443
444           if( iter == mExampleList.end() )
445           {
446             break;
447           }
448         }
449
450         if( iter == mExampleList.end() )
451         {
452           break;
453         }
454       }
455
456       // Set tableview position
457       Vector3 pagePos( stageSize.width * TABLE_RELATIVE_SIZE.x * t, 0.0f, 0.0f );
458       page.SetPosition( pagePos );
459
460       mPages.push_back( page );
461
462       if( iter == mExampleList.end() )
463       {
464         break;
465       }
466     }
467   }
468
469   // Update Ruler info.
470   mScrollRulerX = new FixedRuler( mPageWidth );
471   mScrollRulerY = new DefaultRuler();
472   mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
473   mScrollRulerY->Disable();
474   mScrollView.SetRulerX( mScrollRulerX );
475   mScrollView.SetRulerY( mScrollRulerY );
476 }
477
478 void DaliTableView::Rotate( unsigned int degrees )
479 {
480   // Resize the root actor
481   Vector2 stageSize = Stage::GetCurrent().GetSize();
482   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
483
484   if( degrees == 90 || degrees == 270 )
485   {
486     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
487   }
488
489   if( mRotateAnimation )
490   {
491     mRotateAnimation.Stop();
492     mRotateAnimation.Clear();
493   }
494
495   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
496   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
497   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
498   mRotateAnimation.Play();
499 }
500
501 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position )
502 {
503   Actor focusableTile = Actor::New();
504   focusableTile.SetParentOrigin( ParentOrigin::CENTER );
505   focusableTile.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
506   focusableTile.SetSizeModeFactor( sizeMultiplier );
507   focusableTile.SetName( name );
508
509   // Set the tile to be keyboard focusable
510   focusableTile.SetKeyboardFocusable( true );
511
512   Toolkit::ImageView tileContent = ImageView::New();
513   tileContent.SetParentOrigin( ParentOrigin::CENTER );
514   tileContent.SetAnchorPoint( AnchorPoint::CENTER );
515   tileContent.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
516
517   // Add the image via the property first.
518   tileContent.SetProperty( Toolkit::ImageView::Property::IMAGE, TILE_BACKGROUND_ALPHA );
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 map;
525   Property::Map customShader;
526   customShader[ Visual::Shader::Property::FRAGMENT_SHADER ] = FRAGMENT_SHADER_TEXTURED;
527   map[ Visual::Property::SHADER ] = customShader;
528   tileContent.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
529   tileContent.SetColor( TILE_COLOR );
530
531   // We create a constraint to perform a precalculation on the scroll-view X offset
532   // and pass it to the shader uniform, along with the tile's position.
533   Constraint shaderPosition = Constraint::New < Vector3 > ( tileContent, propertyIndex, TileShaderPositionConstraint( mPageWidth, position.x ) );
534   shaderPosition.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
535   shaderPosition.SetRemoveAction( Constraint::Discard );
536   shaderPosition.Apply();
537   focusableTile.Add( tileContent );
538
539   // Create an ImageView for the 9-patch border around the tile.
540   ImageView borderImage = ImageView::New( TILE_BACKGROUND );
541   borderImage.SetAnchorPoint( AnchorPoint::CENTER );
542   borderImage.SetParentOrigin( ParentOrigin::CENTER );
543   borderImage.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
544   borderImage.SetOpacity( 0.8f );
545   tileContent.Add( borderImage );
546
547   TextLabel label = TextLabel::New();
548   label.SetAnchorPoint( AnchorPoint::CENTER );
549   label.SetParentOrigin( ParentOrigin::CENTER );
550   label.SetStyleName( "LauncherLabel" );
551   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
552   label.SetProperty( TextLabel::Property::TEXT, title );
553   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
554   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
555   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
556
557   // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
558   label.SetPadding( Padding( TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING ) );
559   focusableTile.Add( label );
560
561   // Connect to the touch events
562   focusableTile.TouchSignal().Connect( this, &DaliTableView::OnTilePressed );
563   focusableTile.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
564
565   return focusableTile;
566 }
567
568 bool DaliTableView::OnTilePressed( Actor actor, const TouchData& event )
569 {
570   return DoTilePress( actor, event.GetState( 0 ) );
571 }
572
573 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
574 {
575   bool consumed = false;
576
577   if( PointState::DOWN == pointState )
578   {
579     mPressedActor = actor;
580     consumed = true;
581   }
582
583   // A button press is only valid if the Down & Up events
584   // both occurred within the button.
585   if( ( PointState::UP == pointState ) &&
586       ( mPressedActor == actor ) )
587   {
588     // ignore Example button presses when scrolling or button animating.
589     if( ( !mScrolling ) && ( !mPressedAnimation ) )
590     {
591       std::string name = actor.GetName();
592       const ExampleListIter end = mExampleList.end();
593       for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
594       {
595         if( (*iter).name == name )
596         {
597           // do nothing, until pressed animation finished.
598           consumed = true;
599           break;
600         }
601       }
602     }
603
604     if( consumed )
605     {
606       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
607       mPressedAnimation.SetEndAction( Animation::Discard );
608
609       // scale the content actor within the Tile, as to not affect the placement within the Table.
610       Actor content = actor.GetChildAt(0);
611       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.7f, 0.7f, 1.0f ), AlphaFunction::EASE_IN_OUT,
612                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
613       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
614                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
615
616       // Rotate button on the Y axis when pressed.
617       mPressedAnimation.AnimateBy( Property( content, Actor::Property::ORIENTATION ), Quaternion( Degree( 0.0f ), Degree( 180.0f ), Degree( 0.0f ) ) );
618
619       mPressedAnimation.Play();
620       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
621     }
622   }
623   return consumed;
624 }
625
626 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
627 {
628   mPressedAnimation.Reset();
629   if( mPressedActor )
630   {
631     std::string name = mPressedActor.GetName();
632
633     std::stringstream stream;
634     stream << DEMO_EXAMPLE_BIN << name.c_str();
635     pid_t pid = fork();
636     if( pid == 0)
637     {
638       execlp( stream.str().c_str(), name.c_str(), NULL );
639       DALI_ASSERT_ALWAYS(false && "exec failed!");
640     }
641     mPressedActor.Reset();
642   }
643 }
644
645 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
646 {
647   mScrolling = true;
648
649   PlayAnimation();
650 }
651
652 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
653 {
654   mScrolling = false;
655
656   // move focus to 1st item of new page
657   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
658   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
659 }
660
661 bool DaliTableView::OnScrollTouched( Actor actor, const TouchData& event )
662 {
663   if( PointState::DOWN == event.GetState( 0 ) )
664   {
665     mPressedActor = actor;
666   }
667
668   return false;
669 }
670
671 void DaliTableView::ApplyScrollViewEffect()
672 {
673   // Remove old effect if exists.
674
675   if( mScrollViewEffect )
676   {
677     mScrollView.RemoveEffect( mScrollViewEffect );
678   }
679
680   // Just one effect for now
681   SetupInnerPageCubeEffect();
682
683   mScrollView.ApplyEffect( mScrollViewEffect );
684 }
685
686 void DaliTableView::SetupInnerPageCubeEffect()
687 {
688   const Vector2 stageSize = Stage::GetCurrent().GetSize();
689
690   Dali::Path path = Dali::Path::New();
691   Dali::Property::Array points;
692   points.Resize(3);
693   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
694   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
695   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
696   path.SetProperty( Path::Property::POINTS, points );
697
698   Dali::Property::Array controlPoints;
699   controlPoints.Resize(4);
700   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
701   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
702   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
703   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
704   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
705
706
707   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
708                                                     Vector3(-1.0f,0.0f,0.0f),
709                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
710                                                     Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
711 }
712
713 void DaliTableView::OnKeyEvent( const KeyEvent& event )
714 {
715   if( event.state == KeyEvent::Down )
716   {
717     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
718     {
719       // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
720       // Otherwise quit.
721       if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
722       {
723         mVersionPopup.SetDisplayState( Popup::HIDDEN );
724       }
725       else
726       {
727         mApplication.Quit();
728       }
729     }
730   }
731 }
732
733 void DaliTableView::SetupBackground( Actor bubbleContainer )
734 {
735   // Create distance field shapes.
736   BufferImage distanceFields[2];
737   Size imageSize( 512, 512 );
738
739   CreateShapeImage( CIRCLE, imageSize, distanceFields[0] );
740   CreateShapeImage( BUBBLE, imageSize, distanceFields[1] );
741
742   // Add bubbles to the bubbleContainer.
743   // Note: The bubbleContainer is parented externally to this function.
744   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES, distanceFields );
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, BufferImage* distanceField )
784 {
785   for( int i = 0; i < count; ++i )
786   {
787     float randSize = Random::Range( 10.0f, 400.0f );
788     int distanceFieldType = static_cast<int>( Random::Range( 0.0f, 1.0f ) + 0.5f );
789     ImageView dfActor = ImageView::New( distanceField[ distanceFieldType ] );
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 void DaliTableView::CreateShapeImage( ShapeType shapeType, const Size& size, BufferImage& distanceFieldOut )
805 {
806   // this bitmap will hold the alpha map for the distance field shader
807   distanceFieldOut = BufferImage::New( size.width, size.height, Pixel::A8 );
808
809   // Generate bit pattern
810   std::vector< unsigned char > imageDataA8;
811   imageDataA8.reserve( size.width * size.height ); // A8
812
813   switch( shapeType )
814   {
815     case CIRCLE:
816       GenerateCircle( size, imageDataA8 );
817       break;
818     case BUBBLE:
819       GenerateCircle( size, imageDataA8, true );
820       break;
821     default:
822       break;
823   }
824
825   PixelBuffer* buffer = distanceFieldOut.GetBuffer();
826   if( buffer )
827   {
828     GenerateDistanceFieldMap( &imageDataA8[ 0 ], size, buffer, size, 8.0f, size );
829     distanceFieldOut.Update();
830   }
831 }
832
833 void DaliTableView::GenerateCircle( const Size& size, std::vector< unsigned char >& distanceFieldOut, bool hollow )
834 {
835   const float radius = size.width * 0.5f * size.width * 0.5f;
836   Vector2 center( size.width / 2, size.height / 2 );
837
838   for( int h = 0; h < size.height; ++h )
839   {
840     for( int w = 0; w < size.width; ++w )
841     {
842       Vector2 pos( w, h );
843       Vector2 dist = pos - center;
844
845       float distance = ( dist.x * dist.x ) + ( dist.y * dist.y );
846
847       // If hollow, check the distance against a min & max value, otherwise just use the max value.
848       unsigned char fillByte = ( hollow ? ( ( distance <= radius ) && ( distance > ( radius * 0.7f ) ) ) : ( distance <= radius ) ) ? 0xFF : 0x00;
849
850       distanceFieldOut.push_back( fillByte );
851     }
852   }
853 }
854
855 ImageView DaliTableView::CreateLogo( std::string imagePath )
856 {
857   ImageView logo = ImageView::New( imagePath );
858
859   logo.SetAnchorPoint( AnchorPoint::CENTER );
860   logo.SetParentOrigin( ParentOrigin::CENTER );
861
862   return logo;
863 }
864
865 bool DaliTableView::PauseBackgroundAnimation()
866 {
867   PauseAnimation();
868
869   return false;
870 }
871
872 void DaliTableView::PauseAnimation()
873 {
874   if( mBackgroundAnimsPlaying )
875   {
876     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
877     {
878       Animation anim = *animIter;
879
880       anim.Stop();
881     }
882
883     mBackgroundAnimsPlaying = false;
884   }
885 }
886
887 void DaliTableView::PlayAnimation()
888 {
889   if ( !mBackgroundAnimsPlaying )
890   {
891     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
892     {
893       Animation anim = *animIter;
894
895       anim.Play();
896     }
897
898     mBackgroundAnimsPlaying = true;
899   }
900
901   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
902 }
903
904 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
905 {
906   Actor nextFocusActor = proposed;
907
908   if( !current && !proposed  )
909   {
910     // Set the initial focus to the first tile in the current page should be focused.
911     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
912   }
913   else if( !proposed )
914   {
915     // ScrollView is being focused but nothing in the current page can be focused further
916     // in the given direction. We should work out which page to scroll to next.
917     int currentPage = mScrollView.GetCurrentPage();
918     int newPage = currentPage;
919     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
920     {
921       newPage--;
922     }
923     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
924     {
925       newPage++;
926     }
927
928     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
929     if( newPage == currentPage )
930     {
931       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
932       {
933         newPage = mTotalPages - 1;
934       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
935       {
936         newPage = 0;
937       }
938     }
939
940     // Scroll to the page in the given direction
941     mScrollView.ScrollTo(newPage);
942
943     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
944     {
945       // Work out the cell position for the last tile
946       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
947       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
948       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
949
950       // Move the focus to the last tile in the new page.
951       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
952     }
953     else
954     {
955       // Move the focus to the first tile in the new page.
956       nextFocusActor = mPages[newPage].GetChildAt(0);
957     }
958   }
959
960   return nextFocusActor;
961 }
962
963 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
964 {
965   if(activatedActor)
966   {
967     mPressedActor = activatedActor;
968
969     // Activate the current focused actor;
970     DoTilePress( mPressedActor, PointState::UP );
971   }
972 }
973
974 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
975 {
976   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
977   return true;
978 }
979
980 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
981 {
982   // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
983   if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
984   {
985     if ( !mVersionPopup )
986     {
987       std::ostringstream stream;
988       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
989       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
990       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
991
992       mVersionPopup = Dali::Toolkit::Popup::New();
993
994       Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
995       titleActor.SetName( "titleActor" );
996       titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
997
998       Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
999       contentActor.SetName( "contentActor" );
1000       contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
1001       contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
1002       contentActor.SetPadding( Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
1003
1004       mVersionPopup.SetTitle( titleActor );
1005       mVersionPopup.SetContent( contentActor );
1006
1007       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
1008       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
1009       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
1010
1011       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
1012       Stage::GetCurrent().Add( mVersionPopup );
1013     }
1014
1015     mVersionPopup.SetDisplayState( Popup::SHOWN );
1016   }
1017 }
1018
1019 void DaliTableView::HideVersionPopup()
1020 {
1021   // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
1022   if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
1023   {
1024     mVersionPopup.SetDisplayState( Popup::HIDDEN );
1025   }
1026 }