Added TextLabel and TextField tests
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / popup / popup-impl.cpp
1 /*
2  * Copyright (c) 2014 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/public-api/adaptor-framework/key.h>
24 #include <dali/public-api/adaptor-framework/physical-keyboard.h>
25 #include <dali/public-api/animation/constraints.h>
26 #include <dali/public-api/common/stage.h>
27 #include <dali/public-api/events/key-event.h>
28 #include <dali/public-api/events/touch-event.h>
29 #include <dali/public-api/images/resource-image.h>
30 #include <dali/public-api/object/type-registry.h>
31 #include <dali/public-api/object/type-registry-helper.h>
32 #include <dali/public-api/size-negotiation/relayout-container.h>
33 #include <dali/integration-api/debug.h>
34
35 // INTERNAL INCLUDES
36 #include <dali-toolkit/public-api/controls/buttons/button.h>
37 #include <dali-toolkit/public-api/controls/control-impl.h>
38 #include <dali-toolkit/public-api/controls/default-controls/solid-color-actor.h>
39 #include <dali-toolkit/public-api/focus-manager/focus-manager.h>
40 #include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
41
42 using namespace Dali;
43
44 namespace Dali
45 {
46
47 namespace Toolkit
48 {
49
50 namespace Internal
51 {
52
53 namespace
54 {
55
56 BaseHandle Create()
57 {
58   return Toolkit::Popup::New();
59 }
60
61 // Setup properties, signals and actions using the type-registry.
62 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::Popup, Toolkit::Control, Create )
63
64 DALI_SIGNAL_REGISTRATION( Popup, "touched-outside", SIGNAL_TOUCHED_OUTSIDE )
65 DALI_SIGNAL_REGISTRATION( Popup, "hidden",          SIGNAL_HIDDEN          )
66
67 DALI_TYPE_REGISTRATION_END()
68
69 // Properties
70 const char* const PROPERTY_TITLE = "title";
71 const char* const PROPERTY_STATE = "state";
72
73 const float POPUP_ANIMATION_DURATION = 0.45f;                      ///< Duration of hide/show animations
74
75 const float POPUP_WIDTH = 720.0f;                             ///< Width of Popup
76 const float POPUP_OUT_MARGIN_WIDTH = 16.f;                    ///< Space between the screen edge and the popup edge in the horizontal dimension.
77 const float POPUP_OUT_MARGIN_HEIGHT = 36.f;                   ///< Space between the screen edge and the popup edge in the vertical dimension.
78 const float POPUP_TITLE_WIDTH = 648.0f;                       ///<Width of Popup Title
79 const float POPUP_BUTTON_BG_HEIGHT = 96.f;                    ///< Height of Button Background.
80 const Vector3 DEFAULT_DIALOG_SIZE = Vector3(POPUP_TITLE_WIDTH/POPUP_WIDTH, 0.5f, 0.0f);
81 const Vector3 DEFAULT_BOTTOM_SIZE = Vector3(1.0f, 0.2f, 0.0f);
82
83 } // unnamed namespace
84
85 ///////////////////////////////////////////////////////////////////////////////////////////////////
86 // Popup
87 ///////////////////////////////////////////////////////////////////////////////////////////////////
88
89 Dali::Toolkit::Popup Popup::New()
90 {
91   PopupStylePtr style = PopupStyleDefault::New();
92
93   // Create the implementation
94   PopupPtr popup(new Popup(*style));
95
96   // Pass ownership to CustomActor via derived handle
97   Dali::Toolkit::Popup handle(*popup);
98
99   // Second-phase init of the implementation
100   // This can only be done after the CustomActor connection has been made...
101   popup->Initialize();
102
103   return handle;
104 }
105
106 Popup::Popup(PopupStyle& style)
107 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ),
108   mShowing(false),
109   mState(Toolkit::Popup::POPUP_NONE), // Initially, the popup state should not be set, it's set in OnInitialize
110   mAlterAddedChild(false),
111   mPopupStyle(PopupStylePtr(&style)),
112   mPropertyTitle(Property::INVALID_INDEX),
113   mPropertyState(Property::INVALID_INDEX)
114 {
115   SetKeyboardNavigationSupport( true );
116 }
117
118 void Popup::OnInitialize()
119 {
120   Dali::Stage stage = Dali::Stage::GetCurrent();
121
122   Actor self = Self();
123   self.SetSensitive(false);
124   // Reisize to fit the height of children
125   self.SetResizePolicy( FIT_TO_CHILDREN, HEIGHT );
126
127   // Create Layer
128   mLayer = Layer::New();
129   mLayer.SetName( "POPUP_LAYER" );
130   mLayer.SetParentOrigin(ParentOrigin::CENTER);
131   mLayer.SetAnchorPoint(AnchorPoint::CENTER);
132   mLayer.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
133   mLayer.SetDrawMode( DrawMode::OVERLAY );
134
135   // Any content after this point which is added to Self() will be reparented to
136   // mContent.
137   mAlterAddedChild = true;
138   // Add Backing (Dim effect)
139   CreateBacking();
140   mAlterAddedChild = false;
141
142   // Add Dialog ( background image, title, content container, button container and tail )
143   CreateDialog();
144
145   mLayer.Add( self );
146
147   mPopupLayout = Toolkit::TableView::New( 3, 1 );
148   mPopupLayout.SetName( "POPUP_LAYOUT_TABLE" );
149   mPopupLayout.SetParentOrigin(ParentOrigin::CENTER);
150   mPopupLayout.SetAnchorPoint(AnchorPoint::CENTER);
151   mPopupLayout.SetResizePolicy( FILL_TO_PARENT, WIDTH );
152   mPopupLayout.SetResizePolicy( USE_NATURAL_SIZE, HEIGHT );
153   mPopupLayout.SetFitHeight( 0 );   // Set row to fit
154   mPopupLayout.SetFitHeight( 1 );   // Set row to fit
155   self.Add( mPopupLayout );
156
157   // Any content after this point which is added to Self() will be reparented to
158   // mContent.
159   mAlterAddedChild = true;
160
161   // Default content.
162 //  ShowTail(ParentOrigin::BOTTOM_CENTER);
163
164   // Hide content by default.
165   SetState( Toolkit::Popup::POPUP_HIDE, 0.0f );
166
167   mPropertyTitle = self.RegisterProperty( PROPERTY_TITLE, "", Property::READ_WRITE );
168   mPropertyState = self.RegisterProperty( PROPERTY_STATE, "POPUP_HIDE", Property::READ_WRITE );
169
170   // Make self as keyboard focusable and focus group
171   self.SetKeyboardFocusable(true);
172   SetAsKeyboardFocusGroup(true);
173 }
174
175 void Popup::MarkDirtyForRelayout()
176 {
177   // Flag all the popup controls for relayout as it is about to be hidden and miss the main flagging pass
178   mLayer.RelayoutRequestTree();
179 }
180
181 void Popup::OnPropertySet( Property::Index index, Property::Value propertyValue )
182 {
183   if( index == mPropertyTitle )
184   {
185     SetTitle(propertyValue.Get<std::string>());
186   }
187   else if ( index == mPropertyState )
188   {
189     std::string value( propertyValue.Get<std::string>() );
190     if(value == "POPUP_SHOW")
191     {
192       SetState( Toolkit::Popup::POPUP_SHOW, 0.0f );
193     }
194     else if( value == "POPUP_HIDE")
195     {
196       SetState( Toolkit::Popup::POPUP_HIDE, 0.0f );
197     }
198   }
199 }
200
201 Popup::~Popup()
202 {
203   mLayer.Unparent();
204 }
205
206 size_t Popup::GetButtonCount() const
207 {
208   return mButtons.size();
209 }
210
211 void Popup::SetBackgroundImage( Actor image )
212 {
213   // Removes any previous background.
214   if( mBackgroundImage && mPopupLayout )
215   {
216     mPopupLayout.Remove( mBackgroundImage );
217   }
218
219   // Adds new background to the dialog.
220   mBackgroundImage = image;
221
222   mBackgroundImage.SetName( "POPUP_BACKGROUND_IMAGE" );
223
224   // OnDialogTouched only consume the event. It prevents the touch event to be caught by the backing.
225   mBackgroundImage.TouchedSignal().Connect( this, &Popup::OnDialogTouched );
226
227   mBackgroundImage.SetResizePolicy( SIZE_FIXED_OFFSET_FROM_PARENT, ALL_DIMENSIONS );
228   mBackgroundImage.SetAnchorPoint( AnchorPoint::CENTER );
229   mBackgroundImage.SetParentOrigin( ParentOrigin::CENTER );
230
231   Vector3 border( mPopupStyle->backgroundOuterBorder.x, mPopupStyle->backgroundOuterBorder.z, 0.0f );
232   mBackgroundImage.SetSizeModeFactor( border );
233
234   const bool prevAlter = mAlterAddedChild;
235   mAlterAddedChild = false;
236   Self().Add( mBackgroundImage );
237   mAlterAddedChild = prevAlter;
238 }
239
240 void Popup::SetButtonAreaImage( Actor image )
241 {
242   // Removes any previous area image.
243   if( mButtonAreaImage && mPopupLayout )
244   {
245     mPopupLayout.Remove( mButtonAreaImage );
246   }
247
248   // Adds new area image to the dialog.
249   mButtonAreaImage = image;
250
251   // OnDialogTouched only consume the event. It prevents the touch event to be caught by the backing.
252   mButtonAreaImage.TouchedSignal().Connect( this, &Popup::OnDialogTouched );
253
254   mButtonAreaImage.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
255   mButtonAreaImage.SetAnchorPoint( AnchorPoint::CENTER );
256   mButtonAreaImage.SetParentOrigin( ParentOrigin::CENTER );
257
258   if( GetButtonCount() > 0 )
259   {
260     mBottomBg.Add( mButtonAreaImage );
261   }
262 }
263
264 void Popup::SetTitle( const std::string& text )
265 {
266   // Replaces the current title actor.
267   if( mPopupLayout )
268   {
269     mPopupLayout.RemoveChildAt( Toolkit::TableView::CellPosition( 0, 0 ) );
270   }
271
272   mTitle = Toolkit::TextLabel::New( text );
273   mTitle.SetName( "POPUP_TITLE" );
274   mTitle.SetProperty( Toolkit::TextLabel::Property::MULTI_LINE, true );
275   mTitle.SetProperty( Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT, "CENTER" );
276
277   if( mPopupLayout )
278   {
279     mTitle.SetPadding( Padding( 0.0f, 0.0f, mPopupStyle->margin, mPopupStyle->margin ) );
280     mTitle.SetResizePolicy( FILL_TO_PARENT, WIDTH );
281     mTitle.SetResizePolicy( DIMENSION_DEPENDENCY, HEIGHT );
282     mPopupLayout.AddChild( mTitle, Toolkit::TableView::CellPosition( 0, 0 ) );
283   }
284
285   RelayoutRequest();
286 }
287
288 std::string Popup::GetTitle() const
289 {
290   if( mTitle )
291   {
292     return mTitle.GetProperty<std::string>( Toolkit::TextLabel::Property::TEXT );
293   }
294
295   return std::string();
296 }
297
298 void Popup::CreateFooter()
299 {
300   if( !mBottomBg )
301   {
302     // Adds bottom background
303     mBottomBg = Actor::New();
304     mBottomBg.SetName( "POPUP_BOTTOM_BG" );
305     mBottomBg.SetRelayoutEnabled( true );
306     mBottomBg.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
307
308     mPopupLayout.SetFixedHeight( 2, mPopupStyle->bottomSize.height );   // Buttons
309     mPopupLayout.AddChild( mBottomBg, Toolkit::TableView::CellPosition( 2, 0 ) );
310   }
311 }
312
313 void Popup::AddButton( Toolkit::Button button )
314 {
315   mButtons.push_back( button );
316   button.SetResizePolicy( USE_ASSIGNED_SIZE, ALL_DIMENSIONS );    // Size will be assigned to it
317
318   // If this is the first button added
319   if( mButtons.size() == 1 )
320   {
321     CreateFooter();
322
323     if( mButtonAreaImage )
324     {
325       mBottomBg.Add( mButtonAreaImage );
326     }
327   }
328
329   mBottomBg.Add( button );
330
331   RelayoutRequest();
332 }
333
334 void Popup::SetState( Toolkit::Popup::PopupState state )
335 {
336   SetState( state, POPUP_ANIMATION_DURATION );
337 }
338
339 void Popup::SetState( Toolkit::Popup::PopupState state, float duration )
340 {
341   // default animation behaviour.
342   HandleStateChange(state, duration);
343 }
344
345 Toolkit::Popup::PopupState Popup::GetState() const
346 {
347   return mState;
348 }
349
350 void Popup::ShowTail(const Vector3& position)
351 {
352   // Replaces the tail actor.
353   if(mTailImage && mTailImage.GetParent())
354   {
355     mTailImage.GetParent().Remove( mTailImage );
356     mTailImage.Reset();
357   }
358
359   std::string image = "";
360
361   // depending on position of tail around ParentOrigin, a different tail image is used...
362   if(position.y < Math::MACHINE_EPSILON_1)
363   {
364     image = mPopupStyle->tailUpImage;
365   }
366   else if(position.y > 1.0f - Math::MACHINE_EPSILON_1)
367   {
368     image = mPopupStyle->tailDownImage;
369   }
370   else if(position.x < Math::MACHINE_EPSILON_1)
371   {
372     image = mPopupStyle->tailLeftImage;
373   }
374   else if(position.x > 1.0f - Math::MACHINE_EPSILON_1)
375   {
376     image = mPopupStyle->tailRightImage;
377   }
378
379   if(image != "")
380   {
381     Image tail = ResourceImage::New( image );
382     mTailImage = ImageActor::New(tail);
383     const Vector3 anchorPoint = AnchorPoint::BOTTOM_RIGHT - position;
384
385     mTailImage.SetParentOrigin(position);
386     mTailImage.SetAnchorPoint(anchorPoint);
387
388     CreateFooter();
389
390     mBottomBg.Add(mTailImage);
391   }
392 }
393
394 void Popup::HideTail()
395 {
396   ShowTail(ParentOrigin::CENTER);
397 }
398
399 void Popup::SetStyle(PopupStyle& style)
400 {
401   mPopupStyle = PopupStylePtr(&style);
402   // update //
403 }
404
405 PopupStylePtr Popup::GetStyle() const
406 {
407   return mPopupStyle;
408 }
409
410 void Popup::SetDefaultBackgroundImage()
411 {
412   Image buttonBg = ResourceImage::New( mPopupStyle->buttonAreaImage );
413   ImageActor buttonBgImage = ImageActor::New( buttonBg );
414   buttonBgImage.SetStyle( ImageActor::STYLE_NINE_PATCH );
415   buttonBgImage.SetNinePatchBorder( mPopupStyle->buttonArea9PatchBorder );
416
417   SetBackgroundImage( ImageActor::New( ResourceImage::New( mPopupStyle->backgroundImage ) ) );
418   SetButtonAreaImage( buttonBgImage );
419 }
420
421 void Popup::CreateBacking()
422 {
423   mBacking = Dali::Toolkit::CreateSolidColorActor( mPopupStyle->backingColor );
424   mBacking.SetName( "POPUP_BACKING" );
425
426   mBacking.SetResizePolicy( FILL_TO_PARENT, ALL_DIMENSIONS );
427   mBacking.SetSensitive(true);
428
429   mLayer.Add( mBacking );
430   mBacking.SetOpacity(0.0f);
431   mBacking.TouchedSignal().Connect( this, &Popup::OnBackingTouched );
432   mBacking.MouseWheelEventSignal().Connect(this, &Popup::OnBackingMouseWheelEvent);
433 }
434
435 void Popup::CreateDialog()
436 {
437   // Adds default background image.
438   SetDefaultBackgroundImage();
439 }
440
441 void Popup::HandleStateChange( Toolkit::Popup::PopupState state, float duration )
442 {
443   Vector3 targetSize;
444   float targetBackingAlpha;
445
446   if(mState == state)
447   {
448     return;
449   }
450   mState = state;
451   switch(state)
452   {
453     case Toolkit::Popup::POPUP_HIDE:
454     {
455       targetSize = Vector3(0.0f, 0.0f, 1.0f);
456       targetBackingAlpha = 0.0f;
457       mShowing = false;
458       ClearKeyInputFocus();
459
460       // Retore the keyboard focus when popup is hidden
461       if(mPreviousFocusedActor && mPreviousFocusedActor.IsKeyboardFocusable() )
462       {
463         Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
464         if( keyboardFocusManager )
465         {
466           keyboardFocusManager.SetCurrentFocusActor(mPreviousFocusedActor);
467         }
468       }
469
470       break;
471     }
472
473     case Toolkit::Popup::POPUP_SHOW:
474     default:
475     {
476       targetSize = Vector3(1.0f, 1.0f, 1.0f);
477       targetBackingAlpha = 1.0f;
478       mShowing = true;
479
480       // Add contents to stage for showing.
481       if( !mLayer.GetParent() )
482       {
483         Dali::Stage stage = Dali::Stage::GetCurrent();
484         stage.Add( mLayer );
485         mLayer.RaiseToTop();
486       }
487
488       Self().SetSensitive(true);
489       SetKeyInputFocus();
490
491       // Handle the keyboard focus when popup is shown
492       Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
493       if( keyboardFocusManager )
494       {
495         mPreviousFocusedActor = keyboardFocusManager.GetCurrentFocusActor();
496
497         if( mContent && mContent.IsKeyboardFocusable() )
498         {
499           // If content is focusable, move the focus to content
500           keyboardFocusManager.SetCurrentFocusActor(mContent);
501         }
502         else if( !mButtons.empty() )
503         {
504           // Otherwise, movethe focus to the first button
505           keyboardFocusManager.SetCurrentFocusActor(mButtons[0]);
506         }
507         else
508         {
509           DALI_LOG_WARNING("There is no focusable in popup\n");
510         }
511       }
512       break;
513     }
514   }
515
516   Actor self = Self();
517   if(duration > Math::MACHINE_EPSILON_1)
518   {
519     if ( mAnimation )
520     {
521       mAnimation.Stop();
522       mAnimation.Clear();
523       mAnimation.Reset();
524     }
525     mAnimation = Animation::New(duration);
526
527     if(mShowing)
528     {
529       mAnimation.AnimateTo( Property(mBacking, Actor::Property::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) );
530       mAnimation.AnimateTo( Property(self, Actor::Property::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(duration * 0.5f, duration * 0.5f) );
531     }
532     else
533     {
534       mAnimation.AnimateTo( Property(mBacking, Actor::Property::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) );
535       mAnimation.AnimateTo( Property(self, Actor::Property::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) );
536     }
537     mAnimation.Play();
538     mAnimation.FinishedSignal().Connect(this, &Popup::OnStateAnimationFinished);
539   }
540   else
541   {
542     mBacking.SetOpacity( targetBackingAlpha );
543     self.SetScale( targetSize );
544
545     HandleStateChangeComplete();
546   }
547 }
548
549 void Popup::HandleStateChangeComplete()
550 {
551   // Remove contents from stage if completely hidden.
552   if( ( mState == Toolkit::Popup::POPUP_HIDE ) && mLayer.GetParent() )
553   {
554     mLayer.Unparent();
555     Self().SetSensitive( false );
556
557     // Guard against destruction during signal emission
558     Toolkit::Popup handle( GetOwner() );
559     mHiddenSignal.Emit();
560   }
561 }
562
563 Toolkit::Popup::TouchedOutsideSignalType& Popup::OutsideTouchedSignal()
564 {
565   return mTouchedOutsideSignal;
566 }
567
568 Toolkit::Popup::HiddenSignalType& Popup::HiddenSignal()
569 {
570   return mHiddenSignal;
571 }
572
573 bool Popup::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
574 {
575   Dali::BaseHandle handle( object );
576
577   bool connected( true );
578   Toolkit::Popup popup = Toolkit::Popup::DownCast( handle );
579
580   if( 0 == strcmp( signalName.c_str(), SIGNAL_TOUCHED_OUTSIDE ) )
581   {
582     popup.OutsideTouchedSignal().Connect( tracker, functor );
583   }
584   else if( 0 == strcmp( signalName.c_str(), SIGNAL_HIDDEN ) )
585   {
586     popup.HiddenSignal().Connect( tracker, functor );
587   }
588   else
589   {
590     // signalName does not match any signal
591     connected = false;
592   }
593
594   return connected;
595 }
596
597 void Popup::OnStateAnimationFinished( Animation& source )
598 {
599   HandleStateChangeComplete();
600 }
601
602 bool Popup::OnBackingTouched(Actor actor, const TouchEvent& event)
603 {
604   if(event.GetPointCount()>0)
605   {
606     const TouchPoint& point = event.GetPoint(0);
607
608     if(point.state == TouchPoint::Down)
609     {
610       // Guard against destruction during signal emission
611       Toolkit::Popup handle( GetOwner() );
612
613       mTouchedOutsideSignal.Emit();
614     }
615   }
616
617   return true;
618 }
619
620 bool Popup::OnBackingMouseWheelEvent(Actor actor, const MouseWheelEvent& event)
621 {
622   // consume mouse wheel event in dimmed backing actor
623   return true;
624 }
625
626 bool Popup::OnDialogTouched(Actor actor, const TouchEvent& event)
627 {
628   // consume event (stops backing actor receiving touch events)
629   return true;
630 }
631
632 void Popup::OnControlChildAdd( Actor& child )
633 {
634   // reparent any children added by user to the body layer.
635   if( mAlterAddedChild )
636   {
637     // Removes previously added content.
638     if( mContent )
639     {
640       mPopupLayout.RemoveChildAt( Toolkit::TableView::CellPosition( 1, 0 ) );
641     }
642
643     // keep a handle to the new content.
644     mContent = child;
645
646     mPopupLayout.AddChild( mContent, Toolkit::TableView::CellPosition( 1, 0 ) );
647   }
648 }
649
650 void Popup::OnRelayout( const Vector2& size, RelayoutContainer& container )
651 {
652   // Hide the background image
653   mBackgroundImage.SetVisible( !( mButtons.empty() && mPopupLayout.GetChildCount() == 0 ) );
654
655   // Relayout All buttons
656   if ( !mButtons.empty() )
657   {
658     // All buttons should be the same size and fill the button area. The button spacing needs to be accounted for as well.
659     Vector2 buttonSize( ( ( size.width - mPopupStyle->buttonSpacing * ( mButtons.size() + 1 ) ) / mButtons.size() ),
660                         mPopupStyle->bottomSize.height - mPopupStyle->margin );
661
662     Vector3 buttonPosition( mPopupStyle->buttonSpacing, 0.0f, 0.0f );
663
664     for ( ActorIter iter = mButtons.begin(), endIter = mButtons.end();
665           iter != endIter;
666           ++iter, buttonPosition.x += mPopupStyle->buttonSpacing + buttonSize.width )
667     {
668       Actor button = *iter;
669
670       // If there is only one button, it needs to be laid out on center.
671       if ( mButtons.size() == 1 )
672       {
673         buttonPosition.x = 0.0f;
674         button.SetAnchorPoint( AnchorPoint::CENTER );
675         button.SetParentOrigin( ParentOrigin::CENTER );
676       }
677       else
678       {
679         button.SetAnchorPoint( AnchorPoint::CENTER_LEFT );
680         button.SetParentOrigin( ParentOrigin::CENTER_LEFT );
681       }
682
683       button.SetPosition( buttonPosition );
684
685       button.PropagateRelayoutFlags();    // Reset relayout flags for relayout
686       container.Add( button, buttonSize );
687     }
688   }
689 }
690
691 void Popup::OnSetResizePolicy( ResizePolicy policy, Dimension dimension )
692 {
693   if( mPopupLayout )
694   {
695     if( policy == FIT_TO_CHILDREN )
696     {
697       mPopupLayout.SetResizePolicy( USE_NATURAL_SIZE, dimension );
698       if( dimension & HEIGHT )
699       {
700         mPopupLayout.SetFitHeight( 1 );
701       }
702     }
703     else
704     {
705       mPopupLayout.SetResizePolicy( FILL_TO_PARENT, dimension );
706       // Make the content cell fill the whole of the available space
707       if( dimension & HEIGHT )
708       {
709         mPopupLayout.SetRelativeHeight( 1, 1.0f );
710       }
711     }
712   }
713 }
714
715 bool Popup::OnKeyEvent(const KeyEvent& event)
716 {
717   bool consumed = false;
718
719   if(event.state == KeyEvent::Down)
720   {
721     if (event.keyCode == Dali::DALI_KEY_ESCAPE || event.keyCode == Dali::DALI_KEY_BACK)
722     {
723       SetState(Toolkit::Popup::POPUP_HIDE);
724       consumed = true;
725     }
726   }
727
728   return consumed;
729 }
730
731 Vector3 Popup::GetNaturalSize()
732 {
733   float margin = 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin );
734   const float maxWidth = Stage::GetCurrent().GetSize().width - margin;
735
736   Vector3 naturalSize( 0.0f, 0.0f, 0.0f );
737
738   if ( mTitle )
739   {
740     Vector3 titleNaturalSize = mTitle.GetImplementation().GetNaturalSize();
741     // Buffer to avoid errors. The width of the popup could potentially be the width of the title text.
742     // It was observed in this case that text wrapping was then inconsistent when seen on device
743     const float titleBuffer = 0.5f;
744     titleNaturalSize.width += titleBuffer;
745
746     // As TextLabel GetNaturalSize does not take wrapping into account, limit the width
747     // to that of the stage
748     if( titleNaturalSize.width >= maxWidth)
749     {
750       naturalSize.width = maxWidth;
751       naturalSize.height = mTitle.GetImplementation().GetHeightForWidth( naturalSize.width );
752     }
753     else
754     {
755       naturalSize += titleNaturalSize;
756     }
757
758     naturalSize.height += mPopupStyle->margin;
759   }
760
761   if( mContent )
762   {
763     Vector3 contentSize = mContent.GetNaturalSize();
764     // Choose the biggest width
765     naturalSize.width = std::max( naturalSize.width, contentSize.width );
766     if( naturalSize.width > maxWidth )
767     {
768       naturalSize.width = maxWidth;
769       contentSize.height = mContent.GetHeightForWidth( maxWidth );
770     }
771     naturalSize.height += contentSize.height + mPopupStyle->margin;
772   }
773
774   if( !mButtons.empty() )
775   {
776     naturalSize.height += mPopupStyle->bottomSize.height;
777   }
778
779   // Add the margins
780   naturalSize.width += margin;
781   naturalSize.height += margin;
782
783   return naturalSize;
784 }
785
786 float Popup::GetHeightForWidth( float width )
787 {
788   float height( 0.0f );
789   float popupWidth( width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) );
790
791   if ( mTitle )
792   {
793     height += mTitle.GetImplementation().GetHeightForWidth( popupWidth );
794     height += mPopupStyle->margin;
795   }
796
797   if( mContent )
798   {
799     height += mContent.GetHeightForWidth( popupWidth ) + mPopupStyle->margin;
800   }
801
802   if( !mButtons.empty() )
803   {
804     height += mPopupStyle->bottomSize.height;
805   }
806
807   // Add the margins
808   float margin( 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) );
809   height += margin;
810
811   return height;
812 }
813
814 float Popup::GetWidthForHeight( float height )
815 {
816   return GetNaturalSize().width;
817 }
818
819 Actor Popup::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
820 {
821   Actor nextFocusableActor( currentFocusedActor );
822
823   // TODO: Needs to be optimised
824
825   if ( !currentFocusedActor || ( currentFocusedActor && KeyboardFocusManager::Get().GetFocusGroup(currentFocusedActor) != Self() ) )
826   {
827     // The current focused actor is not within popup
828     if( mContent && mContent.IsKeyboardFocusable() )
829     {
830       // If content is focusable, move the focus to content
831       nextFocusableActor = mContent;
832     }
833     else if( !mButtons.empty() )
834     {
835       // Otherwise, movethe focus to the first button
836       nextFocusableActor = mButtons[0];
837     }
838   }
839   else
840   {
841     // Rebuild the focus chain because button or content can be added or removed dynamically
842     ActorContainer focusableActors;
843     if( mContent && mContent.IsKeyboardFocusable() )
844     {
845       focusableActors.push_back(mContent);
846     }
847
848     for(unsigned int i = 0; i < mButtons.size(); i++)
849     {
850       if( mButtons[i] && mButtons[i].IsKeyboardFocusable() )
851       {
852         focusableActors.push_back(mButtons[i]);
853       }
854     }
855
856     for ( ActorContainer::iterator iter = focusableActors.begin(), end = focusableActors.end(); iter != end; ++iter )
857     {
858       if ( currentFocusedActor == *iter )
859       {
860         switch ( direction )
861         {
862           case Toolkit::Control::Left:
863           {
864             if ( iter == focusableActors.begin() )
865             {
866               nextFocusableActor = *( focusableActors.end() - 1 );
867             }
868             else
869             {
870               nextFocusableActor = *( iter - 1 );
871             }
872             break;
873           }
874           case Toolkit::Control::Right:
875           {
876             if ( iter == focusableActors.end() - 1 )
877             {
878               nextFocusableActor = *( focusableActors.begin() );
879             }
880             else
881             {
882               nextFocusableActor = *( iter + 1 );
883             }
884             break;
885           }
886
887           case Toolkit::Control::Up:
888           {
889             if ( *iter == mContent )
890             {
891               nextFocusableActor = *( focusableActors.end() - 1 );
892             }
893             else
894             {
895               if ( mContent && mContent.IsKeyboardFocusable() )
896               {
897                 nextFocusableActor = mContent;
898               }
899               else
900               {
901                 if ( iter == focusableActors.begin() )
902                 {
903                   nextFocusableActor = *( focusableActors.end() - 1 );
904                 }
905                 else
906                 {
907                   nextFocusableActor = *( iter - 1 );
908                 }
909               }
910             }
911             break;
912           }
913
914           case Toolkit::Control::Down:
915           {
916             if ( mContent && mContent.IsKeyboardFocusable() )
917             {
918               nextFocusableActor = mContent;
919             }
920             else
921             {
922               if ( iter == focusableActors.end() - 1 )
923               {
924                 nextFocusableActor = *( focusableActors.begin() );
925               }
926               else
927               {
928                 nextFocusableActor = *( iter + 1 );
929               }
930             }
931
932             if ( *iter == mContent && !mButtons.empty() )
933             {
934               nextFocusableActor = mButtons[0];
935             }
936             break;
937           }
938         }
939
940         if(!nextFocusableActor)
941         {
942           DALI_LOG_WARNING("Can not decide next focusable actor\n");
943         }
944
945         break;
946       }
947     }
948   }
949
950   return nextFocusableActor;
951 }
952
953 } // namespace Internal
954
955 } // namespace Toolkit
956
957 } // namespace Dali