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