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