[3.0] Fixed dali-demo keyboard focus and updated appearance
[platform/core/uifw/dali-demo.git] / demo / dali-table-view.cpp
1 /*
2  * Copyright (c) 2016 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 #include <dali-toolkit/dali-toolkit.h>
29 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
30
31 // INTERNAL INCLUDES
32 #include "shared/view.h"
33 #include "shared/utility.h"
34
35 using namespace Dali;
36 using namespace Dali::Toolkit;
37
38 ///////////////////////////////////////////////////////////////////////////////
39
40 namespace
41 {
42
43 const std::string LOGO_PATH( DEMO_IMAGE_DIR "Logo-for-demo.png" );
44 const std::string TILE_BACKGROUND(DEMO_IMAGE_DIR "item-background.9.png");
45 const std::string TILE_BACKGROUND_ALPHA( DEMO_IMAGE_DIR "demo-tile-texture.9.png" );
46 const std::string TILE_FOCUS( DEMO_IMAGE_DIR "tile-focus.9.png" );
47
48 // Keyboard focus constants.
49 const float KEYBOARD_FOCUS_ANIMATION_DURATION = 1.0f;           ///< The total duration of the keyboard focus animation
50 const float KEYBOARD_FOCUS_START_SCALE = 1.05f;                 ///< The starting scale of the focus highlight
51 const float KEYBOARD_FOCUS_END_SCALE = 1.18f;                   ///< The end scale of the focus highlight
52 const float KEYBOARD_FOCUS_END_ALPHA = 0.7f;                    ///< The end alpha of the focus highlight
53 const float KEYBOARD_FOCUS_INITIAL_FADE_PERCENTAGE = 0.16f;     ///< The duration of the initial fade (from translucent to the end-alpha) as a percentage of the overal animation duration.
54
55 const float TILE_LABEL_PADDING = 8.0f;                          ///< Border between edge of tile and the example text
56 const float BUTTON_PRESS_ANIMATION_TIME = 0.35f;                ///< Time to perform button scale effect.
57 const float ROTATE_ANIMATION_TIME = 0.5f;                       ///< Time to perform rotate effect.
58 const int MAX_PAGES = 256;                                      ///< Maximum pages (arbitrary safety limit)
59 const int EXAMPLES_PER_ROW = 3;
60 const int ROWS_PER_PAGE = 3;
61 const int EXAMPLES_PER_PAGE = EXAMPLES_PER_ROW * ROWS_PER_PAGE;
62 const float LOGO_MARGIN_RATIO = 0.1f / 0.3f;
63 const float BOTTOM_PADDING_RATIO = 0.4f / 0.9f;
64 const Vector3 SCROLLVIEW_RELATIVE_SIZE(0.9f, 1.0f, 0.8f );      ///< ScrollView's relative size to its parent
65 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.
66 const float STENCIL_RELATIVE_SIZE = 1.0f;
67
68 const float EFFECT_SNAP_DURATION = 0.66f;                       ///< Scroll Snap Duration for Effects
69 const float EFFECT_FLICK_DURATION = 0.5f;                       ///< Scroll Flick Duration for Effects
70 const Vector3 ANGLE_CUBE_PAGE_ROTATE(Math::PI * 0.5f, Math::PI * 0.5f, 0.0f);
71 const Vector4 TILE_COLOR( 0.4f, 0.6f, 0.9f, 0.6f );
72
73 const Vector4 BUBBLE_COLOR[] =
74 {
75   Vector4( 0.3255f, 0.3412f, 0.6353f, 0.32f ),
76   Vector4( 0.3647f, 0.7569f, 0.8157f, 0.32f ),
77   Vector4( 0.3804f, 0.7412f, 0.6510f, 0.32f ),
78   Vector4( 1.f, 1.f, 1.f, 0.13f )
79 };
80 const int NUMBER_OF_BUBBLE_COLOR( sizeof(BUBBLE_COLOR) / sizeof(BUBBLE_COLOR[0]) );
81
82 const int NUM_BACKGROUND_IMAGES = 18;
83 const float BACKGROUND_SWIPE_SCALE = 0.025f;
84 const float BACKGROUND_SPREAD_SCALE = 1.5f;
85 const float SCALE_MOD = 1000.0f * Math::PI * 2.0f;
86 const float SCALE_SPEED = 10.0f;
87 const float SCALE_SPEED_SIN = 0.1f;
88
89 const unsigned int BACKGROUND_ANIMATION_DURATION = 15000; // 15 secs
90
91 const Vector4 BACKGROUND_COLOR( 0.3569f, 0.5451f, 0.7294f, 1.0f );
92
93 const float BUBBLE_MIN_Z = -1.0;
94 const float BUBBLE_MAX_Z = 0.0f;
95
96 // This shader takes a texture.
97 // An alpha discard is performed.
98 // The shader uses the tiles position within the scroll-view page and the scroll-views rotation position to create a parallax effect.
99 const char* FRAGMENT_SHADER_TEXTURED = DALI_COMPOSE_SHADER(
100   varying mediump vec2  vTexCoord;
101   varying mediump vec3  vIllumination;
102   uniform lowp    vec4  uColor;
103   uniform sampler2D     sTexture;
104   uniform mediump vec3  uCustomPosition;
105
106   void main()
107   {
108     if( texture2D( sTexture, vTexCoord ).a <= 0.0001 )
109     {
110       discard;
111     }
112
113     mediump vec2 wrapTexCoord = vec2( ( vTexCoord.x / 4.0 ) + ( uCustomPosition.x / 4.0 ) + ( uCustomPosition.z / 2.0 ), vTexCoord.y / 4.0 );
114     mediump vec4 color = texture2D( sTexture, wrapTexCoord );
115     mediump float positionWeight = ( uCustomPosition.y + 0.3 ) * color.r * 2.0;
116
117     gl_FragColor = vec4( positionWeight, positionWeight, positionWeight, 0.9 ) * uColor + vec4( uColor.xyz, 0.0 );
118   }
119 );
120
121 /**
122  * Creates the background image
123  */
124 Control CreateBackground( std::string stylename )
125 {
126   Control background = Control::New();
127   Stage::GetCurrent().Add( background );
128   background.SetStyleName( stylename );
129   background.SetName( "BACKGROUND" );
130   background.SetAnchorPoint( AnchorPoint::CENTER );
131   background.SetParentOrigin( ParentOrigin::CENTER );
132   background.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
133   return background;
134 }
135
136 /**
137  * Constraint to return a position for a bubble based on the scroll value and vertical wrapping
138  */
139 struct AnimateBubbleConstraint
140 {
141 public:
142   AnimateBubbleConstraint( const Vector3& initialPos, float scale )
143   : mInitialX( initialPos.x ),
144     mScale( scale )
145   {
146   }
147
148   void operator()( Vector3& position, const PropertyInputContainer& inputs )
149   {
150     const Vector3& parentSize = inputs[1]->GetVector3();
151     const Vector3& childSize = inputs[2]->GetVector3();
152
153     // Wrap bubbles vertically.
154     float range = parentSize.y + childSize.y;
155     // This performs a float mod (we don't use fmod as we want the arithmetic modulus as opposed to the remainder).
156     position.y -= range * ( floor( position.y / range ) + 0.5f );
157
158     // Bubbles X position moves parallax to horizontal
159     // panning by a scale factor unique to each bubble.
160     position.x = mInitialX + ( inputs[0]->GetVector2().x * mScale );
161   }
162
163 private:
164   float mInitialX;
165   float mScale;
166 };
167
168 /**
169  * Constraint to precalculate values from the scroll-view
170  * and tile positions to pass to the tile shader.
171  */
172 struct TileShaderPositionConstraint
173 {
174   TileShaderPositionConstraint( float pageWidth, float tileXOffset )
175   : mPageWidth( pageWidth ),
176     mTileXOffset( tileXOffset )
177   {
178   }
179
180   void operator()( Vector3& position, const PropertyInputContainer& inputs )
181   {
182     // Set up position.x as the tiles X offset (0.0 -> 1.0).
183     position.x = mTileXOffset;
184     // Set up position.z as the linear scroll-view X offset (0.0 -> 1.0).
185     position.z = 1.0f * ( -fmod( inputs[0]->GetVector2().x, mPageWidth ) / mPageWidth );
186     // Set up position.y as a rectified version of the scroll-views X offset.
187     // IE. instead of 0.0 -> 1.0, it moves between 0.0 -> 0.5 -> 0.0 within the same span.
188     if( position.z > 0.5f )
189     {
190       position.y = 1.0f - position.z;
191     }
192     else
193     {
194       position.y = position.z;
195     }
196   }
197
198 private:
199   float mPageWidth;
200   float mTileXOffset;
201 };
202
203 bool CompareByTitle( const Example& lhs, const Example& rhs )
204 {
205   return lhs.title < rhs.title;
206 }
207
208 } // namespace
209
210 DaliTableView::DaliTableView( Application& application )
211 : mApplication( application ),
212   mRootActor(),
213   mRotateAnimation(),
214   mPressedAnimation(),
215   mScrollView(),
216   mScrollViewEffect(),
217   mScrollRulerX(),
218   mScrollRulerY(),
219   mPressedActor(),
220   mAnimationTimer(),
221   mLogoTapDetector(),
222   mVersionPopup(),
223   mPages(),
224   mBackgroundAnimations(),
225   mExampleList(),
226   mTotalPages(),
227   mScrolling( false ),
228   mSortAlphabetically( false ),
229   mBackgroundAnimsPlaying( false )
230 {
231   application.InitSignal().Connect( this, &DaliTableView::Initialize );
232 }
233
234 DaliTableView::~DaliTableView()
235 {
236 }
237
238 void DaliTableView::AddExample( Example example )
239 {
240   mExampleList.push_back( example );
241 }
242
243 void DaliTableView::SortAlphabetically( bool sortAlphabetically )
244 {
245   mSortAlphabetically = sortAlphabetically;
246 }
247
248 void DaliTableView::Initialize( Application& application )
249 {
250   Stage::GetCurrent().KeyEventSignal().Connect( this, &DaliTableView::OnKeyEvent );
251   const Vector2 stageSize = Stage::GetCurrent().GetSize();
252
253   // Background
254   mRootActor = CreateBackground( "LauncherBackground" );
255   Stage::GetCurrent().Add( mRootActor );
256
257   // Add logo
258   ImageView logo = CreateLogo( LOGO_PATH );
259   logo.SetName( "LOGO_IMAGE" );
260   logo.SetAnchorPoint( AnchorPoint::TOP_CENTER );
261   logo.SetParentOrigin( Vector3( 0.5f, 0.1f, 0.5f ) );
262   logo.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::ALL_DIMENSIONS );
263
264   // Show version in a popup when log is tapped
265   mLogoTapDetector = TapGestureDetector::New();
266   mLogoTapDetector.Attach( logo );
267   mLogoTapDetector.DetectedSignal().Connect( this, &DaliTableView::OnLogoTapped );
268
269   // Scrollview occupying the majority of the screen
270   mScrollView = ScrollView::New();
271   mScrollView.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
272   mScrollView.SetParentOrigin( Vector3( 0.5f, 1.0f - 0.05f, 0.5f ) );
273   mScrollView.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
274   mScrollView.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::HEIGHT );
275   mScrollView.SetSizeModeFactor( Vector3( 0.0f, 0.6f, 0.0f ) );
276
277   const float buttonsPageMargin = ( 1.0f - TABLE_RELATIVE_SIZE.x ) * 0.5f * stageSize.width;
278   mScrollView.SetPadding( Padding( buttonsPageMargin, buttonsPageMargin, 0.0f, 0.0f ) );
279
280   mScrollView.SetAxisAutoLock( true );
281   mScrollView.ScrollCompletedSignal().Connect( this, &DaliTableView::OnScrollComplete );
282   mScrollView.ScrollStartedSignal().Connect( this, &DaliTableView::OnScrollStart );
283   mScrollView.TouchSignal().Connect( this, &DaliTableView::OnScrollTouched );
284
285   mPageWidth = stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f;
286
287   // Populate background and bubbles - needs to be scrollViewLayer so scroll ends show
288   Actor bubbleContainer = Actor::New();
289   bubbleContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
290   bubbleContainer.SetAnchorPoint( AnchorPoint::CENTER );
291   bubbleContainer.SetParentOrigin( ParentOrigin::CENTER );
292   SetupBackground( bubbleContainer );
293
294   mRootActor.Add( logo );
295   // We use depth index to bring the logo above the bubbles (as an alternative to creating actors).
296   logo.GetRendererAt( 0 ).SetProperty( Renderer::Property::DEPTH_INDEX, 30000 );
297   mRootActor.Add( bubbleContainer );
298   mRootActor.Add( mScrollView );
299
300   // Add scroll view effect and setup constraints on pages
301   ApplyScrollViewEffect();
302
303   // Add pages and tiles
304   Populate();
305
306   // Remove constraints for inner cube effect
307   ApplyCubeEffectToPages();
308
309   Dali::Window winHandle = application.GetWindow();
310   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT );
311   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE );
312   winHandle.AddAvailableOrientation( Dali::Window::PORTRAIT_INVERSE );
313   winHandle.RemoveAvailableOrientation( Dali::Window::LANDSCAPE_INVERSE );
314
315   // Set initial orientation
316   unsigned int degrees = 0;
317   Rotate( degrees );
318
319   winHandle.ShowIndicator( Dali::Window::INVISIBLE );
320
321   // Background animation
322   mAnimationTimer = Timer::New( BACKGROUND_ANIMATION_DURATION );
323   mAnimationTimer.TickSignal().Connect( this, &DaliTableView::PauseBackgroundAnimation );
324   mAnimationTimer.Start();
325   mBackgroundAnimsPlaying = true;
326
327   KeyboardFocusManager::Get().PreFocusChangeSignal().Connect( this, &DaliTableView::OnKeyboardPreFocusChange );
328   KeyboardFocusManager::Get().FocusedActorEnterKeySignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
329   AccessibilityManager::Get().FocusedActorActivatedSignal().Connect( this, &DaliTableView::OnFocusedActorActivated );
330
331   mFocusContainer = ImageView::New( TILE_FOCUS );
332   mFocusContainer.SetParentOrigin( ParentOrigin::CENTER );
333   mFocusContainer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
334   mFocusContainer.SetInheritScale( false );
335   mFocusContainer.SetColorMode( USE_OWN_COLOR );
336   mFocusContainer.SetName( "focusActor" );
337   mFocusContainer.OnStageSignal().Connect( this, &DaliTableView::OnStageConnect );
338
339   mFocusInner = ImageView::New( TILE_FOCUS );
340   mFocusInner.SetParentOrigin( ParentOrigin::CENTER );
341   mFocusInner.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
342   mFocusInner.SetInheritScale( false );
343   mFocusInner.SetColorMode( USE_OWN_COLOR );
344   mFocusInner.SetName( "focusActor" );
345   mFocusInner.OnStageSignal().Connect( this, &DaliTableView::OnStageConnect );
346   mFocusContainer.Add( mFocusInner );
347
348   // Setup the keyboard focus highlight.
349   Vector3 startScale( KEYBOARD_FOCUS_START_SCALE, KEYBOARD_FOCUS_START_SCALE, KEYBOARD_FOCUS_START_SCALE );
350   Vector3 endScale( KEYBOARD_FOCUS_END_SCALE, KEYBOARD_FOCUS_END_SCALE, KEYBOARD_FOCUS_END_SCALE );
351   mFocusAnimation = Animation::New( KEYBOARD_FOCUS_ANIMATION_DURATION );
352   mFocusAnimationInner = Animation::New( KEYBOARD_FOCUS_ANIMATION_DURATION );
353
354   mFocusContainer.SetScale( startScale );
355   mFocusInner.SetScale( startScale );
356   mFocusContainer.SetOpacity( 0.0f );
357   mFocusInner.SetOpacity( 0.0f );
358   const float initialFadeDuration = KEYBOARD_FOCUS_ANIMATION_DURATION * KEYBOARD_FOCUS_INITIAL_FADE_PERCENTAGE;
359
360   mFocusAnimation.AnimateTo( Property( mFocusContainer, Actor::Property::COLOR_ALPHA ), KEYBOARD_FOCUS_END_ALPHA, AlphaFunction::LINEAR, TimePeriod( 0.0f, initialFadeDuration ) );
361   mFocusAnimation.AnimateTo( Property( mFocusContainer, Actor::Property::SCALE ), endScale, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
362   mFocusAnimation.AnimateTo( Property( mFocusContainer, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
363
364   mFocusAnimationInner.AnimateTo( Property( mFocusInner, Actor::Property::COLOR_ALPHA ), KEYBOARD_FOCUS_END_ALPHA, AlphaFunction::LINEAR, TimePeriod( 0.0f, initialFadeDuration ) );
365   mFocusAnimationInner.AnimateTo( Property( mFocusInner, Actor::Property::SCALE ), endScale, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
366   mFocusAnimationInner.AnimateTo( Property( mFocusInner, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::LINEAR, TimePeriod( initialFadeDuration, KEYBOARD_FOCUS_ANIMATION_DURATION - initialFadeDuration ) );
367
368   // Play the animation on the 1st glow object.
369   mFocusAnimation.SetLooping( true );
370   mFocusAnimation.Play();
371   // Stagger the animation on the 2st glow object half way through.
372   mFocusAnimationInner.SetLooping( true );
373   mFocusAnimationInner.PlayFrom( KEYBOARD_FOCUS_ANIMATION_DURATION / 2.0f );
374
375   KeyboardFocusManager::Get().SetFocusIndicatorActor( mFocusContainer );
376 }
377
378 void DaliTableView::OnStageConnect( Dali::Actor actor )
379 {
380   // If this is one of the keyboard focus actors, place it behind the object it is focusing.
381   if( actor.GetName() == "focusActor" )
382   {
383     actor.GetRendererAt( 0 ).SetProperty( Dali::Renderer::Property::DEPTH_INDEX, -40000 );
384   }
385 }
386
387 void DaliTableView::ApplyCubeEffectToPages()
388 {
389   ScrollViewPagePathEffect effect = ScrollViewPagePathEffect::DownCast( mScrollViewEffect );
390   unsigned int pageCount(0);
391   for( std::vector< Actor >::iterator pageIter = mPages.begin(); pageIter != mPages.end(); ++pageIter )
392   {
393     Actor page = *pageIter;
394     effect.ApplyToPage( page, pageCount++ );
395   }
396 }
397
398 void DaliTableView::OnButtonsPageRelayout( const Dali::Actor& actor )
399 {
400 }
401
402 void DaliTableView::Populate()
403 {
404   const Vector2 stageSize = Stage::GetCurrent().GetSize();
405
406   mTotalPages = ( mExampleList.size() + EXAMPLES_PER_PAGE - 1 ) / EXAMPLES_PER_PAGE;
407
408   // Populate ScrollView.
409   if( mExampleList.size() > 0 )
410   {
411     if( mSortAlphabetically )
412     {
413       sort( mExampleList.begin(), mExampleList.end(), CompareByTitle );
414     }
415
416     unsigned int exampleCount = 0;
417     ExampleListConstIter iter = mExampleList.begin();
418
419     for( int t = 0; t < mTotalPages; t++ )
420     {
421       // Create Table
422       TableView page = TableView::New( ROWS_PER_PAGE, EXAMPLES_PER_ROW );
423       page.SetAnchorPoint( AnchorPoint::CENTER );
424       page.SetParentOrigin( ParentOrigin::CENTER );
425       page.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
426       mScrollView.Add( page );
427
428       // Calculate the number of images going across (columns) within a page, according to the screen resolution and dpi.
429       const float margin = 2.0f;
430       const float tileParentMultiplier = 1.0f / EXAMPLES_PER_ROW;
431
432       for(int row = 0; row < ROWS_PER_PAGE; row++)
433       {
434         for(int column = 0; column < EXAMPLES_PER_ROW; column++)
435         {
436           const Example& example = ( *iter );
437
438           // Calculate the tiles relative position on the page (between 0 & 1 in each dimension).
439           Vector2 position( static_cast<float>( column ) / ( EXAMPLES_PER_ROW - 1.0f ), static_cast<float>( row ) / ( EXAMPLES_PER_ROW - 1.0f ) );
440           Actor tile = CreateTile( example.name, example.title, Vector3( tileParentMultiplier, tileParentMultiplier, 1.0f ), position );
441           AccessibilityManager accessibilityManager = AccessibilityManager::Get();
442           accessibilityManager.SetFocusOrder( tile, ++exampleCount );
443           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_LABEL,
444                                                   example.title );
445           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_TRAIT, "Tile" );
446           accessibilityManager.SetAccessibilityAttribute( tile, Dali::Toolkit::AccessibilityManager::ACCESSIBILITY_HINT,
447                                                   "You can run this example" );
448
449           tile.SetPadding( Padding( margin, margin, margin, margin ) );
450           page.AddChild( tile, TableView::CellPosition( row, column ) );
451
452           iter++;
453
454           if( iter == mExampleList.end() )
455           {
456             break;
457           }
458         }
459
460         if( iter == mExampleList.end() )
461         {
462           break;
463         }
464       }
465
466       // Set tableview position
467       Vector3 pagePos( stageSize.width * TABLE_RELATIVE_SIZE.x * t, 0.0f, 0.0f );
468       page.SetPosition( pagePos );
469
470       mPages.push_back( page );
471
472       if( iter == mExampleList.end() )
473       {
474         break;
475       }
476     }
477   }
478
479   // Update Ruler info.
480   mScrollRulerX = new FixedRuler( mPageWidth );
481   mScrollRulerY = new DefaultRuler();
482   mScrollRulerX->SetDomain( RulerDomain( 0.0f, (mTotalPages+1) * stageSize.width * TABLE_RELATIVE_SIZE.x * 0.5f, true ) );
483   mScrollRulerY->Disable();
484   mScrollView.SetRulerX( mScrollRulerX );
485   mScrollView.SetRulerY( mScrollRulerY );
486 }
487
488 void DaliTableView::Rotate( unsigned int degrees )
489 {
490   // Resize the root actor
491   Vector2 stageSize = Stage::GetCurrent().GetSize();
492   Vector3 targetSize( stageSize.x, stageSize.y, 1.0f );
493
494   if( degrees == 90 || degrees == 270 )
495   {
496     targetSize = Vector3( stageSize.y, stageSize.x, 1.0f );
497   }
498
499   if( mRotateAnimation )
500   {
501     mRotateAnimation.Stop();
502     mRotateAnimation.Clear();
503   }
504
505   mRotateAnimation = Animation::New( ROTATE_ANIMATION_TIME );
506   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::ORIENTATION ), Quaternion( Radian( Degree( 360 - degrees ) ), Vector3::ZAXIS ), AlphaFunction::EASE_OUT );
507   mRotateAnimation.AnimateTo( Property( mRootActor, Actor::Property::SIZE ), targetSize, AlphaFunction::EASE_OUT );
508   mRotateAnimation.Play();
509 }
510
511 Actor DaliTableView::CreateTile( const std::string& name, const std::string& title, const Dali::Vector3& sizeMultiplier, Vector2& position )
512 {
513   Actor content = Actor::New();
514   content.SetName( name );
515   content.SetAnchorPoint( AnchorPoint::CENTER );
516   content.SetParentOrigin( ParentOrigin::CENTER );
517   content.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::ALL_DIMENSIONS );
518   content.SetSizeModeFactor( sizeMultiplier );
519
520   Toolkit::ImageView tileContent = ImageView::New();
521   tileContent.SetParentOrigin( ParentOrigin::CENTER );
522   tileContent.SetAnchorPoint( AnchorPoint::CENTER );
523   tileContent.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
524
525   // Add the image via the property first.
526   tileContent.SetProperty( Toolkit::ImageView::Property::IMAGE, TILE_BACKGROUND_ALPHA );
527   // Register a property with the ImageView. This allows us to inject the scroll-view position into the shader.
528   Property::Value value = Vector3( 0.0f, 0.0f, 0.0f );
529   Property::Index propertyIndex = tileContent.RegisterProperty( "uCustomPosition", value );
530
531   // Add a shader to the image (details in shader source).
532   Property::Map map;
533   Property::Map customShader;
534   customShader[ Visual::Shader::Property::FRAGMENT_SHADER ] = FRAGMENT_SHADER_TEXTURED;
535   map[ Visual::Property::SHADER ] = customShader;
536   tileContent.SetProperty( Toolkit::ImageView::Property::IMAGE, map );
537   tileContent.SetColor( TILE_COLOR );
538
539   // We create a constraint to perform a precalculation on the scroll-view X offset
540   // and pass it to the shader uniform, along with the tile's position.
541   Constraint shaderPosition = Constraint::New < Vector3 > ( tileContent, propertyIndex, TileShaderPositionConstraint( mPageWidth, position.x ) );
542   shaderPosition.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
543   shaderPosition.SetRemoveAction( Constraint::Discard );
544   shaderPosition.Apply();
545   content.Add( tileContent );
546
547   // Create an ImageView for the 9-patch border around the tile.
548   ImageView borderImage = ImageView::New( TILE_BACKGROUND );
549   borderImage.SetAnchorPoint( AnchorPoint::CENTER );
550   borderImage.SetParentOrigin( ParentOrigin::CENTER );
551   borderImage.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
552   borderImage.SetOpacity( 0.8f );
553   tileContent.Add( borderImage );
554
555   TextLabel label = TextLabel::New();
556   label.SetAnchorPoint( AnchorPoint::CENTER );
557   label.SetParentOrigin( ParentOrigin::CENTER );
558   label.SetStyleName( "LauncherLabel" );
559   label.SetProperty( TextLabel::Property::MULTI_LINE, true );
560   label.SetProperty( TextLabel::Property::TEXT, title );
561   label.SetProperty( TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
562   label.SetProperty( TextLabel::Property::VERTICAL_ALIGNMENT, "CENTER" );
563   label.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::HEIGHT );
564
565   // Pad around the label as its size is the same as the 9-patch border. It will overlap it without padding.
566   label.SetPadding( Padding( TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING, TILE_LABEL_PADDING ) );
567   content.Add( label );
568
569   // Set the tile to be keyboard focusable
570   content.SetKeyboardFocusable( true );
571
572   // connect to the touch events
573   content.TouchSignal().Connect( this, &DaliTableView::OnTilePressed );
574   content.HoveredSignal().Connect( this, &DaliTableView::OnTileHovered );
575
576   return content;
577 }
578
579 bool DaliTableView::OnTilePressed( Actor actor, const TouchData& event )
580 {
581   return DoTilePress( actor, event.GetState( 0 ) );
582 }
583
584 bool DaliTableView::DoTilePress( Actor actor, PointState::Type pointState )
585 {
586   bool consumed = false;
587
588   if( PointState::DOWN == pointState )
589   {
590     mPressedActor = actor;
591     consumed = true;
592   }
593
594   // A button press is only valid if the Down & Up events
595   // both occurred within the button.
596   if( ( PointState::UP == pointState ) &&
597       ( mPressedActor == actor ) )
598   {
599     // ignore Example button presses when scrolling or button animating.
600     if( ( !mScrolling ) && ( !mPressedAnimation ) )
601     {
602       std::string name = actor.GetName();
603       const ExampleListIter end = mExampleList.end();
604       for( ExampleListIter iter = mExampleList.begin(); iter != end; ++iter )
605       {
606         if( (*iter).name == name )
607         {
608           // do nothing, until pressed animation finished.
609           consumed = true;
610           break;
611         }
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.7f, 0.7f, 1.0f ), AlphaFunction::EASE_IN_OUT,
623                                  TimePeriod( 0.0f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
624       mPressedAnimation.AnimateTo( Property( content, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT,
625                                  TimePeriod( BUTTON_PRESS_ANIMATION_TIME * 0.5f, BUTTON_PRESS_ANIMATION_TIME * 0.5f ) );
626
627       // Rotate button on the Y axis when pressed.
628       mPressedAnimation.AnimateBy( Property( content, Actor::Property::ORIENTATION ), Quaternion( Degree( 0.0f ), Degree( 180.0f ), Degree( 0.0f ) ) );
629
630       mPressedAnimation.Play();
631       mPressedAnimation.FinishedSignal().Connect( this, &DaliTableView::OnPressedAnimationFinished );
632     }
633   }
634   return consumed;
635 }
636
637 void DaliTableView::OnPressedAnimationFinished( Dali::Animation& source )
638 {
639   mPressedAnimation.Reset();
640   if( mPressedActor )
641   {
642     std::string name = mPressedActor.GetName();
643
644     std::stringstream stream;
645     stream << DEMO_EXAMPLE_BIN << name.c_str();
646     pid_t pid = fork();
647     if( pid == 0)
648     {
649       execlp( stream.str().c_str(), name.c_str(), NULL );
650       DALI_ASSERT_ALWAYS(false && "exec failed!");
651     }
652     mPressedActor.Reset();
653   }
654 }
655
656 void DaliTableView::OnScrollStart( const Dali::Vector2& position )
657 {
658   mScrolling = true;
659
660   PlayAnimation();
661 }
662
663 void DaliTableView::OnScrollComplete( const Dali::Vector2& position )
664 {
665   mScrolling = false;
666
667   // move focus to 1st item of new page
668   AccessibilityManager accessibilityManager = AccessibilityManager::Get();
669   accessibilityManager.SetCurrentFocusActor(mPages[mScrollView.GetCurrentPage()].GetChildAt(0) );
670 }
671
672 bool DaliTableView::OnScrollTouched( Actor actor, const TouchData& event )
673 {
674   if( PointState::DOWN == event.GetState( 0 ) )
675   {
676     mPressedActor = actor;
677   }
678
679   return false;
680 }
681
682 void DaliTableView::ApplyScrollViewEffect()
683 {
684   // Remove old effect if exists.
685
686   if( mScrollViewEffect )
687   {
688     mScrollView.RemoveEffect( mScrollViewEffect );
689   }
690
691   // Just one effect for now
692   SetupInnerPageCubeEffect();
693
694   mScrollView.ApplyEffect( mScrollViewEffect );
695 }
696
697 void DaliTableView::SetupInnerPageCubeEffect()
698 {
699   const Vector2 stageSize = Stage::GetCurrent().GetSize();
700
701   Dali::Path path = Dali::Path::New();
702   Dali::Property::Array points;
703   points.Resize(3);
704   points[0] = Vector3( stageSize.x*0.5, 0.0f,  stageSize.x*0.5f);
705   points[1] = Vector3( 0.0f, 0.0f, 0.0f );
706   points[2] = Vector3( -stageSize.x*0.5f, 0.0f, stageSize.x*0.5f);
707   path.SetProperty( Path::Property::POINTS, points );
708
709   Dali::Property::Array controlPoints;
710   controlPoints.Resize(4);
711   controlPoints[0] = Vector3( stageSize.x*0.5f, 0.0f, stageSize.x*0.3f );
712   controlPoints[1] = Vector3( stageSize.x*0.3f, 0.0f, 0.0f );
713   controlPoints[2] = Vector3(-stageSize.x*0.3f, 0.0f, 0.0f );
714   controlPoints[3] = Vector3(-stageSize.x*0.5f, 0.0f,  stageSize.x*0.3f );
715   path.SetProperty( Path::Property::CONTROL_POINTS, controlPoints );
716
717
718   mScrollViewEffect = ScrollViewPagePathEffect::New(path,
719                                                     Vector3(-1.0f,0.0f,0.0f),
720                                                     Toolkit::ScrollView::Property::SCROLL_FINAL_X,
721                                                     Vector3(stageSize.x*TABLE_RELATIVE_SIZE.x,stageSize.y*TABLE_RELATIVE_SIZE.y,0.0f),mTotalPages);
722 }
723
724 void DaliTableView::OnKeyEvent( const KeyEvent& event )
725 {
726   if( event.state == KeyEvent::Down )
727   {
728     if ( IsKey( event, Dali::DALI_KEY_ESCAPE) || IsKey( event, Dali::DALI_KEY_BACK) )
729     {
730       // If there's a Popup, Hide it if it's contributing to the display in any way (EG. transitioning in or out).
731       // Otherwise quit.
732       if ( mVersionPopup && ( mVersionPopup.GetDisplayState() != Toolkit::Popup::HIDDEN ) )
733       {
734         mVersionPopup.SetDisplayState( Popup::HIDDEN );
735       }
736       else
737       {
738         mApplication.Quit();
739       }
740     }
741   }
742 }
743
744 void DaliTableView::SetupBackground( Actor bubbleContainer )
745 {
746   // Create distance field shapes.
747   BufferImage distanceFields[2];
748   Size imageSize( 512, 512 );
749
750   CreateShapeImage( CIRCLE, imageSize, distanceFields[0] );
751   CreateShapeImage( BUBBLE, imageSize, distanceFields[1] );
752
753   // Add bubbles to the bubbleContainer.
754   // Note: The bubbleContainer is parented externally to this function.
755   AddBackgroundActors( bubbleContainer, NUM_BACKGROUND_IMAGES, distanceFields );
756 }
757
758 void DaliTableView::InitialiseBackgroundActors( Actor actor )
759 {
760   // Delete current animations
761   mBackgroundAnimations.clear();
762
763   // Create new animations
764   const Vector3 size = actor.GetTargetSize();
765
766   for( unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i )
767   {
768     Actor child = actor.GetChildAt( i );
769
770     // Calculate a random position
771     Vector3 childPos( Random::Range( -size.x * 0.5f * BACKGROUND_SPREAD_SCALE, size.x * 0.85f * BACKGROUND_SPREAD_SCALE ),
772                       Random::Range( -size.y, size.y ),
773                       Random::Range( BUBBLE_MIN_Z, BUBBLE_MAX_Z ) );
774
775     child.SetPosition( childPos );
776
777     // Define bubble horizontal parallax and vertical wrapping
778     Constraint animConstraint = Constraint::New < Vector3 > ( child, Actor::Property::POSITION, AnimateBubbleConstraint( childPos, Random::Range( -0.85f, 0.25f ) ) );
779     animConstraint.AddSource( Source( mScrollView, ScrollView::Property::SCROLL_POSITION ) );
780     animConstraint.AddSource( Dali::ParentSource( Dali::Actor::Property::SIZE ) );
781     animConstraint.AddSource( Dali::LocalSource( Dali::Actor::Property::SIZE ) );
782     animConstraint.SetRemoveAction( Constraint::Discard );
783     animConstraint.Apply();
784
785     // Kickoff animation
786     Animation animation = Animation::New( Random::Range( 30.0f, 160.0f ) );
787     animation.AnimateBy( Property( child, Actor::Property::POSITION ), Vector3( 0.0f, -2000.0f, 0.0f ), AlphaFunction::LINEAR );
788     animation.SetLooping( true );
789     animation.Play();
790     mBackgroundAnimations.push_back( animation );
791   }
792 }
793
794 void DaliTableView::AddBackgroundActors( Actor layer, int count, BufferImage* distanceField )
795 {
796   for( int i = 0; i < count; ++i )
797   {
798     float randSize = Random::Range( 10.0f, 400.0f );
799     int distanceFieldType = static_cast<int>( Random::Range( 0.0f, 1.0f ) + 0.5f );
800     ImageView dfActor = ImageView::New( distanceField[ distanceFieldType ] );
801     dfActor.SetSize( Vector2( randSize, randSize ) );
802     dfActor.SetParentOrigin( ParentOrigin::CENTER );
803
804     Dali::Property::Map effect = Toolkit::CreateDistanceFieldEffect();
805     dfActor.SetProperty( Toolkit::ImageView::Property::IMAGE, effect );
806     dfActor.SetColor( BUBBLE_COLOR[ i%NUMBER_OF_BUBBLE_COLOR ] );
807
808     layer.Add( dfActor );
809   }
810
811   // Positioning will occur when the layer is relaid out
812   layer.OnRelayoutSignal().Connect( this, &DaliTableView::InitialiseBackgroundActors );
813 }
814
815 void DaliTableView::CreateShapeImage( ShapeType shapeType, const Size& size, BufferImage& distanceFieldOut )
816 {
817   // this bitmap will hold the alpha map for the distance field shader
818   distanceFieldOut = BufferImage::New( size.width, size.height, Pixel::A8 );
819
820   // Generate bit pattern
821   std::vector< unsigned char > imageDataA8;
822   imageDataA8.reserve( size.width * size.height ); // A8
823
824   switch( shapeType )
825   {
826     case CIRCLE:
827       GenerateCircle( size, imageDataA8 );
828       break;
829     case BUBBLE:
830       GenerateCircle( size, imageDataA8, true );
831       break;
832     default:
833       break;
834   }
835
836   PixelBuffer* buffer = distanceFieldOut.GetBuffer();
837   if( buffer )
838   {
839     GenerateDistanceFieldMap( &imageDataA8[ 0 ], size, buffer, size, 8.0f, size );
840     distanceFieldOut.Update();
841   }
842 }
843
844 void DaliTableView::GenerateCircle( const Size& size, std::vector< unsigned char >& distanceFieldOut, bool hollow )
845 {
846   const float radius = size.width * 0.5f * size.width * 0.5f;
847   Vector2 center( size.width / 2, size.height / 2 );
848
849   for( int h = 0; h < size.height; ++h )
850   {
851     for( int w = 0; w < size.width; ++w )
852     {
853       Vector2 pos( w, h );
854       Vector2 dist = pos - center;
855
856       float distance = ( dist.x * dist.x ) + ( dist.y * dist.y );
857
858       // If hollow, check the distance against a min & max value, otherwise just use the max value.
859       unsigned char fillByte = ( hollow ? ( ( distance <= radius ) && ( distance > ( radius * 0.7f ) ) ) : ( distance <= radius ) ) ? 0xFF : 0x00;
860
861       distanceFieldOut.push_back( fillByte );
862     }
863   }
864 }
865
866 ImageView DaliTableView::CreateLogo( std::string imagePath )
867 {
868   ImageView logo = ImageView::New( imagePath );
869
870   logo.SetAnchorPoint( AnchorPoint::CENTER );
871   logo.SetParentOrigin( ParentOrigin::CENTER );
872
873   return logo;
874 }
875
876 bool DaliTableView::PauseBackgroundAnimation()
877 {
878   PauseAnimation();
879
880   return false;
881 }
882
883 void DaliTableView::PauseAnimation()
884 {
885   if( mBackgroundAnimsPlaying )
886   {
887     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
888     {
889       Animation anim = *animIter;
890
891       anim.Stop();
892     }
893
894     mBackgroundAnimsPlaying = false;
895   }
896 }
897
898 void DaliTableView::PlayAnimation()
899 {
900   if ( !mBackgroundAnimsPlaying )
901   {
902     for( AnimationListIter animIter = mBackgroundAnimations.begin(); animIter != mBackgroundAnimations.end(); ++animIter )
903     {
904       Animation anim = *animIter;
905
906       anim.Play();
907     }
908
909     mBackgroundAnimsPlaying = true;
910   }
911
912   mAnimationTimer.SetInterval( BACKGROUND_ANIMATION_DURATION );
913 }
914
915 Dali::Actor DaliTableView::OnKeyboardPreFocusChange( Dali::Actor current, Dali::Actor proposed, Dali::Toolkit::Control::KeyboardFocus::Direction direction )
916 {
917   Actor nextFocusActor = proposed;
918
919   if( !current && !proposed  )
920   {
921     // Set the initial focus to the first tile in the current page should be focused.
922     nextFocusActor = mPages[mScrollView.GetCurrentPage()].GetChildAt(0);
923   }
924   else if( !proposed )
925   {
926     // ScrollView is being focused but nothing in the current page can be focused further
927     // in the given direction. We should work out which page to scroll to next.
928     int currentPage = mScrollView.GetCurrentPage();
929     int newPage = currentPage;
930     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
931     {
932       newPage--;
933     }
934     else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
935     {
936       newPage++;
937     }
938
939     newPage = std::max(0, std::min(mTotalPages - 1, newPage));
940     if( newPage == currentPage )
941     {
942       if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
943       {
944         newPage = mTotalPages - 1;
945       } else if( direction == Dali::Toolkit::Control::KeyboardFocus::RIGHT )
946       {
947         newPage = 0;
948       }
949     }
950
951     // Scroll to the page in the given direction
952     mScrollView.ScrollTo(newPage);
953
954     if( direction == Dali::Toolkit::Control::KeyboardFocus::LEFT )
955     {
956       // Work out the cell position for the last tile
957       int remainingExamples = mExampleList.size() - newPage * EXAMPLES_PER_PAGE;
958       int rowPos = (remainingExamples >= EXAMPLES_PER_PAGE) ? ROWS_PER_PAGE - 1 : ( (remainingExamples % EXAMPLES_PER_PAGE + EXAMPLES_PER_ROW) / EXAMPLES_PER_ROW - 1 );
959       int colPos = remainingExamples >= EXAMPLES_PER_PAGE ? EXAMPLES_PER_ROW - 1 : ( remainingExamples % EXAMPLES_PER_PAGE - rowPos * EXAMPLES_PER_ROW - 1 );
960
961       // Move the focus to the last tile in the new page.
962       nextFocusActor = mPages[newPage].GetChildAt(rowPos * EXAMPLES_PER_ROW + colPos);
963     }
964     else
965     {
966       // Move the focus to the first tile in the new page.
967       nextFocusActor = mPages[newPage].GetChildAt(0);
968     }
969   }
970
971   return nextFocusActor;
972 }
973
974 void DaliTableView::OnFocusedActorActivated( Dali::Actor activatedActor )
975 {
976   if(activatedActor)
977   {
978     mPressedActor = activatedActor;
979
980     // Activate the current focused actor;
981     DoTilePress( mPressedActor, PointState::UP );
982   }
983 }
984
985 bool DaliTableView::OnTileHovered( Actor actor, const HoverEvent& event )
986 {
987   KeyboardFocusManager::Get().SetCurrentFocusActor( actor );
988   return true;
989 }
990
991 void DaliTableView::OnLogoTapped( Dali::Actor actor, const Dali::TapGesture& tap )
992 {
993   // Only show if currently fully hidden. If transitioning-out, the transition will not be interrupted.
994   if ( !mVersionPopup || ( mVersionPopup.GetDisplayState() == Toolkit::Popup::HIDDEN ) )
995   {
996     if ( !mVersionPopup )
997     {
998       std::ostringstream stream;
999       stream << "DALi Core: "    << CORE_MAJOR_VERSION << "." << CORE_MINOR_VERSION << "." << CORE_MICRO_VERSION << std::endl << "(" << CORE_BUILD_DATE << ")\n";
1000       stream << "DALi Adaptor: " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << std::endl << "(" << ADAPTOR_BUILD_DATE << ")\n";
1001       stream << "DALi Toolkit: " << TOOLKIT_MAJOR_VERSION << "." << TOOLKIT_MINOR_VERSION << "." << TOOLKIT_MICRO_VERSION << std::endl << "(" << TOOLKIT_BUILD_DATE << ")\n";
1002
1003       mVersionPopup = Dali::Toolkit::Popup::New();
1004
1005       Toolkit::TextLabel titleActor = Toolkit::TextLabel::New( "Version information" );
1006       titleActor.SetName( "titleActor" );
1007       titleActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
1008
1009       Toolkit::TextLabel contentActor = Toolkit::TextLabel::New( stream.str() );
1010       contentActor.SetName( "contentActor" );
1011       contentActor.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
1012       contentActor.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
1013       contentActor.SetPadding( Padding( 0.0f, 0.0f, 20.0f, 0.0f ) );
1014
1015       mVersionPopup.SetTitle( titleActor );
1016       mVersionPopup.SetContent( contentActor );
1017
1018       mVersionPopup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
1019       mVersionPopup.SetSizeModeFactor( Vector3( 0.75f, 1.0f, 1.0f ) );
1020       mVersionPopup.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::HEIGHT );
1021
1022       mVersionPopup.OutsideTouchedSignal().Connect( this, &DaliTableView::HideVersionPopup );
1023       Stage::GetCurrent().Add( mVersionPopup );
1024     }
1025
1026     mVersionPopup.SetDisplayState( Popup::SHOWN );
1027   }
1028 }
1029
1030 void DaliTableView::HideVersionPopup()
1031 {
1032   // Only hide if currently fully shown. If transitioning-in, the transition will not be interrupted.
1033   if ( mVersionPopup && ( mVersionPopup.GetDisplayState() == Toolkit::Popup::SHOWN ) )
1034   {
1035     mVersionPopup.SetDisplayState( Popup::HIDDEN );
1036   }
1037 }