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