Merge branch 'devel/master' 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
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().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           AccessibilityManager accessibilityManager = AccessibilityManager::Get();
395           accessibilityManager.SetFocusOrder( tile, ++exampleCount );
396           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
397                                                   example.title );
398           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
399           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::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 = CreateAlphaDiscardEffect();
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     AccessibilityManager accessibilityManager = AccessibilityManager::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         AccessibilityManager accessibilityManager = AccessibilityManager::Get();
602
603         // Enable the group mode and wrap mode
604         accessibilityManager.SetGroupMode( true );
605         accessibilityManager.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   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
638   accessibilityManager.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     // Force the bubbles just in front of the solid background
772     dfActor.SetSortModifier( DemoHelper::BACKGROUND_DEPTH_INDEX + 1 );
773
774     ShaderEffect effect = Toolkit::CreateDistanceFieldEffect();
775     dfActor.SetShaderEffect( effect );
776     dfActor.SetColor( randColour );
777     effect.SetUniform("uOutlineParams", Vector2( 0.55f, 0.00f ) );
778     effect.SetUniform("uSmoothing", 0.5f );
779     layer.Add( dfActor );
780   }
781
782   // Positioning will occur when the layer is relaid out
783   layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
784 }
785
786 void DaliTableView::CreateShapeImage( ShapeType shapeType, const Size& size, BufferImage& distanceFieldOut )
787 {
788   // this bitmap will hold the alpha map for the distance field shader
789   distanceFieldOut = BufferImage::New( size.width, size.height, Pixel::A8 );
790
791   // Generate bit pattern
792   std::vector< unsigned char > imageDataA8;
793   imageDataA8.reserve( size.width * size.height ); // A8
794
795   switch( shapeType )
796   {
797     case CIRCLE:
798       GenerateCircle( size, imageDataA8 );
799       break;
800     case SQUARE:
801       GenerateSquare( size, imageDataA8 );
802       break;
803     default:
804       break;
805   }
806
807   PixelBuffer* buffer = distanceFieldOut.GetBuffer();
808   if( buffer )
809   {
810     GenerateDistanceFieldMap( &imageDataA8[ 0 ], size, buffer, size, 8.0f, size );
811     distanceFieldOut.Update();
812   }
813 }
814
815 void DaliTableView::GenerateSquare( const Size& size, std::vector< unsigned char >& distanceFieldOut )
816 {
817   for( int h = 0; h < size.height; ++h )
818   {
819     for( int w = 0; w < size.width; ++w )
820     {
821       distanceFieldOut.push_back( 0xFF );
822     }
823   }
824 }
825
826 void DaliTableView::GenerateCircle( const Size& size, std::vector< unsigned char >& distanceFieldOut )
827 {
828   const float radius = size.width * 0.5f * size.width * 0.5f;
829   Vector2 center( size.width / 2, size.height / 2 );
830
831   for( int h = 0; h < size.height; ++h )
832   {
833     for( int w = 0; w < size.width; ++w )
834     {
835       Vector2 pos( w, h );
836       Vector2 dist = pos - center;
837
838       if( dist.x * dist.x + dist.y * dist.y > radius )
839       {
840         distanceFieldOut.push_back( 0x00 );
841       }
842       else
843       {
844         distanceFieldOut.push_back( 0xFF );
845       }
846     }
847   }
848 }
849
850 ImageActor DaliTableView::CreateLogo( std::string imagePath )
851 {
852   Image image = ResourceImage::New( imagePath );
853   ImageActor logo = ImageActor::New( image );
854
855   logo.SetAnchorPoint( AnchorPoint::CENTER );
856   logo.SetParentOrigin( ParentOrigin::CENTER );
857
858   return logo;
859 }
860
861 bool DaliTableView::PauseBackgroundAnimation()
862 {
863   PauseAnimation();
864
865   return false;
866 }
867
868 void DaliTableView::PauseAnimation()
869 {
870   if( mBackgroundAnimsPlaying )
871   {
872     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
873     {
874       Animation anim = *animIter;
875
876       anim.Stop();
877     }
878
879     mBackgroundAnimsPlaying = false;
880   }
881 }
882
883 void DaliTableView::PlayAnimation()
884 {
885   if ( !mBackgroundAnimsPlaying )
886   {
887     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
888     {
889       Animation anim = *animIter;
890
891       anim.Play();
892     }
893
894     mBackgroundAnimsPlaying = true;
895   }
896
897   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
898 }
899
900 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
901 {
902   Actor nextFocusActor = proposed;
903
904   if ( !current && !proposed  )
905   {
906     // Set the initial focus to the first tile in the current page should be focused.
907     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
908   }
909   else if( !proposed || (proposed && proposed == mScrollViewLayer) )
910   {
911     // ScrollView is being focused but nothing in the current page can be focused further
912     // in the given direction. We should work out which page to scroll to next.
913     int currentPage = mScrollView.GetCurrentPage();
914     int newPage = currentPage;
915     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
916     {
917       newPage--;
918     }
919     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
920     {
921       newPage++;
922     }
923
924     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
925     if( newPage == currentPage )
926     {
927       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
928       {
929         newPage = mTotalPages - 1;
930       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
931       {
932         newPage = 0;
933       }
934     }
935
936     // Scroll to the page in the given direction
937     mScrollView.ScrollTo(newPage);
938
939     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
940     {
941       // Work out the cell position for the last tile
942       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
943       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
944       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
945
946       // Move the focus to the last tile in the new page.
947       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
948     }
949     else
950     {
951       // Move the focus to the first tile in the new page.
952       nextFocusActor = mPages[newPage].GetChildAt(0);
953     }
954   }
955
956   return nextFocusActor;
957 }
958
959 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
960 {
961   if(activatedActor)
962   {
963     mPressedActor = activatedActor;
964
965     // Activate the current focused actor;
966     TouchEvent touchEventUp;
967     touchEventUp.points.push_back( TouchPoint ( 0, TouchPoint::Up, 0.0f, 0.0f ) );
968     OnTilePressed(mPressedActor, touchEventUp);
969   }
970 }
971
972 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
973 {
974   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
975   return true;
976 }
977
978 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
979 {
980   if ( !mVersionPopupShown )
981   {
982     if ( !mVersionPopup )
983     {
984       std::ostringstream stream;
985       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")" << std::endl << std::endl;
986       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")" << std::endl << std::endl;
987       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")";
988
989       mVersionPopup = Dali::Toolkit::Popup::New();
990       mVersionPopup.SetParentOrigin( ParentOrigin::CENTER );
991       mVersionPopup.SetAnchorPoint( AnchorPoint::CENTER );
992       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
993       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
994       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
995       mVersionPopup.SetTitle( stream.str() );
996       mVersionPopup.HideTail();
997       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
998       mVersionPopup.HiddenSignal().Connect( this, &DaliTableView::PopupHidden );
999     }
1000
1001     mVersionPopup.Show();
1002     mVersionPopupShown = true;
1003   }
1004 }
1005
1006 void DaliTableView::HideVersionPopup()
1007 {
1008   if ( mVersionPopup )
1009   {
1010     mVersionPopup.Hide();
1011   }
1012 }
1013
1014 void DaliTableView::PopupHidden()
1015 {
1016   if ( mVersionPopup )
1017   {
1018     mVersionPopupShown = false;
1019   }
1020 }