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