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