Controls no longer have property names.
[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 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 : ControlImpl(true),
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     Vector2 contentSize;
736     contentSize.width = popupSize.width;
737
738     Toolkit::Control control = Toolkit::Control::DownCast( mContent );
739     if( control )
740     {
741       contentSize.height = control.GetHeightForWidth( contentSize.width );
742     }
743     else
744     {
745       contentSize.height = RelayoutHelper::GetHeightForWidth( mContent, contentSize.width );
746     }
747
748     mContent.SetSize( contentSize );
749     Relayout( mContent, contentSize, container );
750
751     mContent.SetParentOrigin(ParentOrigin::TOP_CENTER);
752     mContent.SetAnchorPoint(AnchorPoint::TOP_CENTER);
753
754     mContent.SetPosition( positionOffset );
755
756     positionOffset.y += contentSize.height + mPopupStyle->margin;
757   }
758
759   // Relayout Button Area
760   if( mBottomBg )
761   {
762     mBottomBg.SetSize( popupSize.width, mPopupStyle->bottomSize.height );
763
764     mBottomBg.SetParentOrigin(ParentOrigin::TOP_CENTER);
765     mBottomBg.SetAnchorPoint(AnchorPoint::TOP_CENTER);
766
767     mBottomBg.SetPosition( positionOffset );
768   }
769
770   // Relayout All buttons
771   if ( !mButtons.empty() )
772   {
773     // All buttons should be the same size and fill the button area. The button spacing needs to be accounted for as well.
774     Vector2 buttonSize( ( ( popupSize.width - mPopupStyle->buttonSpacing * ( mButtons.size() - 1 ) ) / mButtons.size() ),
775                         mPopupStyle->bottomSize.height - mPopupStyle->margin );
776
777     Vector3 buttonPosition;
778
779     for ( ActorIter iter = mButtons.begin(), endIter = mButtons.end();
780           iter != endIter;
781           ++iter, buttonPosition.x += mPopupStyle->buttonSpacing + buttonSize.width )
782     {
783       iter->SetPosition( buttonPosition );
784
785       // If there is only one button, it needs to be laid out on center.
786       if ( mButtons.size() == 1 )
787       {
788         iter->SetAnchorPoint( AnchorPoint::CENTER );
789         iter->SetParentOrigin( ParentOrigin::CENTER );
790       }
791       else
792       {
793         iter->SetAnchorPoint( AnchorPoint::CENTER_LEFT );
794         iter->SetParentOrigin( ParentOrigin::CENTER_LEFT );
795       }
796
797       Relayout( *iter, buttonSize, container );
798     }
799   }
800
801   if( mShowing && mBacking )
802   {
803     Vector2 stageSize = Stage::GetCurrent().GetSize();
804     float length = (stageSize.width > stageSize.height) ? stageSize.width : stageSize.height;
805     Vector3 targetBackingSize = Vector3( length, length, 1.0f );
806
807     mBacking.SetSize( targetBackingSize );
808   }
809 }
810
811 bool Popup::OnKeyEvent(const KeyEvent& event)
812 {
813   bool consumed = false;
814
815   if(event.state == KeyEvent::Down)
816   {
817     if (event.keyCode == Dali::DALI_KEY_ESCAPE || event.keyCode == Dali::DALI_KEY_BACK)
818     {
819       SetState(Toolkit::Popup::POPUP_HIDE);
820       consumed = true;
821     }
822   }
823
824   return consumed;
825 }
826
827 Vector3 Popup::GetNaturalSize()
828 {
829   Vector3 naturalSize;
830
831   if ( mTitle )
832   {
833     naturalSize += mTitle.GetImplementation().GetNaturalSize();
834     naturalSize.height += mPopupStyle->margin;
835   }
836
837   if( mContent )
838   {
839     Vector3 contentSize;
840
841     Toolkit::Control control = Toolkit::Control::DownCast( mContent );
842     if( control )
843     {
844       contentSize = control.GetImplementation().GetNaturalSize();
845     }
846     else
847     {
848       contentSize = RelayoutHelper::GetNaturalSize( mContent );
849     }
850
851     naturalSize.width = std::max( naturalSize.width, contentSize.width );
852     naturalSize.height += contentSize.height + mPopupStyle->margin;
853   }
854
855   if( !mButtons.empty() )
856   {
857     naturalSize.height += mPopupStyle->bottomSize.height;
858   }
859
860   // Add the margins
861   float margin( 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) );
862   naturalSize.width += margin;
863   naturalSize.height += margin;
864
865   return naturalSize;
866 }
867
868 float Popup::GetHeightForWidth( float width )
869 {
870   float height( 0.0f );
871   float popupWidth( width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) );
872
873   if ( mTitle )
874   {
875     height += mTitle.GetImplementation().GetHeightForWidth( popupWidth );
876     height += mPopupStyle->margin;
877   }
878
879   if( mContent )
880   {
881     float contentHeight;
882
883     Toolkit::Control control = Toolkit::Control::DownCast( mContent );
884     if( control )
885     {
886       contentHeight = control.GetImplementation().GetHeightForWidth( popupWidth );
887     }
888     else
889     {
890       contentHeight = RelayoutHelper::GetHeightForWidth( mContent, popupWidth );
891     }
892
893     height += contentHeight + mPopupStyle->margin;
894   }
895
896   if( !mButtons.empty() )
897   {
898     height += mPopupStyle->bottomSize.height;
899   }
900
901   // Add the margins
902   float margin( 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) );
903   height += margin;
904
905   return height;
906 }
907
908 float Popup::GetWidthForHeight( float height )
909 {
910   return GetNaturalSize().width;
911 }
912
913 Actor Popup::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
914 {
915   Actor nextFocusableActor( currentFocusedActor );
916
917   // TODO: Needs to be optimised
918
919   if ( !currentFocusedActor || ( currentFocusedActor && KeyboardFocusManager::Get().GetFocusGroup(currentFocusedActor) != Self() ) )
920   {
921     // The current focused actor is not within popup
922     if( mContent && mContent.IsKeyboardFocusable() )
923     {
924       // If content is focusable, move the focus to content
925       nextFocusableActor = mContent;
926     }
927     else if( !mButtons.empty() )
928     {
929       // Otherwise, movethe focus to the first button
930       nextFocusableActor = mButtons[0];
931     }
932   }
933   else
934   {
935     // Rebuild the focus chain because button or content can be added or removed dynamically
936     ActorContainer focusableActors;
937     if( mContent && mContent.IsKeyboardFocusable() )
938     {
939       focusableActors.push_back(mContent);
940     }
941
942     for(unsigned int i = 0; i < mButtons.size(); i++)
943     {
944       if( mButtons[i] && mButtons[i].IsKeyboardFocusable() )
945       {
946         focusableActors.push_back(mButtons[i]);
947       }
948     }
949
950     for ( ActorContainer::iterator iter = focusableActors.begin(), end = focusableActors.end(); iter != end; ++iter )
951     {
952       if ( currentFocusedActor == *iter )
953       {
954         switch ( direction )
955         {
956           case Control::Left:
957           {
958             if ( iter == focusableActors.begin() )
959             {
960               nextFocusableActor = *( focusableActors.end() - 1 );
961             }
962             else
963             {
964               nextFocusableActor = *( iter - 1 );
965             }
966             break;
967           }
968           case Control::Right:
969           {
970             if ( iter == focusableActors.end() - 1 )
971             {
972               nextFocusableActor = *( focusableActors.begin() );
973             }
974             else
975             {
976               nextFocusableActor = *( iter + 1 );
977             }
978             break;
979           }
980
981           case Control::Up:
982           {
983             if ( *iter == mContent )
984             {
985               nextFocusableActor = *( focusableActors.end() - 1 );
986             }
987             else
988             {
989               if ( mContent && mContent.IsKeyboardFocusable() )
990               {
991                 nextFocusableActor = mContent;
992               }
993               else
994               {
995                 if ( iter == focusableActors.begin() )
996                 {
997                   nextFocusableActor = *( focusableActors.end() - 1 );
998                 }
999                 else
1000                 {
1001                   nextFocusableActor = *( iter - 1 );
1002                 }
1003               }
1004             }
1005             break;
1006           }
1007
1008           case Control::Down:
1009           {
1010             if ( mContent && mContent.IsKeyboardFocusable() )
1011             {
1012               nextFocusableActor = mContent;
1013             }
1014             else
1015             {
1016               if ( iter == focusableActors.end() - 1 )
1017               {
1018                 nextFocusableActor = *( focusableActors.begin() );
1019               }
1020               else
1021               {
1022                 nextFocusableActor = *( iter + 1 );
1023               }
1024             }
1025
1026             if ( *iter == mContent && !mButtons.empty() )
1027             {
1028               nextFocusableActor = mButtons[0];
1029             }
1030             break;
1031           }
1032         }
1033
1034         if(!nextFocusableActor)
1035         {
1036           DALI_LOG_WARNING("Can not decide next focusable actor\n");
1037         }
1038
1039         break;
1040       }
1041     }
1042   }
1043
1044   return nextFocusableActor;
1045 }
1046
1047 } // namespace Internal
1048
1049 } // namespace Toolkit
1050
1051 } // namespace Dali