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