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