[Tizen](ATSPI) squashed implementation
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / popup / popup-impl.cpp
1 /*
2  * Copyright (c) 2017 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-toolkit/internal/controls/popup/popup-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for strcmp
23 #include <dali/devel-api/adaptor-framework/physical-keyboard.h>
24 #include <dali/public-api/object/type-registry-helper.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/public-api/adaptor-framework/key.h>
27 #include <dali/public-api/animation/constraints.h>
28 #include <dali/public-api/common/stage.h>
29 #include <dali/public-api/events/key-event.h>
30 #include <dali/public-api/events/touch-data.h>
31 #include <dali/public-api/object/type-registry.h>
32 #include <dali/devel-api/scripting/scripting.h>
33 #include <dali/devel-api/actors/actor-devel.h>
34 #include <dali/public-api/size-negotiation/relayout-container.h>
35
36 // INTERNAL INCLUDES
37 #include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
38 #include <dali-toolkit/public-api/controls/control-impl.h>
39 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
40 #include <dali-toolkit/public-api/visuals/color-visual-properties.h>
41 #include <dali-toolkit/public-api/visuals/visual-properties.h>
42 #include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
43 #include <dali-toolkit/internal/controls/control/control-data-impl.h>
44
45 using namespace Dali;
46
47 namespace Dali
48 {
49
50 namespace Toolkit
51 {
52
53 namespace Internal
54 {
55
56 namespace
57 {
58
59 /**
60  * Creation function for main Popup type.
61  * @return Handle to the new popup object.
62  */
63 BaseHandle Create()
64 {
65   return Toolkit::Popup::New();
66 }
67
68 // Toast style defaults.
69 const int          DEFAULT_TOAST_AUTO_HIDE_DELAY = 3000;                                    ///< Toast will auto-hide after 3000ms (3 seconds)
70 const float        DEFAULT_TOAST_TRANSITION_TIME = 0.65f;                                   ///< Default time the toast Popup will take to show and hide.
71 const Vector3      DEFAULT_TOAST_BOTTOM_PARENT_ORIGIN( 0.5f, 0.94f, 0.5f );                 ///< This is similar to BOTTOM_CENTER, but vertically higher up, as a ratio of parent height.
72 const Vector3      DEFAULT_TOAST_WIDTH_OF_STAGE_RATIO( 0.75f, 0.75f, 0.75f );               ///< Amount of the stage's width that the toast popup will take up.
73
74 /**
75  * Creation function for named type "popupToast".
76  * @return Handle to the new toast popup object.
77  */
78 BaseHandle CreateToast()
79 {
80   Toolkit::Popup popup = Toolkit::Popup::New();
81
82   // Setup for Toast Popup type.
83   popup.SetSizeModeFactor( DEFAULT_TOAST_WIDTH_OF_STAGE_RATIO );
84   popup.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
85   popup.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
86   popup.SetProperty( Toolkit::Popup::Property::CONTEXTUAL_MODE, Toolkit::Popup::NON_CONTEXTUAL );
87   popup.SetProperty( Toolkit::Popup::Property::ANIMATION_DURATION, DEFAULT_TOAST_TRANSITION_TIME );
88   popup.SetProperty( Toolkit::Popup::Property::TAIL_VISIBILITY, false );
89
90   // Disable the dimmed backing.
91   popup.SetProperty( Toolkit::Popup::Property::BACKING_ENABLED, false );
92
93   // The toast popup should fade in (not zoom).
94   popup.SetProperty( Toolkit::Popup::Property::ANIMATION_MODE, Toolkit::Popup::FADE );
95
96   // The toast popup should auto-hide.
97   popup.SetProperty( Toolkit::Popup::Property::AUTO_HIDE_DELAY, DEFAULT_TOAST_AUTO_HIDE_DELAY );
98
99   // Align to the bottom of the screen.
100   popup.SetParentOrigin( DEFAULT_TOAST_BOTTOM_PARENT_ORIGIN );
101   popup.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
102
103   // Let events pass through the toast popup.
104   popup.SetProperty( Toolkit::Popup::Property::TOUCH_TRANSPARENT, true );
105
106   return popup;
107 }
108
109 // Setup properties, signals and actions using the type-registry.
110 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Popup, Toolkit::Control, Create )
111
112 // Main content related properties.
113 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "title",                             MAP,              TITLE                   )
114 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "content",                           MAP,              CONTENT                 )
115 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "footer",                            MAP,              FOOTER                  )
116 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "displayState",                      STRING,           DISPLAY_STATE           )
117 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "touchTransparent",                  BOOLEAN,          TOUCH_TRANSPARENT       )
118
119 // Contextual related properties.
120 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "tailVisibility",                    BOOLEAN,          TAIL_VISIBILITY         )
121 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "tailPosition",                      VECTOR3,          TAIL_POSITION           )
122 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "contextualMode",                    STRING,           CONTEXTUAL_MODE         )
123
124 // Animation related properties.
125 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "animationDuration",                 FLOAT,            ANIMATION_DURATION      )
126 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "animationMode",                     STRING,           ANIMATION_MODE          )
127 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "entryAnimation",                    MAP,              ENTRY_ANIMATION         )
128 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "exitAnimation",                     MAP,              EXIT_ANIMATION          )
129 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "autoHideDelay",                     INTEGER,          AUTO_HIDE_DELAY         )
130
131 // Style related properties.
132 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "backingEnabled",                    BOOLEAN,          BACKING_ENABLED         )
133 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "backingColor",                      VECTOR4,          BACKING_COLOR           )
134 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "popupBackgroundImage",              STRING,           POPUP_BACKGROUND_IMAGE  )
135 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "popupBackgroundBorder",             RECTANGLE,        POPUP_BACKGROUND_BORDER )
136 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "tailUpImage",                       STRING,           TAIL_UP_IMAGE           )
137 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "tailDownImage",                     STRING,           TAIL_DOWN_IMAGE         )
138 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "tailLeftImage",                     STRING,           TAIL_LEFT_IMAGE         )
139 DALI_PROPERTY_REGISTRATION( Toolkit, Popup, "tailRightImage",                    STRING,           TAIL_RIGHT_IMAGE        )
140
141 // Signals.
142 DALI_SIGNAL_REGISTRATION(   Toolkit, Popup, "touchedOutside",                                      SIGNAL_TOUCHED_OUTSIDE  )
143 DALI_SIGNAL_REGISTRATION(   Toolkit, Popup, "showing",                                             SIGNAL_SHOWING          )
144 DALI_SIGNAL_REGISTRATION(   Toolkit, Popup, "shown",                                               SIGNAL_SHOWN            )
145 DALI_SIGNAL_REGISTRATION(   Toolkit, Popup, "hiding",                                              SIGNAL_HIDING           )
146 DALI_SIGNAL_REGISTRATION(   Toolkit, Popup, "hidden",                                              SIGNAL_HIDDEN           )
147
148 DALI_TYPE_REGISTRATION_END()
149
150 // Named type registration.
151
152 // Toast Popup: Non-modal popup that displays information at the bottom of the screen.
153 TypeRegistration typeRegistrationToast( "PopupToast",  typeid( Toolkit::Popup ), CreateToast );
154
155 // Enumeration to / from string conversion tables
156
157 const Scripting::StringEnum DisplayStateTable[] = {
158   { "SHOWING", Toolkit::Popup::SHOWING },
159   { "SHOWN",   Toolkit::Popup::SHOWN   },
160   { "HIDING",  Toolkit::Popup::HIDING  },
161   { "HIDDEN",  Toolkit::Popup::HIDDEN  },
162 }; const unsigned int DisplayStateTableCount = sizeof( DisplayStateTable ) / sizeof( DisplayStateTable[0] );
163
164 const Scripting::StringEnum AnimationModeTable[] = {
165   { "NONE",    Toolkit::Popup::NONE    },
166   { "ZOOM",    Toolkit::Popup::ZOOM    },
167   { "FADE",    Toolkit::Popup::FADE    },
168   { "CUSTOM",  Toolkit::Popup::CUSTOM  },
169 }; const unsigned int AnimationModeTableCount = sizeof( AnimationModeTable ) / sizeof( AnimationModeTable[0] );
170
171 const Scripting::StringEnum ContextualModeTable[] = {
172   { "NON_CONTEXTUAL", Toolkit::Popup::NON_CONTEXTUAL },
173   { "ABOVE",          Toolkit::Popup::ABOVE          },
174   { "RIGHT",          Toolkit::Popup::RIGHT          },
175   { "BELOW",          Toolkit::Popup::BELOW          },
176   { "LEFT",           Toolkit::Popup::LEFT           },
177 }; const unsigned int ContextualModeTableCount = sizeof( ContextualModeTable ) / sizeof( ContextualModeTable[0] );
178
179 // Popup defaults.
180 const Vector3 DEFAULT_POPUP_PARENT_RELATIVE_SIZE( 0.75f, 1.0f, 1.0f );                      ///< Default size percentage of parent.
181 const float   DEFAULT_POPUP_ANIMATION_DURATION =  0.6f;                                     ///< Duration of hide/show animations.
182 const float   POPUP_OUT_MARGIN_WIDTH =            16.f;                                     ///< Space between the screen edge and the popup edge in the horizontal dimension.
183 const float   POPUP_OUT_MARGIN_HEIGHT =           36.f;                                     ///< Space between the screen edge and the popup edge in the vertical dimension.
184 const Vector3 DEFAULT_TAIL_POSITION( 0.5f, 1.0f, 0.0f );                                    ///< Position the tail will be displayed when enabled without setting the position.
185
186 // Contextual defaults.
187 const Vector2 DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN( 10.0f, 10.0f );                          ///< How close the Popup will be to it's contextual parent.
188 const Vector2 DEFAULT_CONTEXTUAL_STAGE_BORDER( 15.0f, 15.0f );                              ///< How close the Popup can be to the stage edges.
189
190 // Popup style defaults.
191 const char*   DEFAULT_BACKGROUND_IMAGE_PATH =     DALI_IMAGE_DIR "00_popup_bg.9.png";       ///< Background image.
192 const char*   DEFAULT_TAIL_UP_IMAGE_PATH =        DALI_IMAGE_DIR "popup_tail_up.png";       ///< Tail up image.
193 const char*   DEFAULT_TAIL_DOWN_IMAGE_PATH =      DALI_IMAGE_DIR "popup_tail_down.png";     ///< Tail down image.
194 const char*   DEFAULT_TAIL_LEFT_IMAGE_PATH =      DALI_IMAGE_DIR "popup_tail_left.png";     ///< Tail left image.
195 const char*   DEFAULT_TAIL_RIGHT_IMAGE_PATH =     DALI_IMAGE_DIR "popup_tail_right.png";    ///< Tail right image.
196
197 const Vector4 DEFAULT_BACKING_COLOR( 0.0f, 0.0f, 0.0f, 0.5f );                              ///< Color of the dimmed backing.
198 const Rect<int> DEFAULT_BACKGROUND_BORDER( 17, 17, 13, 13 );                                ///< Default border of the background.
199 const Rect<float>  DEFAULT_TITLE_PADDING( 20.0f, 20.0f, 20.0f, 20.0f );                     ///< Title padding used on popups with content and/or controls (from Tizen GUI UX).
200 const Rect<float>  DEFAULT_TITLE_ONLY_PADDING( 8.0f, 8.0f, 8.0f, 8.0f );                    ///< Title padding used on popups with a title only (like toast popups).
201 const Vector3 FOOTER_SIZE( 620.0f, 96.0f,0.0f );                                            ///< Default size of the bottom control area.
202 const float   DEFAULT_RELATIVE_PARENT_WIDTH =     0.75f;                                    ///< If width is not fixed, relative size to parent is used by default.
203
204 } // Unnamed namespace
205
206 /*
207  * Implementation.
208  */
209
210 Dali::Toolkit::Popup Popup::New()
211 {
212   // Create the implementation
213   PopupPtr popup( new Popup() );
214
215   // Pass ownership to CustomActor via derived handle.
216   Dali::Toolkit::Popup handle( *popup );
217
218   // Second-phase initialisation of the implementation.
219   // This can only be done after the CustomActor connection has been made.
220   popup->Initialize();
221
222   return handle;
223 }
224
225 Popup::Popup()
226 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
227   mTouchedOutsideSignal(),
228   mShowingSignal(),
229   mShownSignal(),
230   mHidingSignal(),
231   mHiddenSignal(),
232   mLayer(),
233   mPopupLayout(),
234   mBacking(),
235   mPreviousFocusedActor(),
236   mTailImage(),
237   mPopupContainer(),
238   mAnimation(),
239   mAlterAddedChild( false ),
240   mLayoutDirty( true ),
241   mAutoHideTimer(),
242   mTouchTransparent( false ),
243   mTitle(),
244   mContent(),
245   mFooter(),
246   mDisplayState( Toolkit::Popup::HIDDEN ), // Hidden until shown with SetDisplayState()
247   mTailVisible( false ),
248   mTailPosition( DEFAULT_TAIL_POSITION ),
249   mContextualMode( Toolkit::Popup::NON_CONTEXTUAL ),
250   mAnimationDuration( DEFAULT_POPUP_ANIMATION_DURATION ),
251   mAnimationMode( Toolkit::Popup::FADE ),
252   mEntryAnimationData(),
253   mExitAnimationData(),
254   mAutoHideDelay( 0 ),
255   mBackingEnabled( true ),
256   mBackingColor( DEFAULT_BACKING_COLOR ),
257   mPopupBackgroundImage(),
258   mBackgroundBorder( DEFAULT_BACKGROUND_BORDER ),
259   mMargin(),
260   mTailUpImage( DEFAULT_TAIL_UP_IMAGE_PATH ),
261   mTailDownImage( DEFAULT_TAIL_DOWN_IMAGE_PATH ),
262   mTailLeftImage( DEFAULT_TAIL_LEFT_IMAGE_PATH ),
263   mTailRightImage( DEFAULT_TAIL_RIGHT_IMAGE_PATH )
264 {
265   SetKeyboardNavigationSupport( true );
266   DevelControl::SetAccessibilityConstructor( Self(), []( Dali::Actor actor ) {
267     return std::unique_ptr< Dali::Accessibility::Accessible >(
268         new Control::Impl::AccessibleImpl( actor, Dali::Accessibility::Role::DIALOG, true ) );
269   } );
270 }
271
272 void Popup::OnInitialize()
273 {
274   Actor self = Self();
275   self.SetName( "popup" );
276
277   // Apply some default resizing rules.
278   self.SetParentOrigin( ParentOrigin::CENTER );
279   self.SetAnchorPoint( AnchorPoint::CENTER );
280
281   self.SetSizeModeFactor( DEFAULT_POPUP_PARENT_RELATIVE_SIZE );
282   self.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH );
283   self.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
284
285   // Create a new layer so all Popup components can appear above all other actors.
286   mLayer = Layer::New();
287   mLayer.SetName( "popupLayer" );
288
289   mLayer.SetParentOrigin( ParentOrigin::CENTER );
290   mLayer.SetAnchorPoint( AnchorPoint::CENTER );
291   mLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
292
293   // Important to set as invisible as otherwise, if the popup is parented,
294   // but not shown yet it will appear statically on the screen.
295   mLayer.SetVisible( false );
296
297   // Add the layer to the hierarchy.
298   self.Add( mLayer );
299
300   // Add Backing (Dimmed effect).
301   mBacking = CreateBacking();
302   mLayer.Add( mBacking );
303
304   mPopupContainer = Actor::New();
305   mPopupContainer.SetName( "popupContainer" );
306   mPopupContainer.SetParentOrigin( ParentOrigin::CENTER );
307   mPopupContainer.SetAnchorPoint( AnchorPoint::CENTER );
308   mPopupContainer.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::ALL_DIMENSIONS );
309   mLayer.Add( mPopupContainer );
310
311   // Create the Popup layout to contain all main content.
312   mPopupLayout = Toolkit::TableView::New( 3, 1 );
313
314   // Adds the default background image.
315   SetPopupBackgroundImage( Toolkit::ImageView::New( DEFAULT_BACKGROUND_IMAGE_PATH ) );
316
317   mPopupLayout.SetName( "popupLayoutTable" );
318   mPopupLayout.SetParentOrigin( ParentOrigin::CENTER );
319   mPopupLayout.SetAnchorPoint( AnchorPoint::CENTER );
320
321   mPopupLayout.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::WIDTH );
322   mPopupLayout.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
323   mPopupLayout.SetSize( Stage::GetCurrent().GetSize().x * DEFAULT_RELATIVE_PARENT_WIDTH, 0.0f );
324
325   mPopupLayout.SetFitHeight( 0 ); // Set row to fit.
326   mPopupLayout.SetFitHeight( 1 ); // Set row to fit.
327
328   mPopupLayout.TouchSignal().Connect( this, &Popup::OnDialogTouched );
329
330   mPopupContainer.Add( mPopupLayout );
331
332   // Any content after this point which is added to Self() will be re-parented to mContent.
333   mAlterAddedChild = true;
334
335   SetAsKeyboardFocusGroup( true );
336 }
337
338 Popup::~Popup()
339 {
340   if( DevelControl::GetBoundAccessibilityObject( Self() ) )
341     Accessibility::Bridge::GetCurrentBridge()->RemovePopup( DevelControl::GetBoundAccessibilityObject( Self() ) );
342   mEntryAnimationData.Clear();
343   mExitAnimationData.Clear();
344 }
345
346 void Popup::LayoutAnimation()
347 {
348   // Perform setup based on the currently selected animation.
349   switch( mAnimationMode )
350   {
351     case Toolkit::Popup::ZOOM:
352     {
353       // Zoom animations start fully zoomed out.
354       mPopupContainer.SetScale( Vector3::ZERO );
355       break;
356     }
357
358     case Toolkit::Popup::FADE:
359     {
360       // Fade animations start transparent.
361       mPopupContainer.SetOpacity( 0.0f );
362       break;
363     }
364
365     case Toolkit::Popup::CUSTOM:
366     {
367       // Initialise the custom animation by playing to the end of it's exit animation instantly.
368       // EG. If it was zooming in, then we zoom out fully instantly so the zoom in works.
369       StartTransitionAnimation( false, true );
370       break;
371     }
372
373     case Toolkit::Popup::NONE:
374     {
375       break;
376     }
377   }
378 }
379
380 void Popup::StartTransitionAnimation( bool transitionIn, bool instantaneous /* false */ )
381 {
382   // Stop and recreate animation.
383   if ( mAnimation )
384   {
385     mAnimation.Stop();
386     mAnimation.Clear();
387     mAnimation.Reset();
388   }
389   float duration = GetAnimationDuration();
390
391   // Setup variables ready to start the animations.
392   // If we are performing the animation instantaneously, we do not want to emit a signal.
393   if( !instantaneous )
394   {
395     if( transitionIn )
396     {
397       // Setup variables and signal that we are starting the transition.
398       // Note: We signal even if the transition is instant so signal order is consistent.
399       mShowingSignal.Emit();
400     }
401     else
402     {
403       mHidingSignal.Emit();
404     }
405   }
406
407   // Perform chosen animation for the Popup.
408   switch( mAnimationMode )
409   {
410     case Toolkit::Popup::NONE:
411     {
412       mAnimation = Animation::New( 0.0f );
413       break;
414     }
415
416     case Toolkit::Popup::ZOOM:
417     {
418       mAnimation = Animation::New( duration );
419       if( duration > Math::MACHINE_EPSILON_0 )
420       {
421         if( transitionIn )
422         {
423           mAnimation.AnimateTo( Property( mPopupContainer, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT, TimePeriod( duration * 0.25f, duration * 0.75f ) );
424         }
425         else
426         {
427           // Zoom out animation is twice the speed. Modify the duration variable so the backing animation speed is modified also.
428           duration /= 2.0f;
429           mAnimation.SetDuration( duration );
430           mAnimation.AnimateTo( Property( mPopupContainer, Actor::Property::SCALE ), Vector3::ZERO, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.0f, duration ) );
431         }
432       }
433       else
434       {
435         mPopupContainer.SetScale( transitionIn ? Vector3::ONE : Vector3::ZERO );
436       }
437       break;
438     }
439
440     case Toolkit::Popup::FADE:
441     {
442       mAnimation = Animation::New( duration );
443       if( duration > Math::MACHINE_EPSILON_0 )
444       {
445         if( transitionIn )
446         {
447           mAnimation.AnimateTo( Property( mPopupContainer, Actor::Property::COLOR_ALPHA ), 1.0f, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.30f, duration * 0.70f ) );
448         }
449         else
450         {
451           mAnimation.AnimateTo( Property( mPopupContainer, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.0f, duration * 0.70f ) );
452         }
453       }
454       else
455       {
456         mPopupContainer.SetOpacity( transitionIn ? 1.0f : 0.0f );
457       }
458       break;
459     }
460
461     case Toolkit::Popup::CUSTOM:
462     {
463       // Use a user specified animation for in and out.
464       // Read the correct animation depending on entry or exit.
465       // Attempt to use animation data defined from script data.
466       Dali::AnimationData* animationData = transitionIn ? &mEntryAnimationData : &mExitAnimationData;
467
468       // Create a new animation from the pre-defined data in the AnimationData class.
469       // If there is no data, mAnimation is invalidated.
470       mAnimation = animationData->CreateAnimation( mPopupContainer, duration );
471
472       // If we don't have a valid animation, provide a blank one so play() can still function generically.
473       if( !mAnimation )
474       {
475         // No animation was configured (even though custom mode was specified). Create a dummy animation to avoid an exception.
476         mAnimation = Animation::New( 0.0f );
477       }
478
479       break;
480     }
481   }
482
483   // Animate the backing, if enabled.
484   // This is set up last so that different animation modes can have an effect on the backing animation speed.
485   if( mBackingEnabled )
486   {
487     // Use the alpha from the user-specified color.
488     float targetAlpha = mBackingColor.a;
489     if( duration > Math::MACHINE_EPSILON_0 )
490     {
491       if( transitionIn )
492       {
493         mAnimation.AnimateTo( Property( mBacking, Actor::Property::COLOR_ALPHA ), targetAlpha, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.0f, duration * 0.70f ) );
494       }
495       else
496       {
497         mAnimation.AnimateTo( Property( mBacking, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.30f, duration * 0.70f ) );
498       }
499     }
500     else
501     {
502       mBacking.SetProperty( Actor::Property::COLOR_ALPHA, transitionIn ? targetAlpha : 0.0f );
503     }
504   }
505
506   // If we are performing the animation instantaneously, jump to the position directly and do not signal.
507   if( instantaneous )
508   {
509     mAnimation.SetCurrentProgress( 1.0f );
510     mAnimation.Play();
511   }
512   else if( duration > Math::MACHINE_EPSILON_0 )
513   {
514     // Run the animation.
515     mAnimation.FinishedSignal().Connect( this, &Popup::OnDisplayChangeAnimationFinished );
516     mAnimation.Play();
517   }
518   else
519   {
520     // We did not use an animation to achive the transition.
521     // Trigger the state change directly.
522     DisplayStateChangeComplete();
523   }
524 }
525
526 void Popup::OnDisplayChangeAnimationFinished( Animation& source )
527 {
528   DisplayStateChangeComplete();
529 }
530
531 void Popup::DisplayStateChangeComplete()
532 {
533   // Remove contents from stage if completely hidden.
534   if( mDisplayState == Toolkit::Popup::HIDING )
535   {
536     mDisplayState = Toolkit::Popup::HIDDEN;
537
538     mLayer.SetVisible( false );
539     mPopupLayout.SetSensitive( false );
540
541     // Guard against destruction during signal emission.
542     Toolkit::Popup handle( GetOwner() );
543     mHiddenSignal.Emit();
544   }
545   else if( mDisplayState == Toolkit::Popup::SHOWING )
546   {
547     mDisplayState = Toolkit::Popup::SHOWN;
548     Toolkit::Popup handle( GetOwner() );
549     mShownSignal.Emit();
550
551     // Start a timer to auto-hide if enabled.
552     if( mAutoHideDelay > 0u )
553     {
554       mAutoHideTimer = Timer::New( mAutoHideDelay );
555       mAutoHideTimer.TickSignal().Connect( this, &Popup::OnAutoHideTimeReached );
556       mAutoHideTimer.Start();
557     }
558   }
559 }
560
561 bool Popup::OnAutoHideTimeReached()
562 {
563   // Display timer has expired, auto hide the popup exactly as if the user had clicked outside.
564   SetDisplayState( Toolkit::Popup::HIDDEN );
565
566   if( mAutoHideTimer )
567   {
568     mAutoHideTimer.Stop();
569     mAutoHideTimer.TickSignal().Disconnect( this, &Popup::OnAutoHideTimeReached );
570     mAutoHideTimer.Reset();
571   }
572   return true;
573 }
574
575 void Popup::SetPopupBackgroundImage( Actor image )
576 {
577   // Removes any previous background.
578   if( mPopupBackgroundImage )
579   {
580     mPopupBackgroundImage.Unparent();
581     if( mTailImage )
582     {
583       mTailImage.Unparent();
584     }
585   }
586
587   // Adds new background to the dialog.
588   mPopupBackgroundImage = image;
589   mPopupBackgroundImage.SetName( "popupBackgroundImage" );
590   mPopupBackgroundImage.SetAnchorPoint( AnchorPoint::CENTER );
591   mPopupBackgroundImage.SetParentOrigin( ParentOrigin::CENTER );
592
593   // OnDialogTouched only consumes the event. It prevents the touch event to be caught by the backing.
594   mPopupBackgroundImage.TouchSignal().Connect( this, &Popup::OnDialogTouched );
595
596   // Set the popup border to be slightly larger than the layout contents.
597   UpdateBackgroundPositionAndSize();
598
599   const bool prevAlter = mAlterAddedChild;
600   mAlterAddedChild = false;
601   mPopupContainer.Add( mPopupBackgroundImage );
602   mPopupBackgroundImage.LowerToBottom();
603   mAlterAddedChild = prevAlter;
604
605   if( mTailImage )
606   {
607     mPopupBackgroundImage.Add( mTailImage );
608   }
609
610   mLayoutDirty = true;
611 }
612
613 Actor Popup::GetPopupBackgroundImage() const
614 {
615   return mPopupBackgroundImage;
616 }
617
618 void Popup::SetTitle( Actor titleActor )
619 {
620   // Replaces the current title actor.
621   if( !mPopupLayout )
622   {
623     return;
624   }
625
626   if( mTitle )
627   {
628     mPopupLayout.RemoveChildAt( Toolkit::TableView::CellPosition( 0, 0) );
629   }
630   mTitle = titleActor;
631
632   if( mTitle )
633   {
634     // Set up padding to give sensible default behaviour
635     // (an application developer can later override this if they wish).
636     mTitle.SetPadding( DEFAULT_TITLE_PADDING );
637
638     mPopupLayout.AddChild( mTitle, Toolkit::TableView::CellPosition( 0, 0 ) );
639   }
640
641   mLayoutDirty = true;
642   RelayoutRequest();
643 }
644
645 Actor Popup::GetTitle() const
646 {
647   return mTitle;
648 }
649
650 void Popup::SetContent( Actor content )
651 {
652   // Remove previous content actor.
653   if( mPopupLayout )
654   {
655     mPopupLayout.RemoveChildAt( Toolkit::TableView::CellPosition( 1, 0 ) );
656   }
657    // Keep a handle to the new content.
658   mContent = content;
659
660   if( mContent )
661   {
662     mContent.SetName( "popupContent" );
663
664     mPopupLayout.AddChild( mContent, Toolkit::TableView::CellPosition( 1, 0 ) );
665   }
666
667   mLayoutDirty = true;
668   RelayoutRequest();
669 }
670
671 Actor Popup::GetContent() const
672 {
673   return mContent;
674 }
675
676 void Popup::SetFooter( Actor footer )
677 {
678   // Remove previous content actor.
679   if( mPopupLayout )
680   {
681     mPopupLayout.RemoveChildAt( Toolkit::TableView::CellPosition( 2, 0 ) );
682   }
683
684   // Keep a handle to the new content.
685   mFooter = footer;
686
687   if( mFooter )
688   {
689     mFooter.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
690
691     // The control container has a fixed height.
692     mPopupLayout.SetFitHeight( 2u );
693     mPopupLayout.AddChild( footer, Toolkit::TableView::CellPosition( 2, 0 ) );
694   }
695
696   mLayoutDirty = true;
697   RelayoutRequest();
698 }
699
700 Actor Popup::GetFooter() const
701 {
702   return mFooter;
703 }
704
705 void Popup::SetDisplayState( Toolkit::Popup::DisplayState displayState )
706 {
707   // Convert the 4-way state to a bool, true for show, false for hide.
708   bool display = ( displayState == Toolkit::Popup::SHOWING ) || ( displayState == Toolkit::Popup::SHOWN );
709
710   // Ignore if we are already at the target display state.
711   if( display == ( ( mDisplayState == Toolkit::Popup::SHOWING ) || ( mDisplayState == Toolkit::Popup::SHOWN ) ) )
712   {
713     return;
714   }
715
716   // Convert the bool state to the actual display state to use.
717   mDisplayState = display ? Toolkit::Popup::SHOWING : Toolkit::Popup::HIDING;
718
719   if ( display )
720   {
721     // Update the state to indicate the current intent.
722     mDisplayState = Toolkit::Popup::SHOWING;
723
724     // We want the popup to have key input focus when it is displayed
725     SetKeyInputFocus();
726
727     // We are displaying so bring the popup layer to the front, and set it visible so it is rendered.
728     mLayer.RaiseToTop();
729     mLayer.SetVisible( true );
730
731     // Set up the layout if this is the first display or the layout has become dirty.
732     if( mLayoutDirty )
733     {
734       // Bake-in any style and layout options to create the Popup layout.
735       LayoutPopup();
736     }
737
738     // Allow the popup to catch events.
739     mPopupLayout.SetSensitive( true );
740
741     // Handle the keyboard focus when popup is shown.
742     Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
743     if( keyboardFocusManager )
744     {
745       mPreviousFocusedActor = keyboardFocusManager.GetCurrentFocusActor();
746
747       if( Self().IsKeyboardFocusable() )
748       {
749         // Setup the actgor to start focus from.
750         Actor focusActor;
751         if( mContent && mContent.IsKeyboardFocusable() )
752         {
753           // If the content is focusable, move the focus to the content.
754           focusActor = mContent;
755         }
756         else if( mFooter && mFooter.IsKeyboardFocusable() )
757         {
758           // If the footer is focusable, move the focus to the footer.
759           focusActor = mFooter;
760         }
761         else
762         {
763           DALI_LOG_WARNING( "There is no focusable in popup\n" );
764         }
765
766         if( focusActor )
767         {
768           keyboardFocusManager.SetCurrentFocusActor( focusActor );
769         }
770       }
771     }
772   }
773   else // Not visible.
774   {
775     mDisplayState = Toolkit::Popup::HIDING;
776     ClearKeyInputFocus();
777
778     // Restore the keyboard focus when popup is hidden.
779     if( mPreviousFocusedActor && mPreviousFocusedActor.IsKeyboardFocusable() )
780     {
781       Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
782       if( keyboardFocusManager )
783       {
784         keyboardFocusManager.SetCurrentFocusActor( mPreviousFocusedActor );
785       }
786     }
787   }
788
789   // Perform animation.
790   StartTransitionAnimation( display );
791 }
792
793 Toolkit::Popup::DisplayState Popup::GetDisplayState() const
794 {
795   return mDisplayState;
796 }
797
798 void Popup::LayoutPopup()
799 {
800   mLayoutDirty = false;
801
802   /* When animating in, we want to respect the origin applied to Self().
803    * For example, if zooming, not only will the final result be anchored to the
804    * selected point, but the zoom will originate from this point also.
805    *
806    * EG: ParentOrigin::TOP_LEFT, AnchorPoint::TOP_LEFT :
807    *
808    *       --------                --------
809    *       |X|                     |XXX|
810    *       |``        Animates     |XXX|
811    *       |             to:       |XXX|
812    *       |                       |````
813    *       |                       |
814    */
815   mPopupContainer.SetParentOrigin( Self().GetCurrentParentOrigin() );
816   mPopupContainer.SetAnchorPoint( Self().GetCurrentAnchorPoint() );
817
818   // If there is only a title, use less padding.
819   if( mTitle )
820   {
821     if( !mContent && !mFooter )
822     {
823       mTitle.SetPadding( DEFAULT_TITLE_ONLY_PADDING );
824     }
825     else
826     {
827       mTitle.SetPadding( DEFAULT_TITLE_PADDING );
828     }
829   }
830
831   // Allow derived classes to perform any layout they may need to do.
832   OnLayoutSetup();
833
834   // Update background visibility.
835   mPopupContainer.SetVisible( !( !mFooter && mPopupLayout.GetChildCount() == 0 ) );
836
837   // Create / destroy / position the tail as needed.
838   LayoutTail();
839
840   // Setup any layout and initialisation required for the selected animation.
841   LayoutAnimation();
842
843   RelayoutRequest();
844 }
845
846 void Popup::LayoutTail()
847 {
848   // Removes the tail actor.
849   if( mTailImage && mTailImage.GetParent() )
850   {
851     mTailImage.GetParent().Remove( mTailImage );
852     mTailImage.Reset();
853   }
854
855   if( !mTailVisible )
856   {
857     return;
858   }
859
860   const Vector3& parentOrigin = GetTailPosition();
861   Vector3 position;
862   std::string image;
863   Vector3 anchorPoint;
864
865   // depending on position of tail around ParentOrigin, a different tail image is used...
866   if( parentOrigin.y < Math::MACHINE_EPSILON_1 )
867   {
868     image = mTailUpImage;
869     anchorPoint = AnchorPoint::BOTTOM_CENTER;
870     position.y = mBackgroundBorder.top;
871   }
872   else if( parentOrigin.y > ( 1.0f - Math::MACHINE_EPSILON_1 ) )
873   {
874     image = mTailDownImage;
875     anchorPoint = AnchorPoint::TOP_CENTER;
876     position.y = - mBackgroundBorder.bottom;
877   }
878   else if( parentOrigin.x < Math::MACHINE_EPSILON_1 )
879   {
880     image = mTailLeftImage;
881     anchorPoint = AnchorPoint::CENTER_RIGHT;
882     position.x = mBackgroundBorder.left;
883   }
884   else if( parentOrigin.x > ( 1.0f - Math::MACHINE_EPSILON_1 ) )
885   {
886     image = mTailRightImage;
887     anchorPoint = AnchorPoint::CENTER_LEFT;
888     position.x = - mBackgroundBorder.right;
889   }
890
891   if( !image.empty() )
892   {
893     // Adds the tail actor.
894     mTailImage = Toolkit::ImageView::New( image );
895     mTailImage.SetName( "tailImage" );
896     mTailImage.SetParentOrigin( parentOrigin );
897     mTailImage.SetAnchorPoint( anchorPoint );
898     mTailImage.SetPosition( position );
899
900     if( mPopupBackgroundImage )
901     {
902       mPopupBackgroundImage.Add( mTailImage );
903     }
904   }
905 }
906
907 void Popup::SetContextualMode( Toolkit::Popup::ContextualMode mode )
908 {
909   mContextualMode = mode;
910   mLayoutDirty = true;
911 }
912
913 Toolkit::Popup::ContextualMode Popup::GetContextualMode() const
914 {
915   return mContextualMode;
916 }
917
918 Toolkit::Control Popup::CreateBacking()
919 {
920   Toolkit::Control backing = Control::New();
921   backing.SetProperty( Toolkit::Control::Property::BACKGROUND,
922                        Property::Map().Add( Toolkit::Visual::Property::TYPE, Toolkit::Visual::COLOR )
923                                       .Add( Toolkit::ColorVisual::Property::MIX_COLOR, Vector4( mBackingColor.r, mBackingColor.g, mBackingColor.b, 1.0f ) ) );
924   backing.SetName( "popupBacking" );
925
926   // Must always be positioned top-left of stage, regardless of parent.
927   backing.SetInheritPosition(false);
928
929   // Always the full size of the stage.
930   backing.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS );
931   backing.SetSize( Stage::GetCurrent().GetSize() );
932
933   // Catch events.
934   backing.SetSensitive( true );
935
936   // Default to being transparent.
937   backing.SetProperty( Actor::Property::COLOR_ALPHA, 0.0f );
938   backing.TouchSignal().Connect( this, &Popup::OnBackingTouched );
939   backing.WheelEventSignal().Connect( this, &Popup::OnBackingWheelEvent );
940   return backing;
941 }
942
943 Toolkit::Popup::TouchedOutsideSignalType& Popup::OutsideTouchedSignal()
944 {
945   return mTouchedOutsideSignal;
946 }
947
948 Toolkit::Popup::DisplayStateChangeSignalType& Popup::ShowingSignal()
949 {
950   return mShowingSignal;
951 }
952
953 Toolkit::Popup::DisplayStateChangeSignalType& Popup::ShownSignal()
954 {
955   return mShownSignal;
956 }
957
958 Toolkit::Popup::DisplayStateChangeSignalType& Popup::HidingSignal()
959 {
960   return mHidingSignal;
961 }
962
963 Toolkit::Popup::DisplayStateChangeSignalType& Popup::HiddenSignal()
964 {
965   return mHiddenSignal;
966 }
967
968 void Popup::SetTailVisibility( bool visible )
969 {
970   mTailVisible = visible;
971   mLayoutDirty = true;
972 }
973
974 const bool Popup::IsTailVisible() const
975 {
976   return mTailVisible;
977 }
978
979 void Popup::SetTailPosition( Vector3 position )
980 {
981   mTailPosition = position;
982   mLayoutDirty = true;
983 }
984
985 const Vector3& Popup::GetTailPosition() const
986 {
987   return mTailPosition;
988 }
989
990 void Popup::SetAnimationDuration( float duration )
991 {
992   mAnimationDuration = duration;
993   mLayoutDirty = true;
994 }
995
996 float Popup::GetAnimationDuration() const
997 {
998   return mAnimationDuration;
999 }
1000
1001 void Popup::SetAnimationMode( Toolkit::Popup::AnimationMode animationMode )
1002 {
1003   mAnimationMode = animationMode;
1004   mLayoutDirty = true;
1005 }
1006
1007 Toolkit::Popup::AnimationMode Popup::GetAnimationMode() const
1008 {
1009   return mAnimationMode;
1010 }
1011
1012 void Popup::SetEntryAnimationData( const Property::Map& map )
1013 {
1014   mEntryAnimationData.Clear();
1015   Scripting::NewAnimation( map, mEntryAnimationData );
1016 }
1017
1018 void Popup::SetExitAnimationData( const Property::Map& map )
1019 {
1020   mExitAnimationData.Clear();
1021   Scripting::NewAnimation( map, mExitAnimationData );
1022 }
1023
1024 void Popup::UpdateBackgroundPositionAndSize()
1025 {
1026   if( mPopupBackgroundImage )
1027   {
1028     mPopupBackgroundImage.SetResizePolicy( ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT, Dimension::ALL_DIMENSIONS );
1029     mPopupBackgroundImage.SetSizeModeFactor( Vector3( mBackgroundBorder.left + mBackgroundBorder.right, mBackgroundBorder.top + mBackgroundBorder.bottom, 0.0f ) );
1030
1031     // Adjust the position of the background so the transparent areas are set appropriately
1032     mPopupBackgroundImage.SetPosition( ( mBackgroundBorder.right - mBackgroundBorder.left ) * 0.5f, ( mBackgroundBorder.bottom - mBackgroundBorder.top ) * 0.5f );
1033   }
1034 }
1035
1036 void Popup::SetAutoHideDelay( int delay )
1037 {
1038   mAutoHideDelay = delay;
1039 }
1040
1041 int Popup::GetAutoHideDelay() const
1042 {
1043   return mAutoHideDelay;
1044 }
1045
1046 void Popup::SetBackingEnabled( bool enabled )
1047 {
1048   mBackingEnabled = enabled;
1049   mLayoutDirty = true;
1050 }
1051
1052 const bool Popup::IsBackingEnabled() const
1053 {
1054   return mBackingEnabled;
1055 }
1056
1057 void Popup::SetBackingColor( Vector4 color )
1058 {
1059   mBackingColor = color;
1060   mBacking.SetBackgroundColor( Vector4( color.r, color.g, color.b, 1.0f ) );
1061   mLayoutDirty = true;
1062 }
1063
1064 const Vector4& Popup::GetBackingColor() const
1065 {
1066   return mBackingColor;
1067 }
1068
1069 void Popup::SetTailUpImage( std::string image )
1070 {
1071   mTailUpImage = image;
1072   mLayoutDirty = true;
1073   LayoutTail();
1074 }
1075
1076 const std::string& Popup::GetTailUpImage() const
1077 {
1078   return mTailUpImage;
1079 }
1080
1081 void Popup::SetTailDownImage( std::string image )
1082 {
1083   mTailDownImage = image;
1084   mLayoutDirty = true;
1085   LayoutTail();
1086 }
1087
1088 const std::string& Popup::GetTailDownImage() const
1089 {
1090   return mTailDownImage;
1091 }
1092
1093 void Popup::SetTailLeftImage( std::string image )
1094 {
1095   mTailLeftImage = image;
1096   mLayoutDirty = true;
1097   LayoutTail();
1098 }
1099
1100 const std::string& Popup::GetTailLeftImage() const
1101 {
1102   return mTailLeftImage;
1103 }
1104
1105 void Popup::SetTailRightImage( std::string image )
1106 {
1107   mTailRightImage = image;
1108   mLayoutDirty = true;
1109   LayoutTail();
1110 }
1111
1112 const std::string& Popup::GetTailRightImage() const
1113 {
1114   return mTailRightImage;
1115 }
1116
1117 void Popup::SetTouchTransparent( bool enabled )
1118 {
1119   mTouchTransparent = enabled;
1120 }
1121
1122 const bool Popup::IsTouchTransparent() const
1123 {
1124   return mTouchTransparent;
1125 }
1126
1127 void Popup::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
1128 {
1129   Toolkit::Popup popup = Toolkit::Popup::DownCast( Dali::BaseHandle( object ) );
1130
1131   if ( popup )
1132   {
1133     Popup& popupImpl( GetImpl( popup ) );
1134
1135     switch ( propertyIndex )
1136     {
1137       case Toolkit::Popup::Property::TITLE:
1138       {
1139         Property::Map valueMap;
1140         if( value.Get( valueMap ) )
1141         {
1142           popupImpl.SetTitle( Scripting::NewActor( valueMap ) );
1143         }
1144         break;
1145       }
1146       case Toolkit::Popup::Property::CONTENT:
1147       {
1148         Property::Map valueMap;
1149         if( value.Get( valueMap ) )
1150         {
1151           popupImpl.SetContent( Scripting::NewActor( valueMap ) );
1152         }
1153         break;
1154       }
1155       case Toolkit::Popup::Property::FOOTER:
1156       {
1157         Property::Map valueMap;
1158         if( value.Get( valueMap ) )
1159         {
1160           popupImpl.SetFooter( Scripting::NewActor( valueMap ) );
1161         }
1162         break;
1163       }
1164       case Toolkit::Popup::Property::DISPLAY_STATE:
1165       {
1166         std::string valueString;
1167         if( value.Get( valueString ) )
1168         {
1169           Toolkit::Popup::DisplayState displayState( Toolkit::Popup::HIDDEN );
1170           if( Scripting::GetEnumeration< Toolkit::Popup::DisplayState >( valueString.c_str(), DisplayStateTable, DisplayStateTableCount, displayState ) )
1171           {
1172             popupImpl.SetDisplayState( displayState );
1173           }
1174         }
1175         break;
1176       }
1177       case Toolkit::Popup::Property::TOUCH_TRANSPARENT:
1178       {
1179         bool valueBool;
1180         if( value.Get( valueBool ) )
1181         {
1182           popupImpl.SetTouchTransparent( valueBool );
1183         }
1184         break;
1185       }
1186       case Toolkit::Popup::Property::TAIL_VISIBILITY:
1187       {
1188         bool valueBool;
1189         if( value.Get( valueBool ) )
1190         {
1191           popupImpl.SetTailVisibility( valueBool );
1192         }
1193         break;
1194       }
1195       case Toolkit::Popup::Property::TAIL_POSITION:
1196       {
1197         Vector3 valueVector3;
1198         if( value.Get( valueVector3 ) )
1199         {
1200           popupImpl.SetTailPosition( valueVector3 );
1201         }
1202         break;
1203       }
1204       case Toolkit::Popup::Property::CONTEXTUAL_MODE:
1205       {
1206         std::string valueString;
1207         if( value.Get( valueString ) )
1208         {
1209           Toolkit::Popup::ContextualMode contextualMode( Toolkit::Popup::BELOW );
1210           if( Scripting::GetEnumeration< Toolkit::Popup::ContextualMode >( valueString.c_str(), ContextualModeTable, ContextualModeTableCount, contextualMode ) )
1211           {
1212             popupImpl.SetContextualMode( contextualMode );
1213           }
1214         }
1215         break;
1216       }
1217       case Toolkit::Popup::Property::ANIMATION_DURATION:
1218       {
1219         float valueFloat;
1220         if( value.Get( valueFloat ) )
1221         {
1222           popupImpl.SetAnimationDuration( valueFloat );
1223         }
1224         break;
1225       }
1226       case Toolkit::Popup::Property::ANIMATION_MODE:
1227       {
1228         std::string valueString;
1229         if( value.Get( valueString ) )
1230         {
1231           Toolkit::Popup::AnimationMode animationMode( Toolkit::Popup::FADE );
1232           if( Scripting::GetEnumeration< Toolkit::Popup::AnimationMode >( valueString.c_str(), AnimationModeTable, AnimationModeTableCount, animationMode ) )
1233           {
1234             popupImpl.SetAnimationMode( animationMode );
1235           }
1236         }
1237         break;
1238       }
1239       case Toolkit::Popup::Property::ENTRY_ANIMATION:
1240       {
1241         Property::Map valueMap;
1242         if( value.Get( valueMap ) )
1243         {
1244           popupImpl.SetEntryAnimationData( valueMap );
1245         }
1246         break;
1247       }
1248       case Toolkit::Popup::Property::EXIT_ANIMATION:
1249       {
1250         Property::Map valueMap;
1251         if( value.Get( valueMap ) )
1252         {
1253           popupImpl.SetExitAnimationData( valueMap );
1254         }
1255         break;
1256       }
1257       case Toolkit::Popup::Property::AUTO_HIDE_DELAY:
1258       {
1259         int valueInt;
1260         if( value.Get( valueInt ) )
1261         {
1262           popupImpl.SetAutoHideDelay( valueInt );
1263         }
1264         break;
1265       }
1266       case Toolkit::Popup::Property::BACKING_ENABLED:
1267       {
1268         bool valueBool;
1269         if( value.Get( valueBool ) )
1270         {
1271           popupImpl.SetBackingEnabled( valueBool );
1272         }
1273         break;
1274       }
1275       case Toolkit::Popup::Property::BACKING_COLOR:
1276       {
1277         Vector4 valueVector4;
1278         if( value.Get( valueVector4 ) )
1279         {
1280           popupImpl.SetBackingColor( valueVector4 );
1281         }
1282         break;
1283       }
1284       case Toolkit::Popup::Property::POPUP_BACKGROUND_IMAGE:
1285       {
1286         std::string valueString;
1287         if( value.Get( valueString ) )
1288         {
1289           Toolkit::ImageView actor = Toolkit::ImageView::New( valueString );
1290           popupImpl.SetPopupBackgroundImage( actor );
1291         }
1292         break;
1293       }
1294       case Toolkit::Popup::Property::POPUP_BACKGROUND_BORDER:
1295       {
1296         bool valueUpdated = false;
1297
1298         Vector4 valueVector4;
1299         if( value.Get( popupImpl.mBackgroundBorder ) )
1300         {
1301           valueUpdated = true;
1302         }
1303         else if( value.Get( valueVector4 ) )
1304         {
1305           popupImpl.mBackgroundBorder.left   = valueVector4.x;
1306           popupImpl.mBackgroundBorder.right  = valueVector4.y;
1307           popupImpl.mBackgroundBorder.bottom = valueVector4.z;
1308           popupImpl.mBackgroundBorder.top    = valueVector4.w;
1309           valueUpdated = true;
1310         }
1311
1312         if( valueUpdated )
1313         {
1314           popupImpl.LayoutTail(); // Update the tail if required
1315           popupImpl.UpdateBackgroundPositionAndSize(); // Update the background's size and position
1316         }
1317         break;
1318       }
1319       case Toolkit::Popup::Property::TAIL_UP_IMAGE:
1320       {
1321         std::string valueString;
1322         if( value.Get( valueString ) )
1323         {
1324           popupImpl.SetTailUpImage( valueString );
1325         }
1326         break;
1327       }
1328       case Toolkit::Popup::Property::TAIL_DOWN_IMAGE:
1329       {
1330         std::string valueString;
1331         if( value.Get( valueString ) )
1332         {
1333           popupImpl.SetTailDownImage( valueString );
1334         }
1335         break;
1336       }
1337       case Toolkit::Popup::Property::TAIL_LEFT_IMAGE:
1338       {
1339         std::string valueString;
1340         if( value.Get( valueString ) )
1341         {
1342           popupImpl.SetTailLeftImage( valueString );
1343         }
1344         break;
1345       }
1346       case Toolkit::Popup::Property::TAIL_RIGHT_IMAGE:
1347       {
1348         std::string valueString;
1349         if( value.Get( valueString ) )
1350         {
1351           popupImpl.SetTailRightImage( valueString );
1352         }
1353         break;
1354       }
1355     }
1356   }
1357 }
1358
1359 Property::Value Popup::GetProperty( BaseObject* object, Property::Index propertyIndex )
1360 {
1361   Property::Value value;
1362
1363   Toolkit::Popup popup = Toolkit::Popup::DownCast( Dali::BaseHandle( object ) );
1364
1365   if ( popup )
1366   {
1367     Popup& popupImpl( GetImpl( popup ) );
1368
1369     switch ( propertyIndex )
1370     {
1371       case Toolkit::Popup::Property::TITLE:
1372       {
1373         Property::Map map;
1374         Scripting::CreatePropertyMap( popupImpl.GetTitle(), map );
1375         value = map;
1376         break;
1377       }
1378       case Toolkit::Popup::Property::CONTENT:
1379       {
1380         Property::Map map;
1381         Scripting::CreatePropertyMap( popupImpl.GetContent(), map );
1382         value = map;
1383         break;
1384       }
1385       case Toolkit::Popup::Property::FOOTER:
1386       {
1387         Property::Map map;
1388         Scripting::CreatePropertyMap( popupImpl.GetFooter(), map );
1389         value = map;
1390         break;
1391       }
1392       case Toolkit::Popup::Property::DISPLAY_STATE:
1393       {
1394         value = Scripting::GetLinearEnumerationName< Toolkit::Popup::DisplayState >( popupImpl.GetDisplayState(), DisplayStateTable, DisplayStateTableCount );
1395         break;
1396       }
1397       case Toolkit::Popup::Property::TOUCH_TRANSPARENT:
1398       {
1399         value = popupImpl.IsTouchTransparent();
1400         break;
1401       }
1402       case Toolkit::Popup::Property::TAIL_VISIBILITY:
1403       {
1404         value = popupImpl.IsTailVisible();
1405         break;
1406       }
1407       case Toolkit::Popup::Property::TAIL_POSITION:
1408       {
1409         value = popupImpl.GetTailPosition();
1410         break;
1411       }
1412       case Toolkit::Popup::Property::CONTEXTUAL_MODE:
1413       {
1414         value = Scripting::GetLinearEnumerationName< Toolkit::Popup::ContextualMode >( popupImpl.GetContextualMode(), ContextualModeTable, ContextualModeTableCount );
1415         break;
1416       }
1417       case Toolkit::Popup::Property::ANIMATION_DURATION:
1418       {
1419         value = popupImpl.GetAnimationDuration();
1420         break;
1421       }
1422       case Toolkit::Popup::Property::ANIMATION_MODE:
1423       {
1424         value = Scripting::GetLinearEnumerationName< Toolkit::Popup::AnimationMode >( popupImpl.GetAnimationMode(), AnimationModeTable, AnimationModeTableCount );
1425         break;
1426       }
1427       case Toolkit::Popup::Property::ENTRY_ANIMATION:
1428       {
1429         // Note: Cannot retrieve property map from animation.
1430         Property::Map map;
1431         value = map;
1432         break;
1433       }
1434       case Toolkit::Popup::Property::EXIT_ANIMATION:
1435       {
1436         // Note: Cannot retrieve property map from animation.
1437         Property::Map map;
1438         value = map;
1439         break;
1440       }
1441       case Toolkit::Popup::Property::AUTO_HIDE_DELAY:
1442       {
1443         value = popupImpl.GetAutoHideDelay();
1444         break;
1445       }
1446       case Toolkit::Popup::Property::BACKING_ENABLED:
1447       {
1448         value = popupImpl.IsBackingEnabled();
1449         break;
1450       }
1451       case Toolkit::Popup::Property::BACKING_COLOR:
1452       {
1453         value = popupImpl.GetBackingColor();
1454         break;
1455       }
1456       case Toolkit::Popup::Property::POPUP_BACKGROUND_IMAGE:
1457       {
1458         Toolkit::ImageView imageView = Toolkit::ImageView::DownCast( popupImpl.GetPopupBackgroundImage() );
1459         if( imageView )
1460         {
1461           value = imageView.GetProperty( Toolkit::ImageView::Property::IMAGE );
1462         }
1463         break;
1464       }
1465       case Toolkit::Popup::Property::POPUP_BACKGROUND_BORDER:
1466       {
1467         value = popupImpl.mBackgroundBorder;
1468         break;
1469       }
1470       case Toolkit::Popup::Property::TAIL_UP_IMAGE:
1471       {
1472         value = popupImpl.GetTailUpImage();
1473         break;
1474       }
1475       case Toolkit::Popup::Property::TAIL_DOWN_IMAGE:
1476       {
1477         value = popupImpl.GetTailDownImage();
1478         break;
1479       }
1480       case Toolkit::Popup::Property::TAIL_LEFT_IMAGE:
1481       {
1482         value = popupImpl.GetTailLeftImage();
1483         break;
1484       }
1485       case Toolkit::Popup::Property::TAIL_RIGHT_IMAGE:
1486       {
1487         value = popupImpl.GetTailRightImage();
1488         break;
1489       }
1490     }
1491   }
1492
1493   return value;
1494 }
1495
1496 bool Popup::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1497 {
1498   Dali::BaseHandle handle( object );
1499
1500   bool connected( true );
1501   Toolkit::Popup popup = Toolkit::Popup::DownCast( handle );
1502
1503   if( 0 == strcmp( signalName.c_str(), SIGNAL_TOUCHED_OUTSIDE ) )
1504   {
1505     popup.OutsideTouchedSignal().Connect( tracker, functor );
1506   }
1507   else if( 0 == strcmp( signalName.c_str(), SIGNAL_SHOWING ) )
1508   {
1509     popup.ShowingSignal().Connect( tracker, functor );
1510   }
1511   else if( 0 == strcmp( signalName.c_str(), SIGNAL_SHOWN ) )
1512   {
1513     popup.ShownSignal().Connect( tracker, functor );
1514   }
1515   else if( 0 == strcmp( signalName.c_str(), SIGNAL_HIDING ) )
1516   {
1517     popup.HidingSignal().Connect( tracker, functor );
1518   }
1519   else if( 0 == strcmp( signalName.c_str(), SIGNAL_HIDDEN ) )
1520   {
1521     popup.HiddenSignal().Connect( tracker, functor );
1522   }
1523   else
1524   {
1525     // signalName does not match any signal
1526     connected = false;
1527   }
1528
1529   return connected;
1530 }
1531
1532 bool Popup::OnBackingTouched( Actor actor, const TouchData& touch )
1533 {
1534   // Allow events to pass through if touch transparency is enabled.
1535   if( mTouchTransparent )
1536   {
1537     return false;
1538   }
1539
1540   if( touch.GetPointCount() > 0 )
1541   {
1542     if( touch.GetState( 0 ) == PointState::DOWN )
1543     {
1544       // Guard against destruction during signal emission.
1545       Toolkit::Popup handle( GetOwner() );
1546
1547       mTouchedOutsideSignal.Emit();
1548     }
1549   }
1550
1551   // Block anything behind backing becoming touched.
1552   mLayer.SetTouchConsumed( true );
1553   return true;
1554 }
1555
1556 bool Popup::OnBackingWheelEvent( Actor actor, const WheelEvent& event )
1557 {
1558   // Allow events to pass through if touch transparency is enabled.
1559   if( mTouchTransparent )
1560   {
1561     return false;
1562   }
1563
1564   // Consume wheel event in dimmed backing actor.
1565   mLayer.SetTouchConsumed( true );
1566   return true;
1567 }
1568
1569 bool Popup::OnDialogTouched( Actor actor, const TouchData& touch )
1570 {
1571   // Allow events to pass through if touch transparency is enabled.
1572   if( mTouchTransparent )
1573   {
1574     return false;
1575   }
1576
1577   // Consume event (stops backing actor receiving touch events)
1578   mLayer.SetTouchConsumed( true );
1579   return true;
1580 }
1581
1582 void Popup::OnStageDisconnection()
1583 {
1584   auto p = Dali::Accessibility::Accessible::Get(Self());
1585   Accessibility::Bridge::GetCurrentBridge()->RemovePopup( p );
1586   Control::OnStageDisconnection();
1587 }
1588
1589 void Popup::OnStageConnection( int depth )
1590 {
1591   mLayoutDirty = true;
1592   RelayoutRequest();
1593
1594   Control::OnStageConnection( depth );
1595   auto p = Dali::Accessibility::Accessible::Get(Self());
1596   Accessibility::Bridge::GetCurrentBridge()->AddPopup( p );
1597 }
1598
1599 void Popup::OnChildAdd( Actor& child )
1600 {
1601   // Re-parent any children added by user to the body layer.
1602   if( mAlterAddedChild )
1603   {
1604     SetContent( child );
1605   }
1606   else
1607   {
1608     mLayoutDirty = true;
1609     RelayoutRequest();
1610   }
1611
1612   Control::OnChildAdd( child );
1613 }
1614
1615 void Popup::LayoutContext( const Vector2& size )
1616 {
1617   // Do nothing if not in a contextual mode (or there is no parent context).
1618   Actor self = Self();
1619   Actor parent = self.GetParent();
1620   if( ( mContextualMode == Toolkit::Popup::NON_CONTEXTUAL ) || !parent )
1621   {
1622     return;
1623   }
1624
1625   mPopupContainer.SetParentOrigin( ParentOrigin::CENTER );
1626   // We always anchor to the CENTER, rather than a different anchor point for each contextual
1627   // mode to allow code-reuse of the bound checking code (for maintainability).
1628   mPopupContainer.SetAnchorPoint( AnchorPoint::CENTER );
1629
1630   // Setup with some pre-calculations for speed.
1631   Vector3 halfStageSize( Stage().GetCurrent().GetSize() / 2.0f );
1632   Vector3 parentPosition( parent.GetCurrentPosition() );
1633   Vector2 halfSize( size / 2.0f );
1634   Vector2 halfParentSize( parent.GetRelayoutSize( Dimension::WIDTH ) / 2.0f, parent.GetRelayoutSize( Dimension::HEIGHT ) / 2.0f );
1635   Vector3 newPosition( Vector3::ZERO );
1636
1637   // Perform different positioning based on the specified contextual layout mode.
1638   switch( mContextualMode )
1639   {
1640     case Toolkit::Popup::BELOW:
1641     {
1642       newPosition.x += halfSize.x - halfParentSize.x;
1643       newPosition.y += halfSize.y + halfParentSize.y + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.y;
1644       break;
1645     }
1646     case Toolkit::Popup::ABOVE:
1647     {
1648       newPosition.x += halfSize.x - halfParentSize.x;
1649       newPosition.y -= halfSize.y + halfParentSize.y + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.y;
1650       break;
1651     }
1652     case Toolkit::Popup::RIGHT:
1653     {
1654       newPosition.x += halfSize.x + halfParentSize.x + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.x;
1655       newPosition.y += halfSize.y - halfParentSize.y;
1656       break;
1657     }
1658     case Toolkit::Popup::LEFT:
1659     {
1660       newPosition.x -= halfSize.x + halfParentSize.x + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.x;
1661       newPosition.y += halfSize.y - halfParentSize.y;
1662       break;
1663     }
1664     case Toolkit::Popup::NON_CONTEXTUAL:
1665     {
1666       // Code won't reach here (caught earlier).
1667       break;
1668     }
1669   }
1670
1671   // On-screen position checking.
1672   // Check new position is not too far right. If so, correct it.
1673   // Note: Check for right rather than left first, so if popup is too wide, the left check overrides
1674   // the right check and we at least see the left portion of the popup (as this is more useful).
1675   if( newPosition.x >= ( halfStageSize.x - parentPosition.x - halfSize.x - DEFAULT_CONTEXTUAL_STAGE_BORDER.x ) )
1676   {
1677     newPosition.x = halfStageSize.x - parentPosition.x - halfSize.x - DEFAULT_CONTEXTUAL_STAGE_BORDER.x;
1678   }
1679   // Check new position is not too far left. If so, correct it.
1680   if( newPosition.x < halfSize.x - ( parentPosition.x + halfStageSize.x ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.x )
1681   {
1682     newPosition.x = halfSize.x - ( parentPosition.x + halfStageSize.x ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.x;// - parentSize.x;
1683   }
1684   // Check new position is not too far down. If so, correct it.
1685   if( newPosition.y >= ( halfStageSize.y - parentPosition.y - halfSize.y - DEFAULT_CONTEXTUAL_STAGE_BORDER.y ) )
1686   {
1687     newPosition.y = halfStageSize.y - parentPosition.y - halfSize.y - DEFAULT_CONTEXTUAL_STAGE_BORDER.y;
1688   }
1689   // Check new position is not too far up. If so, correct it.
1690   if( newPosition.y < halfSize.y - ( parentPosition.y + halfStageSize.y ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.y )
1691   {
1692     newPosition.y = halfSize.y - ( parentPosition.y + halfStageSize.y ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.y;
1693   }
1694
1695   // Set the final position.
1696   mPopupContainer.SetPosition( newPosition );
1697 }
1698
1699 void Popup::OnRelayout( const Vector2& size, RelayoutContainer& container )
1700 {
1701   Vector2 useSize( size );
1702
1703   // Use the Popup layouts size, unless requested to use a fixed size.
1704   // In which case take the size set for the Popup itself.
1705   ResizePolicy::Type widthPolicy = Self().GetResizePolicy( Dimension::WIDTH );
1706   ResizePolicy::Type heightPolicy = Self().GetResizePolicy( Dimension::HEIGHT );
1707
1708   // Width calculations:
1709   if( widthPolicy == ResizePolicy::USE_NATURAL_SIZE || widthPolicy == ResizePolicy::FIT_TO_CHILDREN )
1710   {
1711     // If we using a child-based policy, take the size from the popup layout.
1712     mPopupLayout.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::WIDTH );
1713     useSize.width = mPopupLayout.GetRelayoutSize( Dimension::WIDTH );
1714
1715     mPopupLayout.SetFitWidth( 0u );
1716   }
1717   else
1718   {
1719     // If we using a parent-based policy, take the size from the popup object itself (self).
1720     mPopupLayout.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::WIDTH );
1721
1722     mPopupLayout.SetFixedWidth( 0u, useSize.width );
1723   }
1724
1725   // Height calculations:
1726   // Title: Let the title be as high as it needs to be.
1727   mPopupLayout.SetFitHeight( 0u );
1728
1729   // Footer: Convert the footer's resize policy to a TableView row policy.
1730   if( mFooter )
1731   {
1732     ResizePolicy::Type footerHeightPolicy = mFooter.GetResizePolicy( Dimension::HEIGHT );
1733     if( ( footerHeightPolicy == ResizePolicy::USE_NATURAL_SIZE ) ||
1734         ( footerHeightPolicy == ResizePolicy::FIT_TO_CHILDREN ) )
1735     {
1736       mPopupLayout.SetFitHeight( 2u );
1737     }
1738     else if( footerHeightPolicy == ResizePolicy::FIXED )
1739     {
1740       mPopupLayout.SetFixedHeight( 2u, mFooter.GetRelayoutSize( Dimension::HEIGHT) );
1741     }
1742     else
1743     {
1744       mPopupLayout.SetRelativeHeight( 2u, 1.0f );
1745     }
1746   }
1747   else
1748   {
1749     mPopupLayout.SetFixedHeight( 2u, 0.0f );
1750   }
1751
1752   // Popup contents: Adjust the tableview's policies based on the popup's policies.
1753   if( heightPolicy == ResizePolicy::USE_NATURAL_SIZE || heightPolicy == ResizePolicy::FIT_TO_CHILDREN )
1754   {
1755     mPopupLayout.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
1756
1757     // Let both the contents expand as necessary.
1758     mPopupLayout.SetFitHeight( 1u );
1759     useSize.height = mPopupLayout.GetRelayoutSize( Dimension::HEIGHT );
1760   }
1761   else
1762   {
1763     mPopupLayout.SetResizePolicy( heightPolicy, Dimension::HEIGHT );
1764
1765     // Let the content expand to fill the remaining space.
1766     mPopupLayout.SetRelativeHeight( 1u, 1.0f );
1767     mPopupLayout.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::HEIGHT );
1768   }
1769
1770   // Relayout the popup-layout to give it it's new size this frame.
1771   container.Add( mPopupLayout, useSize );
1772
1773   if( mContent )
1774   {
1775     container.Add( mContent, Vector2( mContent.GetRelayoutSize( Dimension::WIDTH ), mContent.GetRelayoutSize( Dimension::HEIGHT ) ) );
1776   }
1777
1778   // Perform contextual layout setup if required.
1779   // This is done each time in case the parent moves.
1780   // This will have no effect if no contextual mode is selected.
1781   LayoutContext( useSize );
1782 }
1783
1784 void Popup::OnSetResizePolicy( ResizePolicy::Type policy, Dimension::Type dimension )
1785 {
1786   // To get the popup to emulate fit-to-children, we need to actually set use-natural-size.
1787   if( ( dimension & Dimension::HEIGHT ) && ( policy == ResizePolicy::FIT_TO_CHILDREN ) )
1788   {
1789     Self().SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
1790   }
1791
1792   mLayoutDirty = true;
1793   return;
1794 }
1795
1796 Vector3 Popup::GetNaturalSize()
1797 {
1798   return mPopupLayout.GetNaturalSize();
1799 }
1800
1801 float Popup::GetHeightForWidth( float width )
1802 {
1803   return mPopupLayout.GetHeightForWidth( width );
1804 }
1805
1806 float Popup::GetWidthForHeight( float height )
1807 {
1808   return mPopupLayout.GetWidthForHeight( height );
1809 }
1810
1811 bool Popup::OnKeyEvent( const KeyEvent& event )
1812 {
1813   // Allow events to pass through if touch transparency is enabled.
1814   if( mTouchTransparent )
1815   {
1816     return false;
1817   }
1818
1819   bool consumed = false;
1820
1821   if( event.state == KeyEvent::Down )
1822   {
1823     if (event.keyCode == Dali::DALI_KEY_ESCAPE || event.keyCode == Dali::DALI_KEY_BACK)
1824     {
1825       SetDisplayState( Toolkit::Popup::HIDDEN );
1826       consumed = true;
1827     }
1828   }
1829
1830   return consumed;
1831 }
1832
1833 void Popup::AddFocusableChildrenRecursive( Actor parent, std::vector< Actor >& focusableActors )
1834 {
1835   if( parent )
1836   {
1837     Toolkit::Control control = Toolkit::Control::DownCast( parent );
1838     bool layoutControl = control && GetImplementation( control ).IsKeyboardNavigationSupported();
1839
1840     if( parent.IsKeyboardFocusable() || layoutControl )
1841     {
1842       focusableActors.push_back( parent );
1843
1844       if( !layoutControl )
1845       {
1846         for( unsigned int i = 0, numberChildren = parent.GetChildCount(); i < numberChildren; ++i )
1847         {
1848           Actor child( parent.GetChildAt( i ) );
1849           AddFocusableChildrenRecursive( child, focusableActors );
1850         }
1851       }
1852     }
1853   }
1854 }
1855
1856 void Popup::AddFocusableChildren( Actor parent, std::vector< Actor >& focusableActors )
1857 {
1858   if( parent )
1859   {
1860     Toolkit::Control control = Toolkit::Control::DownCast( parent );
1861     if( !GetImplementation( control ).IsKeyboardNavigationSupported() )
1862     {
1863       for( unsigned int i = 0, numberChildren = parent.GetChildCount(); i < numberChildren; ++i )
1864       {
1865         Actor child( parent.GetChildAt( i ) );
1866         AddFocusableChildrenRecursive( child, focusableActors );
1867       }
1868     }
1869     else
1870     {
1871       focusableActors.push_back( parent );
1872     }
1873   }
1874 }
1875
1876 Actor Popup::GetNextKeyboardFocusableActor( Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled )
1877 {
1878   std::string currentStr;
1879   if( currentFocusedActor )
1880   {
1881     currentStr = currentFocusedActor.GetName();
1882   }
1883
1884   Actor nextFocusableActor( currentFocusedActor );
1885   Actor currentFocusGroup;
1886   if( currentFocusedActor )
1887   {
1888     currentFocusGroup = KeyboardFocusManager::Get().GetFocusGroup( currentFocusedActor );
1889   }
1890
1891   // TODO: Needs to be optimised
1892   // The following statement checks that if we have a current focused actor, then the current focus group is not the popup content or footer.
1893   // This is to detect if the focus is currently outside the popup, and if so, move it inside.
1894   if( !currentFocusedActor ||
1895     ( currentFocusedActor && ( ( !mContent || ( currentFocusGroup != mContent ) ) && ( !mFooter || ( currentFocusGroup != mFooter ) ) ) ) )
1896   {
1897     // The current focused actor is not within popup.
1898     if( mContent && mContent.IsKeyboardFocusable() )
1899     {
1900       // If the content is focusable, move the focus to the content.
1901       nextFocusableActor = mContent;
1902     }
1903     else if( mFooter && mFooter.IsKeyboardFocusable() )
1904     {
1905       // If the footer is focusable, move the focus to the footer.
1906       nextFocusableActor = mFooter;
1907     }
1908   }
1909   else
1910   {
1911     // Rebuild the focus chain because controls or content can be added or removed dynamically
1912     std::vector< Actor > focusableActors;
1913
1914     AddFocusableChildren( mContent, focusableActors );
1915     AddFocusableChildren( mFooter, focusableActors );
1916
1917     std::vector< Actor >::iterator endIterator = focusableActors.end();
1918     std::vector< Actor >::iterator currentIterator = focusableActors.begin();
1919     for( std::vector< Actor >::iterator iterator = focusableActors.begin(); iterator != endIterator; ++iterator )
1920     {
1921       if( currentFocusedActor == *iterator )
1922       {
1923         currentIterator = iterator;
1924       }
1925     }
1926
1927     if( currentIterator != endIterator )
1928     {
1929       switch( direction )
1930       {
1931         case Toolkit::Control::KeyboardFocus::LEFT:
1932         {
1933           if( currentIterator == focusableActors.begin() )
1934           {
1935             nextFocusableActor = *( endIterator - 1 );
1936           }
1937           else
1938           {
1939             nextFocusableActor = *( currentIterator - 1 );
1940           }
1941           break;
1942         }
1943         case Toolkit::Control::KeyboardFocus::RIGHT:
1944         {
1945           if( currentIterator == endIterator - 1 )
1946           {
1947             nextFocusableActor = *( focusableActors.begin() );
1948           }
1949           else
1950           {
1951             nextFocusableActor = *( currentIterator + 1 );
1952           }
1953           break;
1954         }
1955
1956         case Toolkit::Control::KeyboardFocus::UP:
1957         {
1958           nextFocusableActor = *(  focusableActors.begin() );
1959           break;
1960         }
1961
1962         case Toolkit::Control::KeyboardFocus::DOWN:
1963         {
1964           nextFocusableActor = *( endIterator - 1 );
1965           break;
1966         }
1967
1968         default:
1969         {
1970           break;
1971         }
1972       }
1973
1974       if( !nextFocusableActor )
1975       {
1976         DALI_LOG_WARNING( "Can not decide next focusable actor\n" );
1977       }
1978     }
1979   }
1980
1981   return nextFocusableActor;
1982 }
1983
1984
1985 } // namespace Internal
1986
1987 } // namespace Toolkit
1988
1989 } // namespace Dali