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