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