New Popup implementation
[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 ImageView CreateBackground( std::string imagePath )
92 {
93   Image image = ResourceImage::New( imagePath );
94   ImageView background = ImageView::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   mPressedAnimation(),
157   mScrollViewLayer(),
158   mScrollView(),
159   mScrollViewEffect(),
160   mScrollRulerX(),
161   mScrollRulerY(),
162   mButtons(),
163   mPressedActor(),
164   mAnimationTimer(),
165   mLogoTapDetector(),
166   mVersionPopup(),
167   mButtonsPageRelativeSize(),
168   mPages(),
169   mTableViewImages(),
170   mBackgroundActors(),
171   mBackgroundAnimations(),
172   mExampleList(),
173   mExampleMap(),
174   mBackgroundImagePath( DEFAULT_BACKGROUND_IMAGE_PATH ),
175   mTotalPages(),
176   mScrolling( false ),
177   mSortAlphabetically( false ),
178   mBackgroundAnimsPlaying( false )
179 {
180   application.InitSignal().Connect( this, &DaliTableView::Initialize );
181 }
182
183 DaliTableView::~DaliTableView()
184 {
185 }
186
187 void DaliTableView::AddExample( Example example )
188 {
189   mExampleList.push_back( example );
190   mExampleMap[ example.name ] = example;
191 }
192
193 void DaliTableView::SetBackgroundPath( std::string imagePath )
194 {
195   mBackgroundImagePath = imagePath;
196 }
197
198 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
199 {
200   mSortAlphabetically = sortAlphabetically;
201 }
202
203 void DaliTableView::Initialize( Application& application )
204 {
205   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
206
207   const Vector2 stageSize = Stage::GetCurrent().GetSize();
208
209   // Background
210   ImageView background = CreateBackground( mBackgroundImagePath );
211   Stage::GetCurrent().Add( background );
212
213   // Render entire content as overlays, as is all on same 2D plane.
214   mRootActor = TableView::New( 4, 1 );
215   mRootActor.SetAnchorPoint( AnchorPoint::CENTER );
216   mRootActor.SetParentOrigin( ParentOrigin::CENTER );
217   mRootActor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
218   Stage::GetCurrent().Add( mRootActor );
219
220   // Toolbar at top
221   Dali::Toolkit::ToolBar toolbar;
222   Dali::Layer toolBarLayer = DemoHelper::CreateToolbar(toolbar,
223                                                        DEFAULT_TOOLBAR_IMAGE_PATH,
224                                                        DEFAULT_TOOLBAR_TEXT,
225                                                        DemoHelper::DEFAULT_VIEW_STYLE);
226
227   mRootActor.AddChild( toolBarLayer, TableView::CellPosition( 0, 0 ) );
228   mRootActor.SetFitHeight( 0 );
229
230   // Add logo
231   ImageView logo = CreateLogo( LOGO_PATH );
232   logo.SetName( "LOGO_IMAGE" );
233   logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
234   const float paddingHeight = ( ( 1.f-TABLE_RELATIVE_SIZE.y ) * stageSize.y );
235   const float logoMargin = paddingHeight * LOGO_MARGIN_RATIO;
236
237   // Show version in a popup when log is tapped
238   mLogoTapDetector = TapGestureDetector::New();
239   mLogoTapDetector.Attach( logo );
240   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
241
242   const float bottomMargin = paddingHeight * BOTTOM_PADDING_RATIO;
243
244   Alignment alignment = Alignment::New();
245   alignment.SetName( "LOGO_ALIGNMENT" );
246   alignment.Add( logo );
247   alignment.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
248   alignment.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
249   Actor alignmentActor = alignment;
250   alignmentActor.SetPadding( Padding( 0.0f, 0.0f, logoMargin, logoMargin ));
251   mRootActor.AddChild( alignment, TableView::CellPosition( 1, 0 ) );
252   mRootActor.SetFitHeight( 1 );
253
254   // scrollview occupying the majority of the screen
255   mScrollView = ScrollView::New();
256
257   mScrollView.SetAnchorPoint( AnchorPoint::CENTER );
258   mScrollView.SetParentOrigin( ParentOrigin::CENTER );
259   mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
260   const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * stageSize.width;
261   mScrollView.SetPadding( Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
262
263   mScrollView.SetAxisAutoLock( true );
264   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
265   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
266   mScrollView.TouchedSignal().Connect( this, &DaliTableView::OnScrollTouched );
267
268   mScrollViewLayer = Layer::New();
269
270   // Disable the depth test for performance
271   mScrollViewLayer.SetDepthTestDisabled( true );
272   mScrollViewLayer.SetAnchorPoint( AnchorPoint::CENTER );
273   mScrollViewLayer.SetParentOrigin( ParentOrigin::CENTER );
274   mScrollViewLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
275
276   // Create solid background colour.
277   ImageActor backgroundColourActor = Dali::Toolkit::CreateSolidColorActor( BACKGROUND_COLOR );
278   backgroundColourActor.SetAnchorPoint( AnchorPoint::CENTER );
279   backgroundColourActor.SetParentOrigin( ParentOrigin::CENTER );
280   backgroundColourActor.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
281   backgroundColourActor.SetSizeModeFactor( Vector3( 1.0f, 1.5f, 1.0f ) );
282
283   // Force the filled background right to the back
284   backgroundColourActor.SetSortModifier( DemoHelper::BACKGROUND_DEPTH_INDEX );
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   unsigned int degrees = 0;
323   Rotate( degrees );
324
325   //orientation.ChangedSignal().Connect( this, &DaliTableView::OrientationChanged );
326
327   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
328
329   // Background animation
330   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
331   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
332   mAnimationTimer.Start();
333   mBackgroundAnimsPlaying = true;
334
335   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
336   KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
337   AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
338 }
339
340 void DaliTableView::ApplyCubeEffectToPages()
341 {
342   ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
343   unsigned int pageCount(0);
344   for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
345   {
346     Actor page = *pageIter;
347     effect.ApplyToPage( page, pageCount++ );
348   }
349 }
350
351 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
352 {
353
354 }
355
356 void DaliTableView::Populate()
357 {
358   const Vector2 stageSize = Stage::GetCurrent().GetSize();
359
360   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
361
362   // Populate ScrollView.
363   if( mExampleList.size() > 0 )
364   {
365     if( mSortAlphabetically )
366     {
367       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
368     }
369
370     unsigned int exampleCount = 0;
371     ExampleListConstIter iter = mExampleList.begin();
372
373     for( int t = 0; t < mTotalPages; t++ )
374     {
375       // Create Table. (contains up to 9 Examples)
376       TableView page = TableView::New( 3, 3 );
377       page.SetAnchorPoint( AnchorPoint::CENTER );
378       page.SetParentOrigin( ParentOrigin::CENTER );
379       page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
380       mScrollView.Add( page );
381
382       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
383       const float margin = 2.0f;
384       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
385
386       for(int row = 0; row < ROWS_PER_PAGE; row++)
387       {
388         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
389         {
390           const Example& example = ( *iter );
391
392           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), true );
393           AccessibilityManager accessibilityManager = AccessibilityManager::Get();
394           accessibilityManager.SetFocusOrder( tile, ++exampleCount );
395           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
396                                                   example.title );
397           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
398           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT,
399                                                   "You can run this example" );
400
401           tile.SetPadding( Padding( margin, margin, margin, margin ) );
402
403           page.AddChild( tile, TableView::CellPosition( row, column ) );
404
405           iter++;
406
407           if( iter == mExampleList.end() )
408           {
409             break;
410           }
411         }
412
413         if( iter == mExampleList.end() )
414         {
415           break;
416         }
417       }
418
419       // Set tableview position
420       Vector3 pagePos( stageSize.width * TABLE_RELATIVE_SIZE.x * t, 0.0f, 0.0f );
421       page.SetPosition( pagePos );
422
423       mPages.push_back( page );
424
425       if( iter == mExampleList.end() )
426       {
427         break;
428       }
429     }
430   }
431
432   // Update Ruler info.
433   mScrollRulerX = new FixedRuler( stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f );
434   mScrollRulerY = new DefaultRuler();
435   mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
436   mScrollRulerY->Disable();
437   mScrollView.SetRulerX( mScrollRulerX );
438   mScrollView.SetRulerY( mScrollRulerY );
439 }
440
441 void DaliTableView::OrientationChanged( Orientation orientation )
442 {
443   // TODO: Implement if orientation change required
444 }
445
446 void DaliTableView::Rotate( unsigned int degrees )
447 {
448   // Resize the root actor
449   Vector2 stageSize = Stage::GetCurrent().GetSize();
450   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
451
452   if( degrees == 90 || degrees == 270 )
453   {
454     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
455   }
456
457   if( mRotateAnimation )
458   {
459     mRotateAnimation.Stop();
460     mRotateAnimation.Clear();
461   }
462
463   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
464   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
465   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
466   mRotateAnimation.Play();
467 }
468
469 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, bool addBackground )
470 {
471   Actor content = Actor::New();
472   content.SetName( name );
473   content.SetAnchorPoint( AnchorPoint::CENTER );
474   content.SetParentOrigin( ParentOrigin::CENTER );
475   content.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
476   content.SetSizeModeFactor( sizeMultiplier );
477
478   // create background image
479   if( addBackground )
480   {
481     Image bg = ResourceImage::New( TILE_BACKGROUND );
482     ImageActor image = ImageActor::New( bg );
483     image.SetAnchorPoint( AnchorPoint::CENTER );
484     image.SetParentOrigin( ParentOrigin::CENTER );
485     // make the image 100% of tile
486     image.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
487
488     // move image back to get text appear in front
489     image.SetZ( -1 );
490     image.SetStyle( ImageActor::STYLE_NINE_PATCH );
491     image.SetNinePatchBorder( Vector4( IMAGE_BORDER_LEFT, IMAGE_BORDER_TOP, IMAGE_BORDER_RIGHT, IMAGE_BORDER_BOTTOM ) );
492     content.Add( image );
493
494     // Add stencil
495     ImageActor stencil = NewStencilImage();
496     stencil.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
497     image.Add( stencil );
498   }
499
500   TextLabel label = TextLabel::New();
501   label.SetAnchorPoint( AnchorPoint::TOP_LEFT );
502   label.SetProperty( Control::Property::STYLE_NAME, "launcherlabel" );
503   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
504   label.SetProperty( TextLabel::Property::TEXT, title );
505   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
506   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
507   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
508   content.Add( label );
509
510   // Set the tile to be keyboard focusable
511   content.SetKeyboardFocusable(true);
512
513   // connect to the touch events
514   content.TouchedSignal().Connect( this, &DaliTableView::OnTilePressed );
515   content.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
516
517   return content;
518 }
519
520 ImageActor DaliTableView::NewStencilImage()
521 {
522   Image alpha = ResourceImage::New( TILE_BACKGROUND_ALPHA );
523
524   ImageActor stencilActor = ImageActor::New( alpha );
525   stencilActor.SetStyle( ImageActor::STYLE_NINE_PATCH );
526   stencilActor.SetNinePatchBorder( Vector4( IMAGE_BORDER_LEFT, IMAGE_BORDER_TOP, IMAGE_BORDER_RIGHT, IMAGE_BORDER_BOTTOM ) );
527
528   stencilActor.SetParentOrigin( ParentOrigin::CENTER );
529   stencilActor.SetAnchorPoint( AnchorPoint::CENTER );
530   stencilActor.SetDrawMode( DrawMode::STENCIL );
531
532   Dali::ShaderEffect shaderEffect = CreateAlphaDiscardEffect();
533   stencilActor.SetShaderEffect( shaderEffect );
534
535   return stencilActor;
536 }
537
538 bool DaliTableView::OnTilePressed( Actor actor, const TouchEvent& event )
539 {
540   bool consumed = false;
541
542   const TouchPoint& point = event.GetPoint( 0 );
543   if( TouchPoint::Down == point.state )
544   {
545     mPressedActor = actor;
546     consumed = true;
547   }
548
549   // A button press is only valid if the Down & Up events
550   // both occurred within the button.
551   if( ( TouchPoint::Up == point.state ) &&
552       ( mPressedActor == actor ) )
553   {
554     std::string name = actor.GetName();
555     ExampleMapConstIter iter = mExampleMap.find( name );
556
557     AccessibilityManager accessibilityManager = AccessibilityManager::Get();
558
559     if( iter != mExampleMap.end() )
560     {
561       // ignore Example button presses when scrolling or button animating.
562       if( ( !mScrolling ) && ( !mPressedAnimation ) )
563       {
564         // do nothing, until pressed animation finished.
565         consumed = true;
566       }
567     }
568
569     if( consumed )
570     {
571       mPressedAnimation = Animation::New( BUTTON_PRESS_ANIMATION_TIME );
572       mPressedAnimation.SetEndAction( Animation::Discard );
573
574       // scale the content actor within the Tile, as to not affect the placement within the Table.
575       Actor content = actor.GetChildAt(0);
576       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3( 0.9f, 0.9f, 1.0f ), AlphaFunction::EASE_IN_OUT,
577                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
578       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
579                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
580       mPressedAnimation.Play();
581       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
582     }
583   }
584   return consumed;
585 }
586
587 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
588 {
589   mPressedAnimation.Reset();
590   if( mPressedActor )
591   {
592     std::string name = mPressedActor.GetName();
593     ExampleMapConstIter iter = mExampleMap.find( name );
594
595     if( iter == mExampleMap.end() )
596     {
597       if( name == BUTTON_QUIT )
598       {
599         // Move focus to the OK button
600         AccessibilityManager accessibilityManager = AccessibilityManager::Get();
601
602         // Enable the group mode and wrap mode
603         accessibilityManager.SetGroupMode( true );
604         accessibilityManager.SetWrapMode( true );
605       }
606     }
607     else
608     {
609       const Example& example( iter->second );
610
611       std::stringstream stream;
612       stream << DALI_EXAMPLE_BIN << example.name.c_str();
613       pid_t pid = fork();
614       if( pid == 0)
615       {
616         execlp( stream.str().c_str(), example.name.c_str(), NULL );
617         DALI_ASSERT_ALWAYS(false && "exec failed!");
618       }
619     }
620     mPressedActor.Reset();
621   }
622 }
623
624 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
625 {
626   mScrolling = true;
627
628   PlayAnimation();
629 }
630
631 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
632 {
633   mScrolling = false;
634
635   // move focus to 1st item of new page
636   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
637   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
638 }
639
640 bool DaliTableView::OnScrollTouched( Actor actor, const TouchEvent& event )
641 {
642   const TouchPoint& point = event.GetPoint( 0 );
643   if( TouchPoint::Down == point.state )
644   {
645     mPressedActor = actor;
646   }
647
648   return false;
649 }
650
651 void DaliTableView::ApplyScrollViewEffect()
652 {
653   // Remove old effect if exists.
654
655   if( mScrollViewEffect )
656   {
657     mScrollView.RemoveEffect( mScrollViewEffect );
658   }
659
660   // Just one effect for now
661   SetupInnerPageCubeEffect();
662
663   mScrollView.ApplyEffect( mScrollViewEffect );
664 }
665
666 void DaliTableView::SetupInnerPageCubeEffect()
667 {
668   const Vector2 stageSize = Stage::GetCurrent().GetSize();
669
670   Dali::Path path = Dali::Path::New();
671   Dali::Property::Array points;
672   points.Resize(3);
673   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
674   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
675   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
676   path.SetProperty( Path::Property::POINTS, points );
677
678   Dali::Property::Array controlPoints;
679   controlPoints.Resize(4);
680   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
681   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
682   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
683   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
684   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
685
686
687   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
688                                                     Vector3(-1.0f,0.0f,0.0f),
689                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
690                                                     Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
691 }
692
693 void DaliTableView::OnKeyEvent( const KeyEvent& event )
694 {
695   if( event.state == KeyEvent::Down )
696   {
697     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
698     {
699       // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
700       // Otherwise quit.
701       if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
702       {
703         mVersionPopup.SetDisplayState( Popup::HIDDEN );
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 ImageView DaliTableView::CreateLogo( std::string imagePath )
852 {
853   Image image = ResourceImage::New( imagePath );
854   ImageView logo = ImageView::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   // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
982   if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
983   {
984     if ( !mVersionPopup )
985     {
986       std::ostringstream stream;
987       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
988       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
989       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
990
991       mVersionPopup = Dali::Toolkit::Popup::New();
992
993       Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
994       titleActor.SetName( "title-actor" );
995       titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
996
997       Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
998       contentActor.SetName( "content-actor" );
999       contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
1000       contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
1001       contentActor.SetPadding( Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
1002
1003       mVersionPopup.SetTitle( titleActor );
1004       mVersionPopup.SetContent( contentActor );
1005
1006       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
1007       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
1008       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
1009
1010       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
1011       Stage::GetCurrent().Add( mVersionPopup );
1012     }
1013
1014     mVersionPopup.SetDisplayState( Popup::SHOWN );
1015   }
1016 }
1017
1018 void DaliTableView::HideVersionPopup()
1019 {
1020   // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
1021   if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
1022   {
1023     mVersionPopup.SetDisplayState( Popup::HIDDEN );
1024   }
1025 }