Supported position/size for underlay video rendering
[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   backing.SetAnchorPoint( AnchorPoint::TOP_LEFT );
923
924   // Always the full size of the stage.
925   backing.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS );
926   backing.SetSize( Stage::GetCurrent().GetSize() );
927
928   // Catch events.
929   backing.SetSensitive( true );
930
931   // Default to being transparent.
932   backing.SetProperty( Actor::Property::COLOR_ALPHA, 0.0f );
933   backing.TouchSignal().Connect( this, &Popup::OnBackingTouched );
934   backing.WheelEventSignal().Connect( this, &Popup::OnBackingWheelEvent );
935   return backing;
936 }
937
938 Toolkit::Popup::TouchedOutsideSignalType& Popup::OutsideTouchedSignal()
939 {
940   return mTouchedOutsideSignal;
941 }
942
943 Toolkit::Popup::DisplayStateChangeSignalType& Popup::ShowingSignal()
944 {
945   return mShowingSignal;
946 }
947
948 Toolkit::Popup::DisplayStateChangeSignalType& Popup::ShownSignal()
949 {
950   return mShownSignal;
951 }
952
953 Toolkit::Popup::DisplayStateChangeSignalType& Popup::HidingSignal()
954 {
955   return mHidingSignal;
956 }
957
958 Toolkit::Popup::DisplayStateChangeSignalType& Popup::HiddenSignal()
959 {
960   return mHiddenSignal;
961 }
962
963 void Popup::SetTailVisibility( bool visible )
964 {
965   mTailVisible = visible;
966   mLayoutDirty = true;
967 }
968
969 const bool Popup::IsTailVisible() const
970 {
971   return mTailVisible;
972 }
973
974 void Popup::SetTailPosition( Vector3 position )
975 {
976   mTailPosition = position;
977   mLayoutDirty = true;
978 }
979
980 const Vector3& Popup::GetTailPosition() const
981 {
982   return mTailPosition;
983 }
984
985 void Popup::SetAnimationDuration( float duration )
986 {
987   mAnimationDuration = duration;
988   mLayoutDirty = true;
989 }
990
991 float Popup::GetAnimationDuration() const
992 {
993   return mAnimationDuration;
994 }
995
996 void Popup::SetAnimationMode( Toolkit::Popup::AnimationMode animationMode )
997 {
998   mAnimationMode = animationMode;
999   mLayoutDirty = true;
1000 }
1001
1002 Toolkit::Popup::AnimationMode Popup::GetAnimationMode() const
1003 {
1004   return mAnimationMode;
1005 }
1006
1007 void Popup::SetEntryAnimationData( const Property::Map& map )
1008 {
1009   mEntryAnimationData.Clear();
1010   Scripting::NewAnimation( map, mEntryAnimationData );
1011 }
1012
1013 void Popup::SetExitAnimationData( const Property::Map& map )
1014 {
1015   mExitAnimationData.Clear();
1016   Scripting::NewAnimation( map, mExitAnimationData );
1017 }
1018
1019 void Popup::UpdateBackgroundPositionAndSize()
1020 {
1021   if( mPopupBackgroundImage )
1022   {
1023     mPopupBackgroundImage.SetResizePolicy( ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT, Dimension::ALL_DIMENSIONS );
1024     mPopupBackgroundImage.SetSizeModeFactor( Vector3( mBackgroundBorder.left + mBackgroundBorder.right, mBackgroundBorder.top + mBackgroundBorder.bottom, 0.0f ) );
1025
1026     // Adjust the position of the background so the transparent areas are set appropriately
1027     mPopupBackgroundImage.SetPosition( ( mBackgroundBorder.right - mBackgroundBorder.left ) * 0.5f, ( mBackgroundBorder.bottom - mBackgroundBorder.top ) * 0.5f );
1028   }
1029 }
1030
1031 void Popup::SetAutoHideDelay( int delay )
1032 {
1033   mAutoHideDelay = delay;
1034 }
1035
1036 int Popup::GetAutoHideDelay() const
1037 {
1038   return mAutoHideDelay;
1039 }
1040
1041 void Popup::SetBackingEnabled( bool enabled )
1042 {
1043   mBackingEnabled = enabled;
1044   mLayoutDirty = true;
1045 }
1046
1047 const bool Popup::IsBackingEnabled() const
1048 {
1049   return mBackingEnabled;
1050 }
1051
1052 void Popup::SetBackingColor( Vector4 color )
1053 {
1054   mBackingColor = color;
1055   mBacking.SetBackgroundColor( Vector4( color.r, color.g, color.b, 1.0f ) );
1056   mLayoutDirty = true;
1057 }
1058
1059 const Vector4& Popup::GetBackingColor() const
1060 {
1061   return mBackingColor;
1062 }
1063
1064 void Popup::SetTailUpImage( std::string image )
1065 {
1066   mTailUpImage = image;
1067   mLayoutDirty = true;
1068   LayoutTail();
1069 }
1070
1071 const std::string& Popup::GetTailUpImage() const
1072 {
1073   return mTailUpImage;
1074 }
1075
1076 void Popup::SetTailDownImage( std::string image )
1077 {
1078   mTailDownImage = image;
1079   mLayoutDirty = true;
1080   LayoutTail();
1081 }
1082
1083 const std::string& Popup::GetTailDownImage() const
1084 {
1085   return mTailDownImage;
1086 }
1087
1088 void Popup::SetTailLeftImage( std::string image )
1089 {
1090   mTailLeftImage = image;
1091   mLayoutDirty = true;
1092   LayoutTail();
1093 }
1094
1095 const std::string& Popup::GetTailLeftImage() const
1096 {
1097   return mTailLeftImage;
1098 }
1099
1100 void Popup::SetTailRightImage( std::string image )
1101 {
1102   mTailRightImage = image;
1103   mLayoutDirty = true;
1104   LayoutTail();
1105 }
1106
1107 const std::string& Popup::GetTailRightImage() const
1108 {
1109   return mTailRightImage;
1110 }
1111
1112 void Popup::SetTouchTransparent( bool enabled )
1113 {
1114   mTouchTransparent = enabled;
1115 }
1116
1117 const bool Popup::IsTouchTransparent() const
1118 {
1119   return mTouchTransparent;
1120 }
1121
1122 void Popup::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value )
1123 {
1124   Toolkit::Popup popup = Toolkit::Popup::DownCast( Dali::BaseHandle( object ) );
1125
1126   if ( popup )
1127   {
1128     Popup& popupImpl( GetImpl( popup ) );
1129
1130     switch ( propertyIndex )
1131     {
1132       case Toolkit::Popup::Property::TITLE:
1133       {
1134         Property::Map valueMap;
1135         if( value.Get( valueMap ) )
1136         {
1137           popupImpl.SetTitle( Scripting::NewActor( valueMap ) );
1138         }
1139         break;
1140       }
1141       case Toolkit::Popup::Property::CONTENT:
1142       {
1143         Property::Map valueMap;
1144         if( value.Get( valueMap ) )
1145         {
1146           popupImpl.SetContent( Scripting::NewActor( valueMap ) );
1147         }
1148         break;
1149       }
1150       case Toolkit::Popup::Property::FOOTER:
1151       {
1152         Property::Map valueMap;
1153         if( value.Get( valueMap ) )
1154         {
1155           popupImpl.SetFooter( Scripting::NewActor( valueMap ) );
1156         }
1157         break;
1158       }
1159       case Toolkit::Popup::Property::DISPLAY_STATE:
1160       {
1161         std::string valueString;
1162         if( value.Get( valueString ) )
1163         {
1164           Toolkit::Popup::DisplayState displayState( Toolkit::Popup::HIDDEN );
1165           if( Scripting::GetEnumeration< Toolkit::Popup::DisplayState >( valueString.c_str(), DisplayStateTable, DisplayStateTableCount, displayState ) )
1166           {
1167             popupImpl.SetDisplayState( displayState );
1168           }
1169         }
1170         break;
1171       }
1172       case Toolkit::Popup::Property::TOUCH_TRANSPARENT:
1173       {
1174         bool valueBool;
1175         if( value.Get( valueBool ) )
1176         {
1177           popupImpl.SetTouchTransparent( valueBool );
1178         }
1179         break;
1180       }
1181       case Toolkit::Popup::Property::TAIL_VISIBILITY:
1182       {
1183         bool valueBool;
1184         if( value.Get( valueBool ) )
1185         {
1186           popupImpl.SetTailVisibility( valueBool );
1187         }
1188         break;
1189       }
1190       case Toolkit::Popup::Property::TAIL_POSITION:
1191       {
1192         Vector3 valueVector3;
1193         if( value.Get( valueVector3 ) )
1194         {
1195           popupImpl.SetTailPosition( valueVector3 );
1196         }
1197         break;
1198       }
1199       case Toolkit::Popup::Property::CONTEXTUAL_MODE:
1200       {
1201         std::string valueString;
1202         if( value.Get( valueString ) )
1203         {
1204           Toolkit::Popup::ContextualMode contextualMode( Toolkit::Popup::BELOW );
1205           if( Scripting::GetEnumeration< Toolkit::Popup::ContextualMode >( valueString.c_str(), ContextualModeTable, ContextualModeTableCount, contextualMode ) )
1206           {
1207             popupImpl.SetContextualMode( contextualMode );
1208           }
1209         }
1210         break;
1211       }
1212       case Toolkit::Popup::Property::ANIMATION_DURATION:
1213       {
1214         float valueFloat;
1215         if( value.Get( valueFloat ) )
1216         {
1217           popupImpl.SetAnimationDuration( valueFloat );
1218         }
1219         break;
1220       }
1221       case Toolkit::Popup::Property::ANIMATION_MODE:
1222       {
1223         std::string valueString;
1224         if( value.Get( valueString ) )
1225         {
1226           Toolkit::Popup::AnimationMode animationMode( Toolkit::Popup::FADE );
1227           if( Scripting::GetEnumeration< Toolkit::Popup::AnimationMode >( valueString.c_str(), AnimationModeTable, AnimationModeTableCount, animationMode ) )
1228           {
1229             popupImpl.SetAnimationMode( animationMode );
1230           }
1231         }
1232         break;
1233       }
1234       case Toolkit::Popup::Property::ENTRY_ANIMATION:
1235       {
1236         Property::Map valueMap;
1237         if( value.Get( valueMap ) )
1238         {
1239           popupImpl.SetEntryAnimationData( valueMap );
1240         }
1241         break;
1242       }
1243       case Toolkit::Popup::Property::EXIT_ANIMATION:
1244       {
1245         Property::Map valueMap;
1246         if( value.Get( valueMap ) )
1247         {
1248           popupImpl.SetExitAnimationData( valueMap );
1249         }
1250         break;
1251       }
1252       case Toolkit::Popup::Property::AUTO_HIDE_DELAY:
1253       {
1254         int valueInt;
1255         if( value.Get( valueInt ) )
1256         {
1257           popupImpl.SetAutoHideDelay( valueInt );
1258         }
1259         break;
1260       }
1261       case Toolkit::Popup::Property::BACKING_ENABLED:
1262       {
1263         bool valueBool;
1264         if( value.Get( valueBool ) )
1265         {
1266           popupImpl.SetBackingEnabled( valueBool );
1267         }
1268         break;
1269       }
1270       case Toolkit::Popup::Property::BACKING_COLOR:
1271       {
1272         Vector4 valueVector4;
1273         if( value.Get( valueVector4 ) )
1274         {
1275           popupImpl.SetBackingColor( valueVector4 );
1276         }
1277         break;
1278       }
1279       case Toolkit::Popup::Property::POPUP_BACKGROUND_IMAGE:
1280       {
1281         std::string valueString;
1282         if( value.Get( valueString ) )
1283         {
1284           Toolkit::ImageView actor = Toolkit::ImageView::New( valueString );
1285           popupImpl.SetPopupBackgroundImage( actor );
1286         }
1287         break;
1288       }
1289       case Toolkit::Popup::Property::POPUP_BACKGROUND_BORDER:
1290       {
1291         bool valueUpdated = false;
1292
1293         Vector4 valueVector4;
1294         if( value.Get( popupImpl.mBackgroundBorder ) )
1295         {
1296           valueUpdated = true;
1297         }
1298         else if( value.Get( valueVector4 ) )
1299         {
1300           popupImpl.mBackgroundBorder.left   = valueVector4.x;
1301           popupImpl.mBackgroundBorder.right  = valueVector4.y;
1302           popupImpl.mBackgroundBorder.bottom = valueVector4.z;
1303           popupImpl.mBackgroundBorder.top    = valueVector4.w;
1304           valueUpdated = true;
1305         }
1306
1307         if( valueUpdated )
1308         {
1309           popupImpl.LayoutTail(); // Update the tail if required
1310           popupImpl.UpdateBackgroundPositionAndSize(); // Update the background's size and position
1311         }
1312         break;
1313       }
1314       case Toolkit::Popup::Property::TAIL_UP_IMAGE:
1315       {
1316         std::string valueString;
1317         if( value.Get( valueString ) )
1318         {
1319           popupImpl.SetTailUpImage( valueString );
1320         }
1321         break;
1322       }
1323       case Toolkit::Popup::Property::TAIL_DOWN_IMAGE:
1324       {
1325         std::string valueString;
1326         if( value.Get( valueString ) )
1327         {
1328           popupImpl.SetTailDownImage( valueString );
1329         }
1330         break;
1331       }
1332       case Toolkit::Popup::Property::TAIL_LEFT_IMAGE:
1333       {
1334         std::string valueString;
1335         if( value.Get( valueString ) )
1336         {
1337           popupImpl.SetTailLeftImage( valueString );
1338         }
1339         break;
1340       }
1341       case Toolkit::Popup::Property::TAIL_RIGHT_IMAGE:
1342       {
1343         std::string valueString;
1344         if( value.Get( valueString ) )
1345         {
1346           popupImpl.SetTailRightImage( valueString );
1347         }
1348         break;
1349       }
1350     }
1351   }
1352 }
1353
1354 Property::Value Popup::GetProperty( BaseObject* object, Property::Index propertyIndex )
1355 {
1356   Property::Value value;
1357
1358   Toolkit::Popup popup = Toolkit::Popup::DownCast( Dali::BaseHandle( object ) );
1359
1360   if ( popup )
1361   {
1362     Popup& popupImpl( GetImpl( popup ) );
1363
1364     switch ( propertyIndex )
1365     {
1366       case Toolkit::Popup::Property::TITLE:
1367       {
1368         Property::Map map;
1369         Scripting::CreatePropertyMap( popupImpl.GetTitle(), map );
1370         value = map;
1371         break;
1372       }
1373       case Toolkit::Popup::Property::CONTENT:
1374       {
1375         Property::Map map;
1376         Scripting::CreatePropertyMap( popupImpl.GetContent(), map );
1377         value = map;
1378         break;
1379       }
1380       case Toolkit::Popup::Property::FOOTER:
1381       {
1382         Property::Map map;
1383         Scripting::CreatePropertyMap( popupImpl.GetFooter(), map );
1384         value = map;
1385         break;
1386       }
1387       case Toolkit::Popup::Property::DISPLAY_STATE:
1388       {
1389         value = Scripting::GetLinearEnumerationName< Toolkit::Popup::DisplayState >( popupImpl.GetDisplayState(), DisplayStateTable, DisplayStateTableCount );
1390         break;
1391       }
1392       case Toolkit::Popup::Property::TOUCH_TRANSPARENT:
1393       {
1394         value = popupImpl.IsTouchTransparent();
1395         break;
1396       }
1397       case Toolkit::Popup::Property::TAIL_VISIBILITY:
1398       {
1399         value = popupImpl.IsTailVisible();
1400         break;
1401       }
1402       case Toolkit::Popup::Property::TAIL_POSITION:
1403       {
1404         value = popupImpl.GetTailPosition();
1405         break;
1406       }
1407       case Toolkit::Popup::Property::CONTEXTUAL_MODE:
1408       {
1409         value = Scripting::GetLinearEnumerationName< Toolkit::Popup::ContextualMode >( popupImpl.GetContextualMode(), ContextualModeTable, ContextualModeTableCount );
1410         break;
1411       }
1412       case Toolkit::Popup::Property::ANIMATION_DURATION:
1413       {
1414         value = popupImpl.GetAnimationDuration();
1415         break;
1416       }
1417       case Toolkit::Popup::Property::ANIMATION_MODE:
1418       {
1419         value = Scripting::GetLinearEnumerationName< Toolkit::Popup::AnimationMode >( popupImpl.GetAnimationMode(), AnimationModeTable, AnimationModeTableCount );
1420         break;
1421       }
1422       case Toolkit::Popup::Property::ENTRY_ANIMATION:
1423       {
1424         // Note: Cannot retrieve property map from animation.
1425         Property::Map map;
1426         value = map;
1427         break;
1428       }
1429       case Toolkit::Popup::Property::EXIT_ANIMATION:
1430       {
1431         // Note: Cannot retrieve property map from animation.
1432         Property::Map map;
1433         value = map;
1434         break;
1435       }
1436       case Toolkit::Popup::Property::AUTO_HIDE_DELAY:
1437       {
1438         value = popupImpl.GetAutoHideDelay();
1439         break;
1440       }
1441       case Toolkit::Popup::Property::BACKING_ENABLED:
1442       {
1443         value = popupImpl.IsBackingEnabled();
1444         break;
1445       }
1446       case Toolkit::Popup::Property::BACKING_COLOR:
1447       {
1448         value = popupImpl.GetBackingColor();
1449         break;
1450       }
1451       case Toolkit::Popup::Property::POPUP_BACKGROUND_IMAGE:
1452       {
1453         Toolkit::ImageView imageView = Toolkit::ImageView::DownCast( popupImpl.GetPopupBackgroundImage() );
1454         if( imageView )
1455         {
1456           value = imageView.GetProperty( Toolkit::ImageView::Property::IMAGE );
1457         }
1458         break;
1459       }
1460       case Toolkit::Popup::Property::POPUP_BACKGROUND_BORDER:
1461       {
1462         value = popupImpl.mBackgroundBorder;
1463         break;
1464       }
1465       case Toolkit::Popup::Property::TAIL_UP_IMAGE:
1466       {
1467         value = popupImpl.GetTailUpImage();
1468         break;
1469       }
1470       case Toolkit::Popup::Property::TAIL_DOWN_IMAGE:
1471       {
1472         value = popupImpl.GetTailDownImage();
1473         break;
1474       }
1475       case Toolkit::Popup::Property::TAIL_LEFT_IMAGE:
1476       {
1477         value = popupImpl.GetTailLeftImage();
1478         break;
1479       }
1480       case Toolkit::Popup::Property::TAIL_RIGHT_IMAGE:
1481       {
1482         value = popupImpl.GetTailRightImage();
1483         break;
1484       }
1485     }
1486   }
1487
1488   return value;
1489 }
1490
1491 bool Popup::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
1492 {
1493   Dali::BaseHandle handle( object );
1494
1495   bool connected( true );
1496   Toolkit::Popup popup = Toolkit::Popup::DownCast( handle );
1497
1498   if( 0 == strcmp( signalName.c_str(), SIGNAL_TOUCHED_OUTSIDE ) )
1499   {
1500     popup.OutsideTouchedSignal().Connect( tracker, functor );
1501   }
1502   else if( 0 == strcmp( signalName.c_str(), SIGNAL_SHOWING ) )
1503   {
1504     popup.ShowingSignal().Connect( tracker, functor );
1505   }
1506   else if( 0 == strcmp( signalName.c_str(), SIGNAL_SHOWN ) )
1507   {
1508     popup.ShownSignal().Connect( tracker, functor );
1509   }
1510   else if( 0 == strcmp( signalName.c_str(), SIGNAL_HIDING ) )
1511   {
1512     popup.HidingSignal().Connect( tracker, functor );
1513   }
1514   else if( 0 == strcmp( signalName.c_str(), SIGNAL_HIDDEN ) )
1515   {
1516     popup.HiddenSignal().Connect( tracker, functor );
1517   }
1518   else
1519   {
1520     // signalName does not match any signal
1521     connected = false;
1522   }
1523
1524   return connected;
1525 }
1526
1527 bool Popup::OnBackingTouched( Actor actor, const TouchData& touch )
1528 {
1529   // Allow events to pass through if touch transparency is enabled.
1530   if( mTouchTransparent )
1531   {
1532     return false;
1533   }
1534
1535   if( touch.GetPointCount() > 0 )
1536   {
1537     if( touch.GetState( 0 ) == PointState::DOWN )
1538     {
1539       // Guard against destruction during signal emission.
1540       Toolkit::Popup handle( GetOwner() );
1541
1542       mTouchedOutsideSignal.Emit();
1543     }
1544   }
1545
1546   // Block anything behind backing becoming touched.
1547   mLayer.SetTouchConsumed( true );
1548   return true;
1549 }
1550
1551 bool Popup::OnBackingWheelEvent( Actor actor, const WheelEvent& event )
1552 {
1553   // Allow events to pass through if touch transparency is enabled.
1554   if( mTouchTransparent )
1555   {
1556     return false;
1557   }
1558
1559   // Consume wheel event in dimmed backing actor.
1560   mLayer.SetTouchConsumed( true );
1561   return true;
1562 }
1563
1564 bool Popup::OnDialogTouched( Actor actor, const TouchData& touch )
1565 {
1566   // Allow events to pass through if touch transparency is enabled.
1567   if( mTouchTransparent )
1568   {
1569     return false;
1570   }
1571
1572   // Consume event (stops backing actor receiving touch events)
1573   mLayer.SetTouchConsumed( true );
1574   return true;
1575 }
1576
1577 void Popup::OnStageConnection( int depth )
1578 {
1579   mLayoutDirty = true;
1580   RelayoutRequest();
1581
1582   Control::OnStageConnection( depth );
1583 }
1584
1585 void Popup::OnChildAdd( Actor& child )
1586 {
1587   // Re-parent any children added by user to the body layer.
1588   if( mAlterAddedChild )
1589   {
1590     SetContent( child );
1591   }
1592   else
1593   {
1594     mLayoutDirty = true;
1595     RelayoutRequest();
1596   }
1597
1598   Control::OnChildAdd( child );
1599 }
1600
1601 void Popup::LayoutContext( const Vector2& size )
1602 {
1603   // Do nothing if not in a contextual mode (or there is no parent context).
1604   Actor self = Self();
1605   Actor parent = self.GetParent();
1606   if( ( mContextualMode == Toolkit::Popup::NON_CONTEXTUAL ) || !parent )
1607   {
1608     return;
1609   }
1610
1611   mPopupContainer.SetParentOrigin( ParentOrigin::CENTER );
1612   // We always anchor to the CENTER, rather than a different anchor point for each contextual
1613   // mode to allow code-reuse of the bound checking code (for maintainability).
1614   mPopupContainer.SetAnchorPoint( AnchorPoint::CENTER );
1615
1616   // Setup with some pre-calculations for speed.
1617   Vector3 halfStageSize( Stage().GetCurrent().GetSize() / 2.0f );
1618   Vector3 parentPosition( parent.GetCurrentPosition() );
1619   Vector2 halfSize( size / 2.0f );
1620   Vector2 halfParentSize( parent.GetRelayoutSize( Dimension::WIDTH ) / 2.0f, parent.GetRelayoutSize( Dimension::HEIGHT ) / 2.0f );
1621   Vector3 newPosition( Vector3::ZERO );
1622
1623   // Perform different positioning based on the specified contextual layout mode.
1624   switch( mContextualMode )
1625   {
1626     case Toolkit::Popup::BELOW:
1627     {
1628       newPosition.x += halfSize.x - halfParentSize.x;
1629       newPosition.y += halfSize.y + halfParentSize.y + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.y;
1630       break;
1631     }
1632     case Toolkit::Popup::ABOVE:
1633     {
1634       newPosition.x += halfSize.x - halfParentSize.x;
1635       newPosition.y -= halfSize.y + halfParentSize.y + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.y;
1636       break;
1637     }
1638     case Toolkit::Popup::RIGHT:
1639     {
1640       newPosition.x += halfSize.x + halfParentSize.x + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.x;
1641       newPosition.y += halfSize.y - halfParentSize.y;
1642       break;
1643     }
1644     case Toolkit::Popup::LEFT:
1645     {
1646       newPosition.x -= halfSize.x + halfParentSize.x + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.x;
1647       newPosition.y += halfSize.y - halfParentSize.y;
1648       break;
1649     }
1650     case Toolkit::Popup::NON_CONTEXTUAL:
1651     {
1652       // Code won't reach here (caught earlier).
1653       break;
1654     }
1655   }
1656
1657   // On-screen position checking.
1658   // Check new position is not too far right. If so, correct it.
1659   // Note: Check for right rather than left first, so if popup is too wide, the left check overrides
1660   // the right check and we at least see the left portion of the popup (as this is more useful).
1661   if( newPosition.x >= ( halfStageSize.x - parentPosition.x - halfSize.x - DEFAULT_CONTEXTUAL_STAGE_BORDER.x ) )
1662   {
1663     newPosition.x = halfStageSize.x - parentPosition.x - halfSize.x - DEFAULT_CONTEXTUAL_STAGE_BORDER.x;
1664   }
1665   // Check new position is not too far left. If so, correct it.
1666   if( newPosition.x < halfSize.x - ( parentPosition.x + halfStageSize.x ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.x )
1667   {
1668     newPosition.x = halfSize.x - ( parentPosition.x + halfStageSize.x ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.x;// - parentSize.x;
1669   }
1670   // Check new position is not too far down. If so, correct it.
1671   if( newPosition.y >= ( halfStageSize.y - parentPosition.y - halfSize.y - DEFAULT_CONTEXTUAL_STAGE_BORDER.y ) )
1672   {
1673     newPosition.y = halfStageSize.y - parentPosition.y - halfSize.y - DEFAULT_CONTEXTUAL_STAGE_BORDER.y;
1674   }
1675   // Check new position is not too far up. If so, correct it.
1676   if( newPosition.y < halfSize.y - ( parentPosition.y + halfStageSize.y ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.y )
1677   {
1678     newPosition.y = halfSize.y - ( parentPosition.y + halfStageSize.y ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.y;
1679   }
1680
1681   // Set the final position.
1682   mPopupContainer.SetPosition( newPosition );
1683 }
1684
1685 void Popup::OnRelayout( const Vector2& size, RelayoutContainer& container )
1686 {
1687   Vector2 useSize( size );
1688
1689   // Use the Popup layouts size, unless requested to use a fixed size.
1690   // In which case take the size set for the Popup itself.
1691   ResizePolicy::Type widthPolicy = Self().GetResizePolicy( Dimension::WIDTH );
1692   ResizePolicy::Type heightPolicy = Self().GetResizePolicy( Dimension::HEIGHT );
1693
1694   // Width calculations:
1695   if( widthPolicy == ResizePolicy::USE_NATURAL_SIZE || widthPolicy == ResizePolicy::FIT_TO_CHILDREN )
1696   {
1697     // If we using a child-based policy, take the size from the popup layout.
1698     mPopupLayout.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::WIDTH );
1699     useSize.width = mPopupLayout.GetRelayoutSize( Dimension::WIDTH );
1700
1701     mPopupLayout.SetFitWidth( 0u );
1702   }
1703   else
1704   {
1705     // If we using a parent-based policy, take the size from the popup object itself (self).
1706     mPopupLayout.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::WIDTH );
1707
1708     mPopupLayout.SetFixedWidth( 0u, useSize.width );
1709   }
1710
1711   // Height calculations:
1712   // Title: Let the title be as high as it needs to be.
1713   mPopupLayout.SetFitHeight( 0u );
1714
1715   // Footer: Convert the footer's resize policy to a TableView row policy.
1716   if( mFooter )
1717   {
1718     ResizePolicy::Type footerHeightPolicy = mFooter.GetResizePolicy( Dimension::HEIGHT );
1719     if( ( footerHeightPolicy == ResizePolicy::USE_NATURAL_SIZE ) ||
1720         ( footerHeightPolicy == ResizePolicy::FIT_TO_CHILDREN ) )
1721     {
1722       mPopupLayout.SetFitHeight( 2u );
1723     }
1724     else if( footerHeightPolicy == ResizePolicy::FIXED )
1725     {
1726       mPopupLayout.SetFixedHeight( 2u, mFooter.GetRelayoutSize( Dimension::HEIGHT) );
1727     }
1728     else
1729     {
1730       mPopupLayout.SetRelativeHeight( 2u, 1.0f );
1731     }
1732   }
1733   else
1734   {
1735     mPopupLayout.SetFixedHeight( 2u, 0.0f );
1736   }
1737
1738   // Popup contents: Adjust the tableview's policies based on the popup's policies.
1739   if( heightPolicy == ResizePolicy::USE_NATURAL_SIZE || heightPolicy == ResizePolicy::FIT_TO_CHILDREN )
1740   {
1741     mPopupLayout.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
1742
1743     // Let both the contents expand as necessary.
1744     mPopupLayout.SetFitHeight( 1u );
1745     useSize.height = mPopupLayout.GetRelayoutSize( Dimension::HEIGHT );
1746   }
1747   else
1748   {
1749     mPopupLayout.SetResizePolicy( heightPolicy, Dimension::HEIGHT );
1750
1751     // Let the content expand to fill the remaining space.
1752     mPopupLayout.SetRelativeHeight( 1u, 1.0f );
1753     mPopupLayout.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::HEIGHT );
1754   }
1755
1756   // Relayout the popup-layout to give it it's new size this frame.
1757   container.Add( mPopupLayout, useSize );
1758
1759   if( mContent )
1760   {
1761     container.Add( mContent, Vector2( mContent.GetRelayoutSize( Dimension::WIDTH ), mContent.GetRelayoutSize( Dimension::HEIGHT ) ) );
1762   }
1763
1764   // Perform contextual layout setup if required.
1765   // This is done each time in case the parent moves.
1766   // This will have no effect if no contextual mode is selected.
1767   LayoutContext( useSize );
1768 }
1769
1770 void Popup::OnSetResizePolicy( ResizePolicy::Type policy, Dimension::Type dimension )
1771 {
1772   // To get the popup to emulate fit-to-children, we need to actually set use-natural-size.
1773   if( ( dimension & Dimension::HEIGHT ) && ( policy == ResizePolicy::FIT_TO_CHILDREN ) )
1774   {
1775     Self().SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT );
1776   }
1777
1778   mLayoutDirty = true;
1779   return;
1780 }
1781
1782 Vector3 Popup::GetNaturalSize()
1783 {
1784   return mPopupLayout.GetNaturalSize();
1785 }
1786
1787 float Popup::GetHeightForWidth( float width )
1788 {
1789   return mPopupLayout.GetHeightForWidth( width );
1790 }
1791
1792 float Popup::GetWidthForHeight( float height )
1793 {
1794   return mPopupLayout.GetWidthForHeight( height );
1795 }
1796
1797 bool Popup::OnKeyEvent( const KeyEvent& event )
1798 {
1799   // Allow events to pass through if touch transparency is enabled.
1800   if( mTouchTransparent )
1801   {
1802     return false;
1803   }
1804
1805   bool consumed = false;
1806
1807   if( event.state == KeyEvent::Down )
1808   {
1809     if (event.keyCode == Dali::DALI_KEY_ESCAPE || event.keyCode == Dali::DALI_KEY_BACK)
1810     {
1811       SetDisplayState( Toolkit::Popup::HIDDEN );
1812       consumed = true;
1813     }
1814   }
1815
1816   return consumed;
1817 }
1818
1819 void Popup::AddFocusableChildrenRecursive( Actor parent, std::vector< Actor >& focusableActors )
1820 {
1821   if( parent )
1822   {
1823     Toolkit::Control control = Toolkit::Control::DownCast( parent );
1824     bool layoutControl = control && GetImplementation( control ).IsKeyboardNavigationSupported();
1825
1826     if( parent.IsKeyboardFocusable() || layoutControl )
1827     {
1828       focusableActors.push_back( parent );
1829
1830       if( !layoutControl )
1831       {
1832         for( unsigned int i = 0, numberChildren = parent.GetChildCount(); i < numberChildren; ++i )
1833         {
1834           Actor child( parent.GetChildAt( i ) );
1835           AddFocusableChildrenRecursive( child, focusableActors );
1836         }
1837       }
1838     }
1839   }
1840 }
1841
1842 void Popup::AddFocusableChildren( Actor parent, std::vector< Actor >& focusableActors )
1843 {
1844   if( parent )
1845   {
1846     Toolkit::Control control = Toolkit::Control::DownCast( parent );
1847     if( !GetImplementation( control ).IsKeyboardNavigationSupported() )
1848     {
1849       for( unsigned int i = 0, numberChildren = parent.GetChildCount(); i < numberChildren; ++i )
1850       {
1851         Actor child( parent.GetChildAt( i ) );
1852         AddFocusableChildrenRecursive( child, focusableActors );
1853       }
1854     }
1855     else
1856     {
1857       focusableActors.push_back( parent );
1858     }
1859   }
1860 }
1861
1862 Actor Popup::GetNextKeyboardFocusableActor( Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled )
1863 {
1864   std::string currentStr;
1865   if( currentFocusedActor )
1866   {
1867     currentStr = currentFocusedActor.GetName();
1868   }
1869
1870   Actor nextFocusableActor( currentFocusedActor );
1871   Actor currentFocusGroup;
1872   if( currentFocusedActor )
1873   {
1874     currentFocusGroup = KeyboardFocusManager::Get().GetFocusGroup( currentFocusedActor );
1875   }
1876
1877   // TODO: Needs to be optimised
1878   // The following statement checks that if we have a current focused actor, then the current focus group is not the popup content or footer.
1879   // This is to detect if the focus is currently outside the popup, and if so, move it inside.
1880   if( !currentFocusedActor ||
1881     ( currentFocusedActor && ( ( !mContent || ( currentFocusGroup != mContent ) ) && ( !mFooter || ( currentFocusGroup != mFooter ) ) ) ) )
1882   {
1883     // The current focused actor is not within popup.
1884     if( mContent && mContent.IsKeyboardFocusable() )
1885     {
1886       // If the content is focusable, move the focus to the content.
1887       nextFocusableActor = mContent;
1888     }
1889     else if( mFooter && mFooter.IsKeyboardFocusable() )
1890     {
1891       // If the footer is focusable, move the focus to the footer.
1892       nextFocusableActor = mFooter;
1893     }
1894   }
1895   else
1896   {
1897     // Rebuild the focus chain because controls or content can be added or removed dynamically
1898     std::vector< Actor > focusableActors;
1899
1900     AddFocusableChildren( mContent, focusableActors );
1901     AddFocusableChildren( mFooter, focusableActors );
1902
1903     std::vector< Actor >::iterator endIterator = focusableActors.end();
1904     std::vector< Actor >::iterator currentIterator = focusableActors.begin();
1905     for( std::vector< Actor >::iterator iterator = focusableActors.begin(); iterator != endIterator; ++iterator )
1906     {
1907       if( currentFocusedActor == *iterator )
1908       {
1909         currentIterator = iterator;
1910       }
1911     }
1912
1913     if( currentIterator != endIterator )
1914     {
1915       switch( direction )
1916       {
1917         case Toolkit::Control::KeyboardFocus::LEFT:
1918         {
1919           if( currentIterator == focusableActors.begin() )
1920           {
1921             nextFocusableActor = *( endIterator - 1 );
1922           }
1923           else
1924           {
1925             nextFocusableActor = *( currentIterator - 1 );
1926           }
1927           break;
1928         }
1929         case Toolkit::Control::KeyboardFocus::RIGHT:
1930         {
1931           if( currentIterator == endIterator - 1 )
1932           {
1933             nextFocusableActor = *( focusableActors.begin() );
1934           }
1935           else
1936           {
1937             nextFocusableActor = *( currentIterator + 1 );
1938           }
1939           break;
1940         }
1941
1942         case Toolkit::Control::KeyboardFocus::UP:
1943         {
1944           nextFocusableActor = *(  focusableActors.begin() );
1945           break;
1946         }
1947
1948         case Toolkit::Control::KeyboardFocus::DOWN:
1949         {
1950           nextFocusableActor = *( endIterator - 1 );
1951           break;
1952         }
1953
1954         default:
1955         {
1956           break;
1957         }
1958       }
1959
1960       if( !nextFocusableActor )
1961       {
1962         DALI_LOG_WARNING( "Can not decide next focusable actor\n" );
1963       }
1964     }
1965   }
1966
1967   return nextFocusableActor;
1968 }
1969
1970
1971 } // namespace Internal
1972
1973 } // namespace Toolkit
1974
1975 } // namespace Dali