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