2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 // http://floralicense.org/license/
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.
17 #include <dali-toolkit/internal/controls/popup/popup-impl.h>
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>
23 #include <dali-toolkit/internal/controls/relayout-helper.h>
24 #include <dali-toolkit/internal/focus-manager/keyboard-focus-manager-impl.h>
26 #include <dali-toolkit/public-api/focus-manager/focus-manager.h>
27 #include <dali/integration-api/debug.h>
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)
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);
46 const char* const PROPERTY_TITLE = "title";
47 const char* const PROPERTY_STATE = "state";
49 // Constraints ///////////////////////////////////////////////////////////////////////////
52 * BackgroundSizeConstraint
54 * The background size should be at least as big as the Dialog.
55 * In some cases a background may have graphics which are visible
56 * outside of the Dialog, e.g. A Shadow. For this we need to alter
57 * the size of Background.
59 struct BackgroundSizeConstraint
62 * Constraint that sets size to parent's size plus a border.
64 * @param[in] outerBorder The border to extend beyond parent's Size.
66 BackgroundSizeConstraint( Vector4 outerBorder )
67 : mOuterBorder( outerBorder )
72 * (render thread code)
73 * @param[in] current The current size.
74 * @param[in] parentSizeProperty The parent's size
76 Vector3 operator()( const Vector3& current,
77 const PropertyInput& parentSizeProperty )
79 Vector3 size = parentSizeProperty.GetVector3();
81 size.width += mOuterBorder.x + mOuterBorder.y;
82 size.height += mOuterBorder.z + mOuterBorder.w;
87 const Vector4 mOuterBorder; ///< The size of the outer-border (Set to 0.0, 0.0f, 0.0f, 0.0f if doesn't exist).
90 struct ButtonAreaSizeConstraint
93 * Constraint that sets size to parent's size plus a border.
95 * @param[in] outerBorder The border to extend beyond parent's Size.
97 ButtonAreaSizeConstraint( Vector4 outerBorder )
98 : mOuterBorder( outerBorder )
103 * (render thread code)
104 * @param[in] current The current size.
105 * @param[in] parentSizeProperty The parent's size
107 Vector3 operator()( const Vector3& current,
108 const PropertyInput& parentSizeProperty )
110 Vector3 size = parentSizeProperty.GetVector3();
112 size.width += mOuterBorder.x + mOuterBorder.y;
113 size.width -= (POPUP_OUT_MARGIN_WIDTH + POPUP_OUT_MARGIN_WIDTH);
114 size.height = POPUP_BUTTON_BG_HEIGHT;
119 const Vector4 mOuterBorder; ///< The size of the outer-border (Set to 0.0, 0.0f, 0.0f, 0.0f if doesn't exist).
122 } // unnamed namespace
138 return Toolkit::Popup::New();
141 TypeRegistration typeRegistration( typeid(Toolkit::Popup), typeid(Toolkit::Control), Create );
143 SignalConnectorType signalConnector1( typeRegistration, Toolkit::Popup::SIGNAL_TOUCHED_OUTSIDE, &Popup::DoConnectSignal );
144 SignalConnectorType signalConnector2( typeRegistration, Toolkit::Popup::SIGNAL_HIDDEN, &Popup::DoConnectSignal );
150 ///////////////////////////////////////////////////////////////////////////////////////////////////
152 ///////////////////////////////////////////////////////////////////////////////////////////////////
154 Dali::Toolkit::Popup Popup::New()
156 PopupStylePtr style = PopupStyleDefault::New();
158 // Create the implementation
159 PopupPtr popup(new Popup(*style));
161 // Pass ownership to CustomActor via derived handle
162 Dali::Toolkit::Popup handle(*popup);
164 // Second-phase init of the implementation
165 // This can only be done after the CustomActor connection has been made...
171 Popup::Popup(PopupStyle& style)
172 : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_THEME_CHANGE_SIGNALS ) ),
174 mState(Toolkit::Popup::POPUP_NONE), // Initially, the popup state should not be set, it's set in OnInitialize
175 mAlterAddedChild(false),
176 mPopupStyle(PopupStylePtr(&style))
178 SetKeyboardNavigationSupport( true );
181 void Popup::OnInitialize()
184 self.SetSensitive(false);
187 mLayer = Layer::New();
188 mLayer.SetParentOrigin(ParentOrigin::CENTER);
189 mLayer.SetAnchorPoint(AnchorPoint::CENTER);
191 mLayer.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) );
194 mPopupBg = Actor::New();
195 mPopupBg.SetParentOrigin(ParentOrigin::CENTER);
196 mPopupBg.SetAnchorPoint(AnchorPoint::CENTER);
197 mPopupBg.ApplyConstraint( Constraint::New<Vector3>( Actor::SIZE, ParentSource( Actor::SIZE ), EqualToConstraint() ) );
198 mLayer.Add(mPopupBg);
200 // Any content after this point which is added to Self() will be reparented to
202 mAlterAddedChild = true;
204 // Add Backing (Dim effect)
207 // Add Dialog ( background image, title, content container, button container and tail )
211 ShowTail(ParentOrigin::BOTTOM_CENTER);
213 // Hide content by default.
214 SetState( Toolkit::Popup::POPUP_HIDE, 0.0f );
216 mPropertyTitle = self.RegisterProperty( PROPERTY_TITLE, "", Property::READ_WRITE );
217 mPropertyState = self.RegisterProperty( PROPERTY_STATE, "POPUP_HIDE", Property::READ_WRITE );
219 // Make self as keyboard focusable and focus group
220 self.SetKeyboardFocusable(true);
221 SetAsKeyboardFocusGroup(true);
224 void Popup::OnPropertySet( Property::Index index, Property::Value propertyValue )
226 if( index == mPropertyTitle )
228 SetTitle(propertyValue.Get<std::string>());
230 else if ( index == mPropertyState )
232 std::string value( propertyValue.Get<std::string>() );
233 if(value == "POPUP_SHOW")
235 SetState( Toolkit::Popup::POPUP_SHOW, 0.0f );
237 else if( value == "POPUP_HIDE")
239 SetState( Toolkit::Popup::POPUP_HIDE, 0.0f );
248 size_t Popup::GetButtonCount() const
250 return mButtons.size();
253 void Popup::SetBackgroundImage( Actor image )
255 // Removes any previous background.
256 if( mBackgroundImage && mPopupBg )
258 mPopupBg.Remove( mBackgroundImage );
261 // Adds new background to the dialog.
262 mBackgroundImage = image;
264 // OnDialogTouched only consume the event. It prevents the touch event to be caught by the backing.
265 mBackgroundImage.TouchedSignal().Connect( this, &Popup::OnDialogTouched );
267 mPopupBg.Add( mBackgroundImage );
270 void Popup::SetButtonAreaImage( Actor image )
272 // Removes any previous area image.
273 if( mButtonAreaImage && mPopupBg )
275 mPopupBg.Remove( mButtonAreaImage );
278 // Adds new area image to the dialog.
279 mButtonAreaImage = image;
281 // OnDialogTouched only consume the event. It prevents the touch event to be caught by the backing.
282 mButtonAreaImage.TouchedSignal().Connect( this, &Popup::OnDialogTouched );
284 mPopupBg.Add( mButtonAreaImage );
287 void Popup::SetTitle( const std::string& text )
289 Toolkit::TextView titleActor = Toolkit::TextView::New();
290 titleActor.SetText( text );
291 titleActor.SetColor( Color::BLACK );
292 titleActor.SetMultilinePolicy( Toolkit::TextView::SplitByWord );
293 titleActor.SetWidthExceedPolicy( Toolkit::TextView::Split );
294 titleActor.SetLineJustification( Toolkit::TextView::Center );
296 SetTitle( titleActor );
299 void Popup::SetTitle( Toolkit::TextView titleActor )
301 // Replaces the current title actor.
302 if( mTitle && mPopupBg )
304 mPopupBg.Remove( mTitle );
308 mPopupBg.Add( mTitle );
313 Toolkit::TextView Popup::GetTitle() const
318 void Popup::AddButton( Toolkit::Button button )
320 mButtons.push_back( button );
321 mBottomBg.Add( button );
326 void Popup::SetState( Toolkit::Popup::PopupState state )
328 SetState( state, POPUP_ANIMATION_DURATION );
331 void Popup::SetState( Toolkit::Popup::PopupState state, float duration )
333 // default animation behaviour.
334 HandleStateChange(state, duration);
337 Toolkit::Popup::PopupState Popup::GetState() const
342 void Popup::ShowTail(const Vector3& position)
344 // Replaces the tail actor.
345 if(mTailImage && mTailImage.GetParent())
347 mTailImage.GetParent().Remove( mTailImage );
351 std::string image = "";
353 // depending on position of tail around ParentOrigin, a different tail image is used...
354 if(position.y < Math::MACHINE_EPSILON_1)
356 image = mPopupStyle->tailUpImage;
358 else if(position.y > 1.0f - Math::MACHINE_EPSILON_1)
360 image = mPopupStyle->tailDownImage;
362 else if(position.x < Math::MACHINE_EPSILON_1)
364 image = mPopupStyle->tailLeftImage;
366 else if(position.x > 1.0f - Math::MACHINE_EPSILON_1)
368 image = mPopupStyle->tailRightImage;
373 Image tail = Image::New( image );
374 mTailImage = ImageActor::New(tail);
375 const Vector3 anchorPoint = AnchorPoint::FRONT_BOTTOM_RIGHT - position;
377 mTailImage.SetParentOrigin(position);
378 mTailImage.SetAnchorPoint(anchorPoint);
380 mBottomBg.Add(mTailImage);
384 void Popup::HideTail()
386 ShowTail(ParentOrigin::CENTER);
389 void Popup::SetStyle(PopupStyle& style)
391 mPopupStyle = PopupStylePtr(&style);
395 PopupStylePtr Popup::GetStyle() const
400 void Popup::SetDefaultBackgroundImage()
402 Image bg = Image::New( mPopupStyle->backgroundImage );
403 ImageActor bgImage = ImageActor::New( bg );
404 bgImage.SetStyle( ImageActor::STYLE_NINE_PATCH );
405 bgImage.SetNinePatchBorder( mPopupStyle->backgroundScale9Border );
407 Image buttonBg = Image::New( mPopupStyle->buttonAreaImage );
408 ImageActor buttonBgImage = ImageActor::New( buttonBg );
409 buttonBgImage.SetStyle( ImageActor::STYLE_NINE_PATCH );
410 buttonBgImage.SetNinePatchBorder( mPopupStyle->buttonArea9PatchBorder );
412 SetBackgroundImage( bgImage );
413 SetButtonAreaImage( buttonBgImage );
416 void Popup::CreateBacking()
418 mBacking = Dali::Toolkit::CreateSolidColorActor( mPopupStyle->backingColor );
420 mBacking.SetPositionInheritanceMode(DONT_INHERIT_POSITION);
421 mBacking.SetSensitive(true);
423 mLayer.Add(mBacking);
424 mBacking.SetOpacity(0.0f);
425 mBacking.SetPosition(0.0f, 0.0f, BACKING_DEPTH);
426 mBacking.TouchedSignal().Connect( this, &Popup::OnBackingTouched );
427 mBacking.MouseWheelEventSignal().Connect(this, &Popup::OnBackingMouseWheelEvent);
430 void Popup::CreateDialog()
432 // Adds default background image.
433 SetDefaultBackgroundImage();
435 // Adds bottom background
436 mBottomBg = Actor::New();
437 mPopupBg.Add( mBottomBg );
440 void Popup::HandleStateChange( Toolkit::Popup::PopupState state, float duration )
442 const Vector2& stageSize( Stage::GetCurrent().GetSize() );
445 float targetBackingAlpha;
446 Vector3 targetBackingSize;
455 case Toolkit::Popup::POPUP_HIDE:
457 targetSize = Vector3(0.0f, 0.0f, 1.0f);
458 targetBackingAlpha = 0.0f;
459 targetBackingSize = Vector3(0.0f, 0.0f, 1.0f);
461 ClearKeyInputFocus();
463 // Retore the keyboard focus when popup is hidden
464 if(mPreviousFocusedActor && mPreviousFocusedActor.IsKeyboardFocusable() )
466 Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
467 if( keyboardFocusManager )
469 keyboardFocusManager.SetCurrentFocusActor(mPreviousFocusedActor);
476 case Toolkit::Popup::POPUP_SHOW:
479 targetSize = Vector3(1.0f, 1.0f, 1.0f);
480 targetBackingAlpha = 1.0f;
481 float length = (stageSize.width > stageSize.height) ? stageSize.width : stageSize.height;
482 targetBackingSize = Vector3( length, length, 1.0f );
485 // Add contents to stage for showing.
486 if( !mLayer.GetParent() )
488 mAlterAddedChild = false;
490 mAlterAddedChild = true;
492 Self().SetSensitive(true);
495 // Handle the keyboard focus when popup is shown
496 Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get();
497 if( keyboardFocusManager )
499 mPreviousFocusedActor = keyboardFocusManager.GetCurrentFocusActor();
501 if( mContent && mContent.IsKeyboardFocusable() )
503 // If content is focusable, move the focus to content
504 keyboardFocusManager.SetCurrentFocusActor(mContent);
506 else if( !mButtons.empty() )
508 // Otherwise, movethe focus to the first button
509 keyboardFocusManager.SetCurrentFocusActor(mButtons[0]);
513 DALI_LOG_WARNING("There is no focusable in popup\n");
520 mBacking.SetSize( targetBackingSize );
522 if(duration > Math::MACHINE_EPSILON_1)
530 mAnimation = Animation::New(duration);
534 mAnimation.AnimateTo( Property(mBacking, Actor::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) );
535 mAnimation.AnimateTo( Property(mPopupBg, Actor::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(duration * 0.5f, duration * 0.5f) );
539 mAnimation.AnimateTo( Property(mBacking, Actor::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) );
540 mAnimation.AnimateTo( Property(mPopupBg, Actor::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) );
543 mAnimation.FinishedSignal().Connect(this, &Popup::OnStateAnimationFinished);
547 mBacking.SetOpacity( targetBackingAlpha );
548 mPopupBg.SetScale( targetSize );
550 HandleStateChangeComplete();
554 void Popup::HandleStateChangeComplete()
556 // Remove contents from stage if completely hidden.
557 if( (mState == Toolkit::Popup::POPUP_HIDE) && (mLayer.GetParent()) )
559 Self().Remove(mLayer);
560 Self().SetSensitive( false );
562 // Guard against destruction during signal emission
563 Toolkit::Popup handle( GetOwner() );
564 mHiddenSignalV2.Emit();
568 Toolkit::Popup::TouchedOutsideSignalV2& Popup::OutsideTouchedSignal()
570 return mTouchedOutsideSignalV2;
573 Toolkit::Popup::HiddenSignalV2& Popup::HiddenSignal()
575 return mHiddenSignalV2;
578 bool Popup::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
580 Dali::BaseHandle handle( object );
582 bool connected( true );
583 Toolkit::Popup popup = Toolkit::Popup::DownCast(handle);
585 if( Dali::Toolkit::Popup::SIGNAL_TOUCHED_OUTSIDE == signalName )
587 popup.OutsideTouchedSignal().Connect( tracker, functor );
589 else if( Dali::Toolkit::Popup::SIGNAL_HIDDEN == signalName )
591 popup.HiddenSignal().Connect( tracker, functor );
595 // signalName does not match any signal
602 void Popup::OnStateAnimationFinished( Animation& source )
604 HandleStateChangeComplete();
607 bool Popup::OnBackingTouched(Actor actor, const TouchEvent& event)
609 if(event.GetPointCount()>0)
611 const TouchPoint& point = event.GetPoint(0);
613 if(point.state == TouchPoint::Down)
615 // Guard against destruction during signal emission
616 Toolkit::Popup handle( GetOwner() );
618 mTouchedOutsideSignalV2.Emit();
625 bool Popup::OnBackingMouseWheelEvent(Actor actor, const MouseWheelEvent& event)
627 // consume mouse wheel event in dimmed backing actor
631 bool Popup::OnDialogTouched(Actor actor, const TouchEvent& event)
633 // consume event (stops backing actor receiving touch events)
637 void Popup::OnControlChildAdd( Actor& child )
639 // reparent any children added by user to the body layer.
640 if( mAlterAddedChild )
642 // Removes previously added content.
645 mPopupBg.Remove( mContent );
648 // Reparent new content.
649 Self().Remove( child );
651 // keep a handle to the new content.
654 mPopupBg.Add( mContent );
658 void Popup::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
660 // Set the popup size
662 popupSize.width = size.width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin );
663 popupSize.height = size.height - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin );
665 // Update sizes of all popup's components.
667 // Relayout background image.
668 // Adjust background position and size relative to parent to cater to outer Border.
669 // Some backgrounds are intended to over-spill. That is some content
670 // should appear outside the Dialog on all sides i.e. Shadows, glow effects.
671 const Vector4 outerBorder = mPopupStyle->backgroundOuterBorder;
673 if( mBackgroundImage )
675 Constraint constraint = Constraint::New<Vector3>( Actor::SIZE,
676 ParentSource( Actor::SIZE ),
677 BackgroundSizeConstraint(outerBorder) );
679 mBackgroundImage.RemoveConstraints();
680 mBackgroundImage.ApplyConstraint( constraint );
682 mBackgroundImage.SetAnchorPoint( AnchorPoint::TOP_LEFT );
683 mBackgroundImage.SetParentOrigin( ParentOrigin::TOP_LEFT );
684 mBackgroundImage.SetPosition( -outerBorder.x, -outerBorder.y, 0.0f );
687 if( mPopupBg && mButtonAreaImage )
689 // If there are no buttons, button background is also removed.
690 if ( mButtons.size() == 0 )
692 mPopupBg.Remove( mButtonAreaImage );
696 Constraint constraint = Constraint::New<Vector3>( Actor::SIZE,
697 ParentSource( Actor::SIZE ),
698 ButtonAreaSizeConstraint(outerBorder) );
700 mButtonAreaImage.RemoveConstraints();
701 mButtonAreaImage.ApplyConstraint( constraint );
703 mButtonAreaImage.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
704 mButtonAreaImage.SetParentOrigin( ParentOrigin::BOTTOM_CENTER );
705 mButtonAreaImage.SetY( -outerBorder.z - POPUP_OUT_MARGIN_HEIGHT );
707 mPopupBg.Add( mButtonAreaImage );
712 Vector3 positionOffset( 0.0f, mPopupStyle->margin + POPUP_OUT_MARGIN_WIDTH, CONTENT_DEPTH );
716 titleSize.width = popupSize.width;
717 titleSize.height = mTitle.GetHeightForWidth( titleSize.width );
719 // As the default size policy for text-view is Fixed & Fixed, a size needs to be set.
720 // Otherwise size-negotiation algorithm uses the GetNaturalSize() with doesn't take
721 // into account the multiline and exceed policies, giving as result a wrong size.
722 mTitle.SetSize( titleSize );
723 Relayout( mTitle, titleSize, container );
725 mTitle.SetAnchorPoint( AnchorPoint::TOP_CENTER );
726 mTitle.SetParentOrigin( ParentOrigin::TOP_CENTER );
727 mTitle.SetPosition( positionOffset );
729 positionOffset.y += titleSize.height + mPopupStyle->margin;
735 // If the content width is greater than popup width then scale it down/wrap text as needed
736 Vector2 contentSize( RelayoutHelper::GetNaturalSize( mContent ) );
737 if( contentSize.width > popupSize.width )
739 contentSize.width = popupSize.width;
740 contentSize.height = RelayoutHelper::GetHeightForWidth( mContent, contentSize.width );
743 mContent.SetSize( contentSize );
744 Relayout( mContent, contentSize, container );
746 mContent.SetParentOrigin(ParentOrigin::TOP_CENTER);
747 mContent.SetAnchorPoint(AnchorPoint::TOP_CENTER);
749 mContent.SetPosition( positionOffset );
751 positionOffset.y += contentSize.height + mPopupStyle->margin;
754 // Relayout Button Area
757 mBottomBg.SetSize( popupSize.width, mPopupStyle->bottomSize.height );
759 mBottomBg.SetParentOrigin(ParentOrigin::TOP_CENTER);
760 mBottomBg.SetAnchorPoint(AnchorPoint::TOP_CENTER);
762 mBottomBg.SetPosition( positionOffset );
765 // Relayout All buttons
766 if ( !mButtons.empty() )
768 // All buttons should be the same size and fill the button area. The button spacing needs to be accounted for as well.
769 Vector2 buttonSize( ( ( popupSize.width - mPopupStyle->buttonSpacing * ( mButtons.size() - 1 ) ) / mButtons.size() ),
770 mPopupStyle->bottomSize.height - mPopupStyle->margin );
772 Vector3 buttonPosition;
774 for ( ActorIter iter = mButtons.begin(), endIter = mButtons.end();
776 ++iter, buttonPosition.x += mPopupStyle->buttonSpacing + buttonSize.width )
778 iter->SetPosition( buttonPosition );
780 // If there is only one button, it needs to be laid out on center.
781 if ( mButtons.size() == 1 )
783 iter->SetAnchorPoint( AnchorPoint::CENTER );
784 iter->SetParentOrigin( ParentOrigin::CENTER );
788 iter->SetAnchorPoint( AnchorPoint::CENTER_LEFT );
789 iter->SetParentOrigin( ParentOrigin::CENTER_LEFT );
792 Relayout( *iter, buttonSize, container );
796 if( mShowing && mBacking )
798 Vector2 stageSize = Stage::GetCurrent().GetSize();
799 float length = (stageSize.width > stageSize.height) ? stageSize.width : stageSize.height;
800 Vector3 targetBackingSize = Vector3( length, length, 1.0f );
802 mBacking.SetSize( targetBackingSize );
806 bool Popup::OnKeyEvent(const KeyEvent& event)
808 bool consumed = false;
810 if(event.state == KeyEvent::Down)
812 if (event.keyCode == Dali::DALI_KEY_ESCAPE || event.keyCode == Dali::DALI_KEY_BACK)
814 SetState(Toolkit::Popup::POPUP_HIDE);
822 Vector3 Popup::GetNaturalSize()
824 float margin = 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin );
825 const float maxWidth = Stage::GetCurrent().GetSize().width - margin;
827 Vector3 naturalSize( 0.0f, 0.0f, 0.0f );
831 Vector3 titleNaturalSize = mTitle.GetImplementation().GetNaturalSize();
832 // Buffer to avoid errors. The width of the popup could potentially be the width of the title text.
833 // It was observed in this case that text wrapping was then inconsistent when seen on device
834 const float titleBuffer = 0.5f;
835 titleNaturalSize.width += titleBuffer;
837 // As TextView GetNaturalSize does not take wrapping into account, limit the width
838 // to that of the stage
839 if( titleNaturalSize.width >= maxWidth)
841 naturalSize.width = maxWidth;
842 naturalSize.height = mTitle.GetImplementation().GetHeightForWidth( naturalSize.width );
846 naturalSize += titleNaturalSize;
849 naturalSize.height += mPopupStyle->margin;
854 Vector3 contentSize = RelayoutHelper::GetNaturalSize( mContent );
855 // Choose the biggest width
856 naturalSize.width = std::max( naturalSize.width, contentSize.width );
857 naturalSize.height += contentSize.height + mPopupStyle->margin;
860 if( !mButtons.empty() )
862 naturalSize.height += mPopupStyle->bottomSize.height;
866 naturalSize.width += margin;
867 naturalSize.height += margin;
872 float Popup::GetHeightForWidth( float width )
874 float height( 0.0f );
875 float popupWidth( width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) );
879 height += mTitle.GetImplementation().GetHeightForWidth( popupWidth );
880 height += mPopupStyle->margin;
885 height += RelayoutHelper::GetHeightForWidth( mContent, popupWidth ) + mPopupStyle->margin;
888 if( !mButtons.empty() )
890 height += mPopupStyle->bottomSize.height;
894 float margin( 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) );
900 float Popup::GetWidthForHeight( float height )
902 return GetNaturalSize().width;
905 Actor Popup::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled)
907 Actor nextFocusableActor( currentFocusedActor );
909 // TODO: Needs to be optimised
911 if ( !currentFocusedActor || ( currentFocusedActor && KeyboardFocusManager::Get().GetFocusGroup(currentFocusedActor) != Self() ) )
913 // The current focused actor is not within popup
914 if( mContent && mContent.IsKeyboardFocusable() )
916 // If content is focusable, move the focus to content
917 nextFocusableActor = mContent;
919 else if( !mButtons.empty() )
921 // Otherwise, movethe focus to the first button
922 nextFocusableActor = mButtons[0];
927 // Rebuild the focus chain because button or content can be added or removed dynamically
928 ActorContainer focusableActors;
929 if( mContent && mContent.IsKeyboardFocusable() )
931 focusableActors.push_back(mContent);
934 for(unsigned int i = 0; i < mButtons.size(); i++)
936 if( mButtons[i] && mButtons[i].IsKeyboardFocusable() )
938 focusableActors.push_back(mButtons[i]);
942 for ( ActorContainer::iterator iter = focusableActors.begin(), end = focusableActors.end(); iter != end; ++iter )
944 if ( currentFocusedActor == *iter )
948 case Toolkit::Control::Left:
950 if ( iter == focusableActors.begin() )
952 nextFocusableActor = *( focusableActors.end() - 1 );
956 nextFocusableActor = *( iter - 1 );
960 case Toolkit::Control::Right:
962 if ( iter == focusableActors.end() - 1 )
964 nextFocusableActor = *( focusableActors.begin() );
968 nextFocusableActor = *( iter + 1 );
973 case Toolkit::Control::Up:
975 if ( *iter == mContent )
977 nextFocusableActor = *( focusableActors.end() - 1 );
981 if ( mContent && mContent.IsKeyboardFocusable() )
983 nextFocusableActor = mContent;
987 if ( iter == focusableActors.begin() )
989 nextFocusableActor = *( focusableActors.end() - 1 );
993 nextFocusableActor = *( iter - 1 );
1000 case Toolkit::Control::Down:
1002 if ( mContent && mContent.IsKeyboardFocusable() )
1004 nextFocusableActor = mContent;
1008 if ( iter == focusableActors.end() - 1 )
1010 nextFocusableActor = *( focusableActors.begin() );
1014 nextFocusableActor = *( iter + 1 );
1018 if ( *iter == mContent && !mButtons.empty() )
1020 nextFocusableActor = mButtons[0];
1026 if(!nextFocusableActor)
1028 DALI_LOG_WARNING("Can not decide next focusable actor\n");
1036 return nextFocusableActor;
1039 } // namespace Internal
1041 } // namespace Toolkit