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