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