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