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