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