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