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