Property refactor in dali-toolkit: Toolkit changes
[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   Toolkit::TextView titleActor = Toolkit::TextView::New();
260   titleActor.SetText( text );
261   titleActor.SetColor( Color::BLACK );
262   titleActor.SetMultilinePolicy( Toolkit::TextView::SplitByWord );
263   titleActor.SetWidthExceedPolicy( Toolkit::TextView::Split );
264   titleActor.SetLineJustification( Toolkit::TextView::Center );
265
266   SetTitle( titleActor );
267 }
268
269 void Popup::SetTitle( Toolkit::TextView titleActor )
270 {
271   // Replaces the current title actor.
272   if( mTitle && mPopupBg )
273   {
274     mPopupBg.Remove( mTitle );
275   }
276   mTitle = titleActor;
277
278   mPopupBg.Add( mTitle );
279
280   RelayoutRequest();
281 }
282
283 Toolkit::TextView Popup::GetTitle() const
284 {
285   return mTitle;
286 }
287
288 void Popup::AddButton( Toolkit::Button button )
289 {
290   mButtons.push_back( button );
291   mBottomBg.Add( button );
292
293   RelayoutRequest();
294 }
295
296 void Popup::SetState( Toolkit::Popup::PopupState state )
297 {
298   SetState( state, POPUP_ANIMATION_DURATION );
299 }
300
301 void Popup::SetState( Toolkit::Popup::PopupState state, float duration )
302 {
303   // default animation behaviour.
304   HandleStateChange(state, duration);
305 }
306
307 Toolkit::Popup::PopupState Popup::GetState() const
308 {
309   return mState;
310 }
311
312 void Popup::ShowTail(const Vector3& position)
313 {
314   // Replaces the tail actor.
315   if(mTailImage && mTailImage.GetParent())
316   {
317     mTailImage.GetParent().Remove( mTailImage );
318     mTailImage.Reset();
319   }
320
321   std::string image = "";
322
323   // depending on position of tail around ParentOrigin, a different tail image is used...
324   if(position.y < Math::MACHINE_EPSILON_1)
325   {
326     image = mPopupStyle->tailUpImage;
327   }
328   else if(position.y > 1.0f - Math::MACHINE_EPSILON_1)
329   {
330     image = mPopupStyle->tailDownImage;
331   }
332   else if(position.x < Math::MACHINE_EPSILON_1)
333   {
334     image = mPopupStyle->tailLeftImage;
335   }
336   else if(position.x > 1.0f - Math::MACHINE_EPSILON_1)
337   {
338     image = mPopupStyle->tailRightImage;
339   }
340
341   if(image != "")
342   {
343     Image tail = ResourceImage::New( image );
344     mTailImage = ImageActor::New(tail);
345     const Vector3 anchorPoint = AnchorPoint::BOTTOM_RIGHT - position;
346
347     mTailImage.SetParentOrigin(position);
348     mTailImage.SetAnchorPoint(anchorPoint);
349
350     mBottomBg.Add(mTailImage);
351   }
352 }
353
354 void Popup::HideTail()
355 {
356   ShowTail(ParentOrigin::CENTER);
357 }
358
359 void Popup::SetStyle(PopupStyle& style)
360 {
361   mPopupStyle = PopupStylePtr(&style);
362   // update //
363 }
364
365 PopupStylePtr Popup::GetStyle() const
366 {
367   return mPopupStyle;
368 }
369
370 void Popup::SetDefaultBackgroundImage()
371 {
372   Image bg = ResourceImage::New( mPopupStyle->backgroundImage );
373   ImageActor bgImage = ImageActor::New( bg );
374   bgImage.SetStyle( ImageActor::STYLE_NINE_PATCH );
375   bgImage.SetNinePatchBorder( mPopupStyle->backgroundScale9Border );
376
377   Image buttonBg = ResourceImage::New( mPopupStyle->buttonAreaImage );
378   ImageActor buttonBgImage = ImageActor::New( buttonBg );
379   buttonBgImage.SetStyle( ImageActor::STYLE_NINE_PATCH );
380   buttonBgImage.SetNinePatchBorder( mPopupStyle->buttonArea9PatchBorder );
381
382   SetBackgroundImage( bgImage );
383   SetButtonAreaImage( buttonBgImage );
384 }
385
386 void Popup::CreateBacking()
387 {
388   mBacking = Dali::Toolkit::CreateSolidColorActor( mPopupStyle->backingColor );
389
390   mBacking.SetPositionInheritanceMode(DONT_INHERIT_POSITION);
391   mBacking.SetSensitive(true);
392
393   mLayer.Add(mBacking);
394   mBacking.SetOpacity(0.0f);
395   mBacking.SetPosition(0.0f, 0.0f, BACKING_DEPTH);
396   mBacking.TouchedSignal().Connect( this, &Popup::OnBackingTouched );
397   mBacking.MouseWheelEventSignal().Connect(this, &Popup::OnBackingMouseWheelEvent);
398 }
399
400 void Popup::CreateDialog()
401 {
402   // Adds default background image.
403   SetDefaultBackgroundImage();
404
405   // Adds bottom background
406   mBottomBg = Actor::New();
407   mPopupBg.Add( mBottomBg );
408 }
409
410 void Popup::HandleStateChange( Toolkit::Popup::PopupState state, float duration )
411 {
412   const Vector2& stageSize( Stage::GetCurrent().GetSize() );
413
414   Vector3 targetSize;
415   float targetBackingAlpha;
416   Vector3 targetBackingSize;
417
418   if(mState == state)
419   {
420     return;
421   }
422   mState = state;
423   switch(state)
424   {
425     case Toolkit::Popup::POPUP_HIDE:
426     {
427       targetSize = Vector3(0.0f, 0.0f, 1.0f);
428       targetBackingAlpha = 0.0f;
429       targetBackingSize = Vector3(0.0f, 0.0f, 1.0f);
430       mShowing = false;
431       ClearKeyInputFocus();
432
433       // Retore the keyboard focus when popup is hidden
434       if(mPreviousFocusedActor && mPreviousFocusedActor.IsKeyboardFocusable() )
435       {
436         Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
437         if( keyboardFocusManager )
438         {
439           keyboardFocusManager.SetCurrentFocusActor(mPreviousFocusedActor);
440         }
441       }
442
443       break;
444     }
445
446     case Toolkit::Popup::POPUP_SHOW:
447     default:
448     {
449       targetSize = Vector3(1.0f, 1.0f, 1.0f);
450       targetBackingAlpha = 1.0f;
451       float length = (stageSize.width > stageSize.height) ? stageSize.width : stageSize.height;
452       targetBackingSize = Vector3( length, length, 1.0f );
453       mShowing = true;
454
455       // Add contents to stage for showing.
456       if( !mLayer.GetParent() )
457       {
458         mAlterAddedChild = false;
459         Self().Add(mLayer);
460         mAlterAddedChild = true;
461       }
462       Self().SetSensitive(true);
463       SetKeyInputFocus();
464
465       // Handle the keyboard focus when popup is shown
466       Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
467       if( keyboardFocusManager )
468       {
469         mPreviousFocusedActor = keyboardFocusManager.GetCurrentFocusActor();
470
471         if( mContent && mContent.IsKeyboardFocusable() )
472         {
473           // If content is focusable, move the focus to content
474           keyboardFocusManager.SetCurrentFocusActor(mContent);
475         }
476         else if( !mButtons.empty() )
477         {
478           // Otherwise, movethe focus to the first button
479           keyboardFocusManager.SetCurrentFocusActor(mButtons[0]);
480         }
481         else
482         {
483           DALI_LOG_WARNING("There is no focusable in popup\n");
484         }
485       }
486       break;
487     }
488   }
489
490   mBacking.SetSize( targetBackingSize );
491
492   if(duration > Math::MACHINE_EPSILON_1)
493   {
494     if ( mAnimation )
495     {
496       mAnimation.Stop();
497       mAnimation.Clear();
498       mAnimation.Reset();
499     }
500     mAnimation = Animation::New(duration);
501
502     if(mShowing)
503     {
504       mAnimation.AnimateTo( Property(mBacking, Actor::Property::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) );
505       mAnimation.AnimateTo( Property(mPopupBg, Actor::Property::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(duration * 0.5f, duration * 0.5f) );
506     }
507     else
508     {
509       mAnimation.AnimateTo( Property(mBacking, Actor::Property::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) );
510       mAnimation.AnimateTo( Property(mPopupBg, Actor::Property::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) );
511     }
512     mAnimation.Play();
513     mAnimation.FinishedSignal().Connect(this, &Popup::OnStateAnimationFinished);
514   }
515   else
516   {
517     mBacking.SetOpacity( targetBackingAlpha );
518     mPopupBg.SetScale( targetSize );
519
520     HandleStateChangeComplete();
521   }
522 }
523
524 void Popup::HandleStateChangeComplete()
525 {
526   // Remove contents from stage if completely hidden.
527   if( (mState == Toolkit::Popup::POPUP_HIDE) && (mLayer.GetParent()) )
528   {
529     Self().Remove(mLayer);
530     Self().SetSensitive( false );
531
532     // Guard against destruction during signal emission
533     Toolkit::Popup handle( GetOwner() );
534     mHiddenSignal.Emit();
535   }
536 }
537
538 Toolkit::Popup::TouchedOutsideSignalType& Popup::OutsideTouchedSignal()
539 {
540   return mTouchedOutsideSignal;
541 }
542
543 Toolkit::Popup::HiddenSignalType& Popup::HiddenSignal()
544 {
545   return mHiddenSignal;
546 }
547
548 bool Popup::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
549 {
550   Dali::BaseHandle handle( object );
551
552   bool connected( true );
553   Toolkit::Popup popup = Toolkit::Popup::DownCast( handle );
554
555   if( 0 == strcmp( signalName.c_str(), SIGNAL_TOUCHED_OUTSIDE ) )
556   {
557     popup.OutsideTouchedSignal().Connect( tracker, functor );
558   }
559   else if( 0 == strcmp( signalName.c_str(), SIGNAL_HIDDEN ) )
560   {
561     popup.HiddenSignal().Connect( tracker, functor );
562   }
563   else
564   {
565     // signalName does not match any signal
566     connected = false;
567   }
568
569   return connected;
570 }
571
572 void Popup::OnStateAnimationFinished( Animation& source )
573 {
574   HandleStateChangeComplete();
575 }
576
577 bool Popup::OnBackingTouched(Actor actor, const TouchEvent& event)
578 {
579   if(event.GetPointCount()>0)
580   {
581     const TouchPoint& point = event.GetPoint(0);
582
583     if(point.state == TouchPoint::Down)
584     {
585       // Guard against destruction during signal emission
586       Toolkit::Popup handle( GetOwner() );
587
588       mTouchedOutsideSignal.Emit();
589     }
590   }
591
592   return true;
593 }
594
595 bool Popup::OnBackingMouseWheelEvent(Actor actor, const MouseWheelEvent& event)
596 {
597   // consume mouse wheel event in dimmed backing actor
598   return true;
599 }
600
601 bool Popup::OnDialogTouched(Actor actor, const TouchEvent& event)
602 {
603   // consume event (stops backing actor receiving touch events)
604   return true;
605 }
606
607 void Popup::OnControlChildAdd( Actor& child )
608 {
609   // reparent any children added by user to the body layer.
610   if( mAlterAddedChild )
611   {
612     // Removes previously added content.
613     if( mContent )
614     {
615       mPopupBg.Remove( mContent );
616     }
617
618     // Reparent new content.
619     Self().Remove( child );
620
621     // keep a handle to the new content.
622     mContent = child;
623
624     mPopupBg.Add( mContent );
625   }
626 }
627
628 void Popup::OnControlSizeSet( const Vector3& targetSize )
629 {
630   mLayer.SetSize( targetSize );
631   mPopupBg.SetSize( targetSize );
632
633   const Vector4 outerBorder = mPopupStyle->backgroundOuterBorder;
634   if( mBackgroundImage )
635   {
636     mBackgroundImage.SetSize( BackgroundSize( outerBorder,targetSize ) );
637   }
638   if( mButtonAreaImage )
639   {
640     mButtonAreaImage.SetSize( ButtonAreaSize( outerBorder, targetSize ) );
641   }
642
643 }
644
645 void Popup::OnRelayout( const Vector2& size, ActorSizeContainer& container )
646 {
647   // Set the popup size
648   Vector2 popupSize;
649   popupSize.width  = size.width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin );
650   popupSize.height = size.height - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin );
651
652   // Update sizes of all popup's components.
653
654   // Relayout background image.
655   // Adjust background position and size relative to parent to cater to outer Border.
656   // Some backgrounds are intended to over-spill. That is some content
657   // should appear outside the Dialog on all sides i.e. Shadows, glow effects.
658   const Vector4 outerBorder = mPopupStyle->backgroundOuterBorder;
659
660   if( mBackgroundImage )
661   {
662     mBackgroundImage.SetSize(BackgroundSize(outerBorder, Vector3(size)));
663     mBackgroundImage.SetAnchorPoint( AnchorPoint::TOP_LEFT );
664     mBackgroundImage.SetParentOrigin( ParentOrigin::TOP_LEFT );
665     mBackgroundImage.SetPosition( -outerBorder.x, -outerBorder.y, 0.0f );
666   }
667
668   if( mPopupBg && mButtonAreaImage )
669   {
670     // If there are no buttons, button background is also removed.
671     if ( mButtons.size() == 0 )
672     {
673       mPopupBg.Remove( mButtonAreaImage );
674     }
675     else
676     {
677       mButtonAreaImage.SetSize( ButtonAreaSize(outerBorder, Vector3(size)) );
678       mButtonAreaImage.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
679       mButtonAreaImage.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
680       mButtonAreaImage.SetY( -outerBorder.z - POPUP_OUT_MARGIN_HEIGHT );
681
682       mPopupBg.Add( mButtonAreaImage );
683     }
684   }
685
686   // Relayout title
687   Vector3 positionOffset( 0.0f, mPopupStyle->margin + POPUP_OUT_MARGIN_WIDTH, CONTENT_DEPTH );
688   if( mTitle )
689   {
690     Vector2 titleSize;
691     titleSize.width  = popupSize.width;
692     titleSize.height = mTitle.GetHeightForWidth( titleSize.width );
693
694     // As the default size policy for text-view is Fixed & Fixed, a size needs to be set.
695     // Otherwise size-negotiation algorithm uses the GetNaturalSize() with doesn't take
696     // into account the multiline and exceed policies, giving as result a wrong size.
697     mTitle.SetSize( titleSize );
698     Relayout( mTitle, titleSize, container );
699
700     mTitle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
701     mTitle.SetParentOrigin( ParentOrigin::TOP_CENTER );
702     mTitle.SetPosition( positionOffset );
703
704     positionOffset.y += titleSize.height + mPopupStyle->margin;
705   }
706
707   // Relayout content
708   if( mContent )
709   {
710     // If the content width is greater than popup width then scale it down/wrap text as needed
711     Vector2 contentSize( RelayoutHelper::GetNaturalSize( mContent ) );
712     if( contentSize.width > popupSize.width )
713     {
714       contentSize.width = popupSize.width;
715       contentSize.height = RelayoutHelper::GetHeightForWidth( mContent, contentSize.width );
716     }
717
718     mContent.SetSize( contentSize );
719     Relayout( mContent, contentSize, container );
720
721     mContent.SetParentOrigin(ParentOrigin::TOP_CENTER);
722     mContent.SetAnchorPoint(AnchorPoint::TOP_CENTER);
723
724     mContent.SetPosition( positionOffset );
725
726     positionOffset.y += contentSize.height + mPopupStyle->margin;
727   }
728
729   // Relayout Button Area
730   if( mBottomBg )
731   {
732     mBottomBg.SetSize( popupSize.width, mPopupStyle->bottomSize.height );
733
734     mBottomBg.SetParentOrigin(ParentOrigin::TOP_CENTER);
735     mBottomBg.SetAnchorPoint(AnchorPoint::TOP_CENTER);
736
737     mBottomBg.SetPosition( positionOffset );
738   }
739
740   // Relayout All buttons
741   if ( !mButtons.empty() )
742   {
743     // All buttons should be the same size and fill the button area. The button spacing needs to be accounted for as well.
744     Vector2 buttonSize( ( ( popupSize.width - mPopupStyle->buttonSpacing * ( mButtons.size() - 1 ) ) / mButtons.size() ),
745                         mPopupStyle->bottomSize.height - mPopupStyle->margin );
746
747     Vector3 buttonPosition;
748
749     for ( ActorIter iter = mButtons.begin(), endIter = mButtons.end();
750           iter != endIter;
751           ++iter, buttonPosition.x += mPopupStyle->buttonSpacing + buttonSize.width )
752     {
753       iter->SetPosition( buttonPosition );
754
755       // If there is only one button, it needs to be laid out on center.
756       if ( mButtons.size() == 1 )
757       {
758         iter->SetAnchorPoint( AnchorPoint::CENTER );
759         iter->SetParentOrigin( ParentOrigin::CENTER );
760       }
761       else
762       {
763         iter->SetAnchorPoint( AnchorPoint::CENTER_LEFT );
764         iter->SetParentOrigin( ParentOrigin::CENTER_LEFT );
765       }
766
767       Relayout( *iter, buttonSize, container );
768     }
769   }
770
771   if( mShowing && mBacking )
772   {
773     Vector2 stageSize = Stage::GetCurrent().GetSize();
774     float length = (stageSize.width > stageSize.height) ? stageSize.width : stageSize.height;
775     Vector3 targetBackingSize = Vector3( length, length, 1.0f );
776
777     mBacking.SetSize( targetBackingSize );
778   }
779 }
780
781 bool Popup::OnKeyEvent(const KeyEvent& event)
782 {
783   bool consumed = false;
784
785   if(event.state == KeyEvent::Down)
786   {
787     if (event.keyCode == Dali::DALI_KEY_ESCAPE || event.keyCode == Dali::DALI_KEY_BACK)
788     {
789       SetState(Toolkit::Popup::POPUP_HIDE);
790       consumed = true;
791     }
792   }
793
794   return consumed;
795 }
796
797 Vector3 Popup::GetNaturalSize()
798 {
799   float margin = 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin );
800   const float maxWidth = Stage::GetCurrent().GetSize().width - margin;
801
802   Vector3 naturalSize( 0.0f, 0.0f, 0.0f );
803
804   if ( mTitle )
805   {
806     Vector3 titleNaturalSize = mTitle.GetImplementation().GetNaturalSize();
807     // Buffer to avoid errors. The width of the popup could potentially be the width of the title text.
808     // It was observed in this case that text wrapping was then inconsistent when seen on device
809     const float titleBuffer = 0.5f;
810     titleNaturalSize.width += titleBuffer;
811
812     // As TextView GetNaturalSize does not take wrapping into account, limit the width
813     // to that of the stage
814     if( titleNaturalSize.width >= maxWidth)
815     {
816       naturalSize.width = maxWidth;
817       naturalSize.height = mTitle.GetImplementation().GetHeightForWidth( naturalSize.width );
818     }
819     else
820     {
821       naturalSize += titleNaturalSize;
822     }
823
824     naturalSize.height += mPopupStyle->margin;
825   }
826
827   if( mContent )
828   {
829     Vector3 contentSize = RelayoutHelper::GetNaturalSize( mContent );
830     // Choose the biggest width
831     naturalSize.width = std::max( naturalSize.width, contentSize.width );
832     if( naturalSize.width > maxWidth )
833     {
834       naturalSize.width = maxWidth;
835       contentSize.height = RelayoutHelper::GetHeightForWidth( mContent, maxWidth );
836     }
837     naturalSize.height += contentSize.height + mPopupStyle->margin;
838   }
839
840   if( !mButtons.empty() )
841   {
842     naturalSize.height += mPopupStyle->bottomSize.height;
843   }
844
845   // Add the margins
846   naturalSize.width += margin;
847   naturalSize.height += margin;
848
849   return naturalSize;
850 }
851
852 float Popup::GetHeightForWidth( float width )
853 {
854   float height( 0.0f );
855   float popupWidth( width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) );
856
857   if ( mTitle )
858   {
859     height += mTitle.GetImplementation().GetHeightForWidth( popupWidth );
860     height += mPopupStyle->margin;
861   }
862
863   if( mContent )
864   {
865     height += RelayoutHelper::GetHeightForWidth( mContent, popupWidth ) + mPopupStyle->margin;
866   }
867
868   if( !mButtons.empty() )
869   {
870     height += mPopupStyle->bottomSize.height;
871   }
872
873   // Add the margins
874   float margin( 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) );
875   height += margin;
876
877   return height;
878 }
879
880 float Popup::GetWidthForHeight( float height )
881 {
882   return GetNaturalSize().width;
883 }
884
885 Actor Popup::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
886 {
887   Actor nextFocusableActor( currentFocusedActor );
888
889   // TODO: Needs to be optimised
890
891   if ( !currentFocusedActor || ( currentFocusedActor && KeyboardFocusManager::Get().GetFocusGroup(currentFocusedActor) != Self() ) )
892   {
893     // The current focused actor is not within popup
894     if( mContent && mContent.IsKeyboardFocusable() )
895     {
896       // If content is focusable, move the focus to content
897       nextFocusableActor = mContent;
898     }
899     else if( !mButtons.empty() )
900     {
901       // Otherwise, movethe focus to the first button
902       nextFocusableActor = mButtons[0];
903     }
904   }
905   else
906   {
907     // Rebuild the focus chain because button or content can be added or removed dynamically
908     ActorContainer focusableActors;
909     if( mContent && mContent.IsKeyboardFocusable() )
910     {
911       focusableActors.push_back(mContent);
912     }
913
914     for(unsigned int i = 0; i < mButtons.size(); i++)
915     {
916       if( mButtons[i] && mButtons[i].IsKeyboardFocusable() )
917       {
918         focusableActors.push_back(mButtons[i]);
919       }
920     }
921
922     for ( ActorContainer::iterator iter = focusableActors.begin(), end = focusableActors.end(); iter != end; ++iter )
923     {
924       if ( currentFocusedActor == *iter )
925       {
926         switch ( direction )
927         {
928           case Toolkit::Control::Left:
929           {
930             if ( iter == focusableActors.begin() )
931             {
932               nextFocusableActor = *( focusableActors.end() - 1 );
933             }
934             else
935             {
936               nextFocusableActor = *( iter - 1 );
937             }
938             break;
939           }
940           case Toolkit::Control::Right:
941           {
942             if ( iter == focusableActors.end() - 1 )
943             {
944               nextFocusableActor = *( focusableActors.begin() );
945             }
946             else
947             {
948               nextFocusableActor = *( iter + 1 );
949             }
950             break;
951           }
952
953           case Toolkit::Control::Up:
954           {
955             if ( *iter == mContent )
956             {
957               nextFocusableActor = *( focusableActors.end() - 1 );
958             }
959             else
960             {
961               if ( mContent && mContent.IsKeyboardFocusable() )
962               {
963                 nextFocusableActor = mContent;
964               }
965               else
966               {
967                 if ( iter == focusableActors.begin() )
968                 {
969                   nextFocusableActor = *( focusableActors.end() - 1 );
970                 }
971                 else
972                 {
973                   nextFocusableActor = *( iter - 1 );
974                 }
975               }
976             }
977             break;
978           }
979
980           case Toolkit::Control::Down:
981           {
982             if ( mContent && mContent.IsKeyboardFocusable() )
983             {
984               nextFocusableActor = mContent;
985             }
986             else
987             {
988               if ( iter == focusableActors.end() - 1 )
989               {
990                 nextFocusableActor = *( focusableActors.begin() );
991               }
992               else
993               {
994                 nextFocusableActor = *( iter + 1 );
995               }
996             }
997
998             if ( *iter == mContent && !mButtons.empty() )
999             {
1000               nextFocusableActor = mButtons[0];
1001             }
1002             break;
1003           }
1004         }
1005
1006         if(!nextFocusableActor)
1007         {
1008           DALI_LOG_WARNING("Can not decide next focusable actor\n");
1009         }
1010
1011         break;
1012       }
1013     }
1014   }
1015
1016   return nextFocusableActor;
1017 }
1018
1019 } // namespace Internal
1020
1021 } // namespace Toolkit
1022
1023 } // namespace Dali