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