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