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