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