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