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