Merge remote-tracking branch 'origin/tizen' into devel/new_mesh
[platform/core/uifw/dali-demo.git] / demo / dali-table-view.cpp
1 /*
2  * Copyright (c) 2014 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
29 // INTERNAL INCLUDES
30 #include "shared/view.h"
31
32 using namespace Dali;
33 using namespace Dali::Toolkit;
34
35 ///////////////////////////////////////////////////////////////////////////////
36
37 namespace
38 {
39
40 const std::string BUTTON_BACKWARD( "Backward" );
41 const std::string BUTTON_FORWARD( "Forward" );
42 const std::string BUTTON_QUIT( "Quit" );
43 const std::string BUTTON_OK( "Ok" );
44 const std::string BUTTON_CANCEL( "Cancel" );
45
46 const std::string DEFAULT_BACKGROUND_IMAGE_PATH( DALI_IMAGE_DIR "background-gradient.jpg" );
47 const std::string LOGO_PATH( DALI_IMAGE_DIR "dali-logo.png" );
48 const std::string DEFAULT_TOOLBAR_IMAGE_PATH( DALI_IMAGE_DIR "top-bar.png" );
49 const std::string BUTTON_BACKGROUND(DALI_IMAGE_DIR "button-background.png");
50 const std::string TILE_BACKGROUND(DALI_IMAGE_DIR "item-background.png");
51 const std::string TILE_BACKGROUND_ALPHA(DALI_IMAGE_DIR "item-background-alpha.png");
52
53 const char * const DEFAULT_TOOLBAR_TEXT( "TOUCH TO LAUNCH EXAMPLE" );
54
55 const float BUTTON_PRESS_ANIMATION_TIME = 0.25f;                ///< Time to perform button scale effect.
56 const float ROTATE_ANIMATION_TIME = 0.5f;                       ///< Time to perform rotate effect.
57 const int MAX_PAGES = 256;                                      ///< Maximum pages (arbitrary safety limit)
58 const int EXAMPLES_PER_ROW = 3;
59 const int ROWS_PER_PAGE = 3;
60 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
61 const float LOGO_MARGIN_RATIO = 0.1f / 0.3f;
62 const float BOTTOM_PADDING_RATIO = 0.4f / 0.9f;
63 const Vector3 SCROLLVIEW_RELATIVE_SIZE(0.9f, 1.0f, 0.8f );     ///< ScrollView's relative size to its parent
64 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.
65 const float STENCIL_RELATIVE_SIZE = 1.0f;
66
67 const float EFFECT_SNAP_DURATION = 0.66f;                       ///< Scroll Snap Duration for Effects
68 const float EFFECT_FLICK_DURATION = 0.5f;                       ///< Scroll Flick Duration for Effects
69 const Vector3 ANGLE_CUBE_PAGE_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
70
71
72
73 const int NUM_BACKGROUND_IMAGES = 18;
74 const float BACKGROUND_SWIPE_SCALE = 0.025f;
75 const float BACKGROUND_SPREAD_SCALE = 1.5f;
76 const float SCALE_MOD = 1000.0f * Math::PI * 2.0f;
77 const float SCALE_SPEED = 10.0f;
78 const float SCALE_SPEED_SIN = 0.1f;
79
80 const unsigned int BACKGROUND_ANIMATION_DURATION = 15000; // 15 secs
81
82 const float BACKGROUND_Z = -1.0f;
83 const Vector4 BACKGROUND_COLOR( 1.0f, 1.0f, 1.0f, 1.0f );
84
85 const float BUBBLE_MIN_Z = -1.0;
86 const float BUBBLE_MAX_Z = 0.0f;
87
88 /**
89  * Creates the background image
90  */
91 ImageActor CreateBackground( std::string imagePath )
92 {
93   Image image = ResourceImage::New( imagePath );
94   ImageActor background = ImageActor::New( image );
95   background.SetName( "BACKGROUND" );
96   background.SetAnchorPoint( AnchorPoint::CENTER );
97   background.SetParentOrigin( ParentOrigin::CENTER );
98   background.SetZ( -1.0f );
99   background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
100
101   return background;
102 }
103
104 // These values depend on the tile image
105 const float IMAGE_BORDER_LEFT = 11.0f;
106 const float IMAGE_BORDER_RIGHT = IMAGE_BORDER_LEFT;
107 const float IMAGE_BORDER_TOP = IMAGE_BORDER_LEFT;
108 const float IMAGE_BORDER_BOTTOM = IMAGE_BORDER_LEFT;
109
110 /**
111  * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
112  */
113 struct AnimateBubbleConstraint
114 {
115 public:
116   AnimateBubbleConstraint( const Vector3& initialPos, float scale )
117       : mInitialX( initialPos.x ),
118         mScale( scale )
119   {
120   }
121
122   void operator()( Vector3& position, const PropertyInputContainer& inputs )
123   {
124     const Vector3& parentSize = inputs[1]->GetVector3();
125     const Vector3& childSize = inputs[2]->GetVector3();
126
127     // Wrap bubbles verically.
128     if( position.y + childSize.y * 0.5f < -parentSize.y * 0.5f )
129     {
130       position.y = parentSize.y * 0.5f + childSize.y * 0.5f;
131     }
132
133     // Bubbles X position moves parallax to horizontal
134     // panning by a scale factor unique to each bubble.
135     position.x = mInitialX + ( inputs[0]->GetVector2().x * mScale );
136   }
137
138 private:
139   float mInitialX;
140   float mScale;
141   float mShapeSize;
142 };
143
144 bool CompareByTitle( const Example& lhs, const Example& rhs )
145 {
146   return lhs.title < rhs.title;
147 }
148
149 } // namespace
150
151 DaliTableView::DaliTableView( Application& application )
152 : mApplication( application ),
153   mBackgroundLayer(),
154   mRootActor(),
155   mRotateAnimation(),
156   mBackground(),
157   mPressedAnimation(),
158   mScrollViewLayer(),
159   mScrollView(),
160   mScrollViewEffect(),
161   mScrollRulerX(),
162   mScrollRulerY(),
163   mButtons(),
164   mPressedActor(),
165   mAnimationTimer(),
166   mLogoTapDetector(),
167   mVersionPopup(),
168   mButtonsPageRelativeSize(),
169   mPages(),
170   mTableViewImages(),
171   mBackgroundActors(),
172   mBackgroundAnimations(),
173   mExampleList(),
174   mExampleMap(),
175   mBackgroundImagePath( DEFAULT_BACKGROUND_IMAGE_PATH ),
176   mTotalPages(),
177   mScrolling( false ),
178   mSortAlphabetically( false ),
179   mBackgroundAnimsPlaying( false ),
180   mVersionPopupShown( false )
181 {
182   application.InitSignal().Connect( this, &DaliTableView::Initialize );
183 }
184
185 DaliTableView::~DaliTableView()
186 {
187 }
188
189 void DaliTableView::AddExample( Example example )
190 {
191   mExampleList.push_back( example );
192   mExampleMap[ example.name ] = example;
193 }
194
195 void DaliTableView::SetBackgroundPath( std::string imagePath )
196 {
197   mBackgroundImagePath = imagePath;
198 }
199
200 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
201 {
202   mSortAlphabetically = sortAlphabetically;
203 }
204
205 void DaliTableView::Initialize( Application& application )
206 {
207   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
208
209   const Vector2 stageSize = Stage::GetCurrent().GetSize();
210
211   // Background
212   Actor background = CreateBackground( mBackgroundImagePath );
213   Stage::GetCurrent().Add( background );
214
215   // Render entire content as overlays, as is all on same 2D plane.
216   mRootActor = TableView::New( 4, 1 );
217   mRootActor.SetAnchorPoint( AnchorPoint::CENTER );
218   mRootActor.SetParentOrigin( ParentOrigin::CENTER );
219   mRootActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
220   Stage::GetCurrent().Add( mRootActor );
221
222   // Toolbar at top
223   Dali::Toolkit::ToolBar toolbar;
224   Dali::Layer toolBarLayer = DemoHelper::CreateToolbar(toolbar,
225                                                        DEFAULT_TOOLBAR_IMAGE_PATH,
226                                                        DEFAULT_TOOLBAR_TEXT,
227                                                        DemoHelper::DEFAULT_VIEW_STYLE);
228
229   mRootActor.AddChild( toolBarLayer, TableView::CellPosition( 0, 0 ) );
230   mRootActor.SetFitHeight( 0 );
231
232   // Add logo
233   Dali::ImageActor logo = CreateLogo( LOGO_PATH );
234   logo.SetName( "LOGO_IMAGE" );
235   logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
236   const float paddingHeight = ( ( 1.f-TABLE_RELATIVE_SIZE.y ) * stageSize.y );
237   const float logoMargin = paddingHeight * LOGO_MARGIN_RATIO;
238
239   // Show version in a popup when log is tapped
240   mLogoTapDetector = TapGestureDetector::New();
241   mLogoTapDetector.Attach( logo );
242   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
243
244   const float bottomMargin = paddingHeight * BOTTOM_PADDING_RATIO;
245
246   Alignment alignment = Alignment::New();
247   alignment.SetName( "LOGO_ALIGNMENT" );
248   alignment.Add( logo );
249   alignment.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
250   alignment.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
251   Actor alignmentActor = alignment;
252   alignmentActor.SetPadding( Padding( 0.0f, 0.0f, logoMargin, logoMargin ));
253   mRootActor.AddChild( alignment, TableView::CellPosition( 1, 0 ) );
254   mRootActor.SetFitHeight( 1 );
255
256   // scrollview occupying the majority of the screen
257   mScrollView = ScrollView::New();
258
259   mScrollView.SetAnchorPoint( AnchorPoint::CENTER );
260   mScrollView.SetParentOrigin( ParentOrigin::CENTER );
261   mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
262   const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * stageSize.width;
263   mScrollView.SetPadding( Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
264
265   mScrollView.SetAxisAutoLock( true );
266   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
267   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
268   mScrollView.TouchedSignal().Connect( this, &DaliTableView::OnScrollTouched );
269
270   mScrollViewLayer = Layer::New();
271   mScrollViewLayer.SetAnchorPoint( AnchorPoint::CENTER );
272   mScrollViewLayer.SetParentOrigin( ParentOrigin::CENTER );
273   mScrollViewLayer.SetDrawMode( DrawMode::OVERLAY );
274   mScrollViewLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
275
276   // Create solid background colour.
277   ImageActor backgroundColourActor = Dali::Toolkit::CreateSolidColorActor( BACKGROUND_COLOR );
278   backgroundColourActor.SetAnchorPoint( AnchorPoint::CENTER );
279   backgroundColourActor.SetParentOrigin( ParentOrigin::CENTER );
280   backgroundColourActor.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
281   backgroundColourActor.SetSizeModeFactor( Vector3( 1.0f, 1.5f, 1.0f ) );
282   backgroundColourActor.SetZ( BACKGROUND_Z );
283   mScrollViewLayer.Add( backgroundColourActor );
284
285   // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
286   Actor bubbleContainer = Actor::New();
287   bubbleContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
288   bubbleContainer.SetAnchorPoint( AnchorPoint::CENTER );
289   bubbleContainer.SetParentOrigin( ParentOrigin::CENTER );
290   mScrollViewLayer.Add( bubbleContainer );
291
292   SetupBackground( bubbleContainer );
293
294   Alignment buttonsAlignment = Alignment::New();
295   buttonsAlignment.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
296   buttonsAlignment.Add( mScrollViewLayer );
297
298   mScrollViewLayer.Add( mScrollView );
299
300   mRootActor.AddChild( buttonsAlignment, TableView::CellPosition( 2, 0 ) );
301
302   mRootActor.SetFixedHeight( 3, bottomMargin );
303
304   // Add scroll view effect and setup constraints on pages
305   ApplyScrollViewEffect();
306
307   // Add pages and tiles
308   Populate();
309
310   // Remove constraints for inner cube effect
311   ApplyCubeEffectToPages();
312
313   Dali::Window winHandle = application.GetWindow();
314   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
315   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
316   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
317   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
318
319   // Set initial orientation
320   unsigned int degrees = 0;
321   Rotate( degrees );
322
323   //orientation.ChangedSignal().Connect( this, &DaliTableView::OrientationChanged );
324
325   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
326
327   // Background animation
328   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
329   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
330   mAnimationTimer.Start();
331   mBackgroundAnimsPlaying = true;
332
333   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
334   KeyboardFocusManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
335 }
336
337 void DaliTableView::ApplyCubeEffectToPages()
338 {
339   ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
340   unsigned int pageCount(0);
341   for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
342   {
343     Actor page = *pageIter;
344     effect.ApplyToPage( page, pageCount++ );
345   }
346 }
347
348 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
349 {
350
351 }
352
353 void DaliTableView::Populate()
354 {
355   const Vector2 stageSize = Stage::GetCurrent().GetSize();
356
357   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
358
359   // Populate ScrollView.
360   if( mExampleList.size() > 0 )
361   {
362     if( mSortAlphabetically )
363     {
364       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
365     }
366
367     unsigned int exampleCount = 0;
368     ExampleListConstIter iter = mExampleList.begin();
369
370     for( int t = 0; t < mTotalPages; t++ )
371     {
372       // Create Table. (contains up to 9 Examples)
373       TableView page = TableView::New( 3, 3 );
374       page.SetAnchorPoint( AnchorPoint::CENTER );
375       page.SetParentOrigin( ParentOrigin::CENTER );
376       page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
377       mScrollView.Add( page );
378
379       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
380       const float margin = 2.0f;
381       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
382
383       for(int row = 0; row < ROWS_PER_PAGE; row++)
384       {
385         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
386         {
387           const Example& example = ( *iter );
388
389           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), true );
390           AccessibilityFocusManager accessibilityFocusManager = AccessibilityFocusManager::Get();
391           accessibilityFocusManager.SetFocusOrder( tile, ++exampleCount );
392           accessibilityFocusManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityFocusManager::ACCESSIBILITY_LABEL,
393                                                   example.title );
394           accessibilityFocusManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityFocusManager::ACCESSIBILITY_TRAIT, "Tile" );
395           accessibilityFocusManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityFocusManager::ACCESSIBILITY_HINT,
396                                                   "You can run this example" );
397
398           tile.SetPadding( Padding( margin, margin, margin, margin ) );
399
400           page.AddChild( tile, TableView::CellPosition( row, column ) );
401
402           iter++;
403
404           if( iter == mExampleList.end() )
405           {
406             break;
407           }
408         }
409
410         if( iter == mExampleList.end() )
411         {
412           break;
413         }
414       }
415
416       // Set tableview position
417       Vector3 pagePos( stageSize.width * TABLE_RELATIVE_SIZE.x * t, 0.0f, 0.0f );
418       page.SetPosition( pagePos );
419
420       mPages.push_back( page );
421
422       if( iter == mExampleList.end() )
423       {
424         break;
425       }
426     }
427   }
428
429   // Update Ruler info.
430   mScrollRulerX = new FixedRuler( stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f );
431   mScrollRulerY = new DefaultRuler();
432   mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
433   mScrollRulerY->Disable();
434   mScrollView.SetRulerX( mScrollRulerX );
435   mScrollView.SetRulerY( mScrollRulerY );
436 }
437
438 void DaliTableView::OrientationChanged( Orientation orientation )
439 {
440   // TODO: Implement if orientation change required
441 }
442
443 void DaliTableView::Rotate( unsigned int degrees )
444 {
445   // Resize the root actor
446   Vector2 stageSize = Stage::GetCurrent().GetSize();
447   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
448
449   if( degrees == 90 || degrees == 270 )
450   {
451     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
452   }
453
454   if( mRotateAnimation )
455   {
456     mRotateAnimation.Stop();
457     mRotateAnimation.Clear();
458   }
459
460   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
461   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
462   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
463   mRotateAnimation.Play();
464 }
465
466 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, bool addBackground )
467 {
468   Actor content = Actor::New();
469   content.SetName( name );
470   content.SetAnchorPoint( AnchorPoint::CENTER );
471   content.SetParentOrigin( ParentOrigin::CENTER );
472   content.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
473   content.SetSizeModeFactor( sizeMultiplier );
474
475   // create background image
476   if( addBackground )
477   {
478     Image bg = ResourceImage::New( TILE_BACKGROUND );
479     ImageActor image = ImageActor::New( bg );
480     image.SetAnchorPoint( AnchorPoint::CENTER );
481     image.SetParentOrigin( ParentOrigin::CENTER );
482     // make the image 100% of tile
483     image.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
484
485     // move image back to get text appear in front
486     image.SetZ( -1 );
487     image.SetStyle( ImageActor::STYLE_NINE_PATCH );
488     image.SetNinePatchBorder( Vector4( IMAGE_BORDER_LEFT, IMAGE_BORDER_TOP, IMAGE_BORDER_RIGHT, IMAGE_BORDER_BOTTOM ) );
489     content.Add( image );
490
491     // Add stencil
492     ImageActor stencil = NewStencilImage();
493     stencil.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
494     image.Add( stencil );
495   }
496
497   TextLabel label = TextLabel::New();
498   label.SetAnchorPoint( AnchorPoint::TOP_LEFT );
499   label.SetProperty( Control::Property::STYLE_NAME, "launcherlabel" );
500   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
501   label.SetProperty( TextLabel::Property::TEXT, title );
502   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
503   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
504   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
505   content.Add( label );
506
507   // Set the tile to be keyboard focusable
508   content.SetKeyboardFocusable(true);
509
510   // connect to the touch events
511   content.TouchedSignal().Connect( this, &DaliTableView::OnTilePressed );
512   content.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
513
514   return content;
515 }
516
517 ImageActor DaliTableView::NewStencilImage()
518 {
519   Image alpha = ResourceImage::New( TILE_BACKGROUND_ALPHA );
520
521   ImageActor stencilActor = ImageActor::New( alpha );
522   stencilActor.SetStyle( ImageActor::STYLE_NINE_PATCH );
523   stencilActor.SetNinePatchBorder( Vector4( IMAGE_BORDER_LEFT, IMAGE_BORDER_TOP, IMAGE_BORDER_RIGHT, IMAGE_BORDER_BOTTOM ) );
524
525   stencilActor.SetParentOrigin( ParentOrigin::CENTER );
526   stencilActor.SetAnchorPoint( AnchorPoint::CENTER );
527   stencilActor.SetDrawMode( DrawMode::STENCIL );
528
529   Dali::ShaderEffect shaderEffect = CreateAlphaDiscardEffect();
530   stencilActor.SetShaderEffect( shaderEffect );
531
532   return stencilActor;
533 }
534
535 bool DaliTableView::OnTilePressed( Actor actor, const TouchEvent& event )
536 {
537   bool consumed = false;
538
539   const TouchPoint& point = event.GetPoint( 0 );
540   if( TouchPoint::Down == point.state )
541   {
542     mPressedActor = actor;
543     consumed = true;
544   }
545
546   // A button press is only valid if the Down & Up events
547   // both occurred within the button.
548   if( ( TouchPoint::Up == point.state ) &&
549       ( mPressedActor == actor ) )
550   {
551     std::string name = actor.GetName();
552     ExampleMapConstIter iter = mExampleMap.find( name );
553
554     AccessibilityFocusManager accessibilityFocusManager = AccessibilityFocusManager::Get();
555
556     if( iter != mExampleMap.end() )
557     {
558       // ignore Example button presses when scrolling or button animating.
559       if( ( !mScrolling ) && ( !mPressedAnimation ) )
560       {
561         // do nothing, until pressed animation finished.
562         consumed = true;
563       }
564     }
565
566     if( consumed )
567     {
568       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
569       mPressedAnimation.SetEndAction( Animation::Discard );
570
571       // scale the content actor within the Tile, as to not affect the placement within the Table.
572       Actor content = actor.GetChildAt(0);
573       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.9f, 0.9f, 1.0f ), AlphaFunction::EASE_IN_OUT,
574                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
575       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
576                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
577       mPressedAnimation.Play();
578       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
579     }
580   }
581   return consumed;
582 }
583
584 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
585 {
586   mPressedAnimation.Reset();
587   if( mPressedActor )
588   {
589     std::string name = mPressedActor.GetName();
590     ExampleMapConstIter iter = mExampleMap.find( name );
591
592     if( iter == mExampleMap.end() )
593     {
594       if( name == BUTTON_QUIT )
595       {
596         // Move focus to the OK button
597         AccessibilityFocusManager accessibilityFocusManager = AccessibilityFocusManager::Get();
598
599         // Enable the group mode and wrap mode
600         accessibilityFocusManager.SetGroupMode( true );
601         accessibilityFocusManager.SetWrapMode( true );
602       }
603     }
604     else
605     {
606       const Example& example( iter->second );
607
608       std::stringstream stream;
609       stream << DALI_EXAMPLE_BIN << example.name.c_str();
610       pid_t pid = fork();
611       if( pid == 0)
612       {
613         execlp( stream.str().c_str(), example.name.c_str(), NULL );
614         DALI_ASSERT_ALWAYS(false && "exec failed!");
615       }
616     }
617     mPressedActor.Reset();
618   }
619 }
620
621 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
622 {
623   mScrolling = true;
624
625   PlayAnimation();
626 }
627
628 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
629 {
630   mScrolling = false;
631
632   // move focus to 1st item of new page
633   AccessibilityFocusManager accessibilityFocusManager = AccessibilityFocusManager::Get();
634   accessibilityFocusManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
635 }
636
637 bool DaliTableView::OnScrollTouched( Actor actor, const TouchEvent& event )
638 {
639   const TouchPoint& point = event.GetPoint( 0 );
640   if( TouchPoint::Down == point.state )
641   {
642     mPressedActor = actor;
643   }
644
645   return false;
646 }
647
648 void DaliTableView::ApplyScrollViewEffect()
649 {
650   // Remove old effect if exists.
651
652   if( mScrollViewEffect )
653   {
654     mScrollView.RemoveEffect( mScrollViewEffect );
655   }
656
657   // Just one effect for now
658   SetupInnerPageCubeEffect();
659
660   mScrollView.ApplyEffect( mScrollViewEffect );
661 }
662
663 void DaliTableView::SetupInnerPageCubeEffect()
664 {
665   const Vector2 stageSize = Stage::GetCurrent().GetSize();
666
667   Dali::Path path = Dali::Path::New();
668   Dali::Property::Array points;
669   points.Resize(3);
670   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
671   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
672   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
673   path.SetProperty( Path::Property::POINTS, points );
674
675   Dali::Property::Array controlPoints;
676   controlPoints.Resize(4);
677   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
678   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
679   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
680   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
681   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
682
683
684   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
685                                                     Vector3(-1.0f,0.0f,0.0f),
686                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
687                                                     Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
688 }
689
690 void DaliTableView::OnKeyEvent( const KeyEvent& event )
691 {
692   if( event.state == KeyEvent::Down )
693   {
694     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
695     {
696       if ( mVersionPopup && mVersionPopupShown )
697       {
698         HideVersionPopup();
699       }
700       else
701       {
702         mApplication.Quit();
703       }
704     }
705   }
706 }
707
708 void DaliTableView::SetupBackground( Actor bubbleContainer )
709 {
710   // Create distance field shape.
711   BufferImage distanceField;
712   Size imageSize( 512, 512 );
713   CreateShapeImage( CIRCLE, imageSize, distanceField );
714
715   // Add bubbles to the bubbleContainer.
716   // Note: The bubbleContainer is parented externally to this function.
717   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES, distanceField );
718 }
719
720 void DaliTableView::InitialiseBackgroundActors( Actor actor )
721 {
722   // Delete current animations
723   mBackgroundAnimations.clear();
724
725   // Create new animations
726   const Vector3 size = actor.GetTargetSize();
727
728   for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
729   {
730     Actor child = actor.GetChildAt( i );
731
732     // Calculate a random position
733     Vector3 childPos( Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.5f * BACKGROUND_SPREAD_SCALE ),
734                       Random::Range( -size.y, size.y ),
735                       Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
736
737     child.SetPosition( childPos );
738
739     // Define bubble horizontal parallax and vertical wrapping
740     Constraint animConstraint = Constraint::New < Vector3 > ( child, Actor::Property::POSITION, AnimateBubbleConstraint( childPos, Random::Range( -0.85f, 0.25f ) ) );
741     animConstraint.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
742     animConstraint.AddSource( Dali::ParentSource( Dali::Actor::Property::SIZE ) );
743     animConstraint.AddSource( Dali::LocalSource( Dali::Actor::Property::SIZE ) );
744     animConstraint.Apply();
745
746     // Kickoff animation
747     Animation animation = Animation::New( Random::Range( 40.0f, 80.0f ) );
748     animation.AnimateBy( Property( child, Actor::Property::POSITION ), Vector3( 0.0f, -1.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, BufferImage distanceField )
756 {
757   for( int i = 0; i < count; ++i )
758   {
759     float randSize = Random::Range( 10.0f, 400.0f );
760     float hue = Random::Range( 0.3f, 1.0f );
761     Vector4 randColour( hue, hue * 0.5, 0.0f, Random::Range( 0.3f, 0.6f ));
762
763     ImageActor dfActor = ImageActor::New( distanceField );
764     dfActor.SetSize( Vector2( randSize, randSize ) );
765     dfActor.SetParentOrigin( ParentOrigin::CENTER );
766
767     ShaderEffect effect = Toolkit::CreateDistanceFieldEffect();
768     dfActor.SetShaderEffect( effect );
769     dfActor.SetColor( randColour );
770     effect.SetUniform("uOutlineParams", Vector2( 0.55f, 0.00f ) );
771     effect.SetUniform("uSmoothing", 0.5f );
772     layer.Add( dfActor );
773   }
774
775   // Positioning will occur when the layer is relaid out
776   layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
777 }
778
779 void DaliTableView::CreateShapeImage( ShapeType shapeType, const Size& size, BufferImage& distanceFieldOut )
780 {
781   // this bitmap will hold the alpha map for the distance field shader
782   distanceFieldOut = BufferImage::New( size.width, size.height, Pixel::A8 );
783
784   // Generate bit pattern
785   std::vector< unsigned char > imageDataA8;
786   imageDataA8.reserve( size.width * size.height ); // A8
787
788   switch( shapeType )
789   {
790     case CIRCLE:
791       GenerateCircle( size, imageDataA8 );
792       break;
793     case SQUARE:
794       GenerateSquare( size, imageDataA8 );
795       break;
796     default:
797       break;
798   }
799
800   PixelBuffer* buffer = distanceFieldOut.GetBuffer();
801   if( buffer )
802   {
803     GenerateDistanceFieldMap( &imageDataA8[ 0 ], size, buffer, size, 8.0f, size );
804     distanceFieldOut.Update();
805   }
806 }
807
808 void DaliTableView::GenerateSquare( const Size& size, std::vector< unsigned char >& distanceFieldOut )
809 {
810   for( int h = 0; h < size.height; ++h )
811   {
812     for( int w = 0; w < size.width; ++w )
813     {
814       distanceFieldOut.push_back( 0xFF );
815     }
816   }
817 }
818
819 void DaliTableView::GenerateCircle( const Size& size, std::vector< unsigned char >& distanceFieldOut )
820 {
821   const float radius = size.width * 0.5f * size.width * 0.5f;
822   Vector2 center( size.width / 2, size.height / 2 );
823
824   for( int h = 0; h < size.height; ++h )
825   {
826     for( int w = 0; w < size.width; ++w )
827     {
828       Vector2 pos( w, h );
829       Vector2 dist = pos - center;
830
831       if( dist.x * dist.x + dist.y * dist.y > radius )
832       {
833         distanceFieldOut.push_back( 0x00 );
834       }
835       else
836       {
837         distanceFieldOut.push_back( 0xFF );
838       }
839     }
840   }
841 }
842
843 ImageActor DaliTableView::CreateLogo( std::string imagePath )
844 {
845   Image image = ResourceImage::New( imagePath );
846   ImageActor logo = ImageActor::New( image );
847
848   logo.SetAnchorPoint( AnchorPoint::CENTER );
849   logo.SetParentOrigin( ParentOrigin::CENTER );
850
851   return logo;
852 }
853
854 bool DaliTableView::PauseBackgroundAnimation()
855 {
856   PauseAnimation();
857
858   return false;
859 }
860
861 void DaliTableView::PauseAnimation()
862 {
863   if( mBackgroundAnimsPlaying )
864   {
865     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
866     {
867       Animation anim = *animIter;
868
869       anim.Stop();
870     }
871
872     mBackgroundAnimsPlaying = false;
873   }
874 }
875
876 void DaliTableView::PlayAnimation()
877 {
878   if ( !mBackgroundAnimsPlaying )
879   {
880     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
881     {
882       Animation anim = *animIter;
883
884       anim.Play();
885     }
886
887     mBackgroundAnimsPlaying = true;
888   }
889
890   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
891 }
892
893 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
894 {
895   Actor nextFocusActor = proposed;
896
897   if ( !current && !proposed  )
898   {
899     // Set the initial focus to the first tile in the current page should be focused.
900     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
901   }
902   else if( !proposed || (proposed && proposed == mScrollViewLayer) )
903   {
904     // ScrollView is being focused but nothing in the current page can be focused further
905     // in the given direction. We should work out which page to scroll to next.
906     int currentPage = mScrollView.GetCurrentPage();
907     int newPage = currentPage;
908     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
909     {
910       newPage--;
911     }
912     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
913     {
914       newPage++;
915     }
916
917     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
918     if( newPage == currentPage )
919     {
920       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
921       {
922         newPage = mTotalPages - 1;
923       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
924       {
925         newPage = 0;
926       }
927     }
928
929     // Scroll to the page in the given direction
930     mScrollView.ScrollTo(newPage);
931
932     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
933     {
934       // Work out the cell position for the last tile
935       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
936       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
937       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
938
939       // Move the focus to the last tile in the new page.
940       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
941     }
942     else
943     {
944       // Move the focus to the first tile in the new page.
945       nextFocusActor = mPages[newPage].GetChildAt(0);
946     }
947   }
948
949   return nextFocusActor;
950 }
951
952 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
953 {
954   if(activatedActor)
955   {
956     mPressedActor = activatedActor;
957
958     // Activate the current focused actor;
959     TouchEvent touchEventUp;
960     touchEventUp.points.push_back( TouchPoint ( 0, TouchPoint::Up, 0.0f, 0.0f ) );
961     OnTilePressed(mPressedActor, touchEventUp);
962   }
963 }
964
965 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
966 {
967   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
968   return true;
969 }
970
971 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
972 {
973   if ( !mVersionPopupShown )
974   {
975     if ( !mVersionPopup )
976     {
977       std::ostringstream stream;
978       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")" << std::endl << std::endl;
979       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")" << std::endl << std::endl;
980       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")";
981
982       mVersionPopup = Dali::Toolkit::Popup::New();
983       mVersionPopup.SetParentOrigin( ParentOrigin::CENTER );
984       mVersionPopup.SetAnchorPoint( AnchorPoint::CENTER );
985       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
986       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
987       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
988       mVersionPopup.SetTitle( stream.str() );
989       mVersionPopup.HideTail();
990       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
991       mVersionPopup.HiddenSignal().Connect( this, &DaliTableView::PopupHidden );
992     }
993
994     mVersionPopup.Show();
995     mVersionPopupShown = true;
996   }
997 }
998
999 void DaliTableView::HideVersionPopup()
1000 {
1001   if ( mVersionPopup )
1002   {
1003     mVersionPopup.Hide();
1004   }
1005 }
1006
1007 void DaliTableView::PopupHidden()
1008 {
1009   if ( mVersionPopup )
1010   {
1011     mVersionPopupShown = false;
1012   }
1013 }