X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Fcontrols%2Fpopup%2Fpopup-impl.cpp;h=de3528933a1f6a2018a73fe30a6f97983e5469a2;hp=441be0422d409c1d22597e36d56166ccfa99bcb7;hb=e9d852fcdacc5788785bfe0b617bd757794e8208;hpb=034a4b7aca01b9ba88a488d2aa5971367368865f diff --git a/dali-toolkit/internal/controls/popup/popup-impl.cpp b/dali-toolkit/internal/controls/popup/popup-impl.cpp index 441be04..de35289 100755 --- a/dali-toolkit/internal/controls/popup/popup-impl.cpp +++ b/dali-toolkit/internal/controls/popup/popup-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Copyright (c) 2015 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,86 +19,29 @@ #include // EXTERNAL INCLUDES +#include // for strcmp +#include +#include +#include #include -#include #include #include #include #include -#include -#include #include +#include +#include +#include // INTERNAL INCLUDES -#include -#include +#include #include -#include +#include +#include #include -#include using namespace Dali; -namespace -{ -const float CONTENT_DEPTH = 1.0f; ///< 3D Effect of buttons/title etc. appearing off the popup. -const float POPUP_ANIMATION_DURATION = 0.5f; ///< Duration of hide/show animations -const float BACKING_DEPTH = -1.0f; ///< Depth of backing (positioned just behind dialog, so dialog catches hit events first) - -const float POPUP_WIDTH = 720.0f; ///< Width of Popup -const float POPUP_OUT_MARGIN_WIDTH = 16.f; ///< Space between the screen edge and the popup edge in the horizontal dimension. -const float POPUP_OUT_MARGIN_HEIGHT = 36.f; ///< Space between the screen edge and the popup edge in the vertical dimension. -const float POPUP_TITLE_WIDTH = 648.0f; /// DEFAULT_TITLE_PADDING( 20.0f, 20.0f, 20.0f, 20.0f ); ///< Title padding used on popups with content and/or controls (from Tizen GUI UX). +const Rect DEFAULT_TITLE_ONLY_PADDING( 8.0f, 8.0f, 8.0f, 8.0f ); ///< Title padding used on popups with a title only (like toast popups). +const Vector3 FOOTER_SIZE( 620.0f, 96.0f,0.0f ); ///< Default size of the bottom control area. +const float DEFAULT_RELATIVE_PARENT_WIDTH = 0.75f; ///< If width is not fixed, relative size to parent is used by default. + +} // Unnamed namespace -/////////////////////////////////////////////////////////////////////////////////////////////////// -// Popup -/////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * Implementation. + */ Dali::Toolkit::Popup Popup::New() { - PopupStylePtr style = PopupStyleDefault::New(); - // Create the implementation - PopupPtr popup(new Popup(*style)); + PopupPtr popup( new Popup() ); - // Pass ownership to CustomActor via derived handle - Dali::Toolkit::Popup handle(*popup); + // Pass ownership to CustomActor via derived handle. + Dali::Toolkit::Popup handle( *popup ); - // Second-phase init of the implementation - // This can only be done after the CustomActor connection has been made... + // Second-phase initialisation of the implementation. + // This can only be done after the CustomActor connection has been made. popup->Initialize(); return handle; } -Popup::Popup(PopupStyle& style) +Popup::Popup() : Control( ControlBehaviour( REQUIRES_TOUCH_EVENTS | REQUIRES_STYLE_CHANGE_SIGNALS ) ), - mShowing(false), - mState(Toolkit::Popup::POPUP_NONE), // Initially, the popup state should not be set, it's set in OnInitialize - mAlterAddedChild(false), - mPopupStyle(PopupStylePtr(&style)), - mPropertyTitle(Property::INVALID_INDEX), - mPropertyState(Property::INVALID_INDEX) + mTouchedOutsideSignal(), + mShowingSignal(), + mShownSignal(), + mHidingSignal(), + mHiddenSignal(), + mLayer(), + mPopupLayout(), + mBacking(), + mPreviousFocusedActor(), + mTailImage(), + mPopupContainer(), + mAnimation(), + mAlterAddedChild( false ), + mLayoutDirty( true ), + mAutoHideTimer(), + mTouchTransparent( false ), + mTitle(), + mContent(), + mFooter(), + mDisplayState( Toolkit::Popup::HIDDEN ), // Hidden until shown with SetDisplayState() + mTailVisible( false ), + mTailPosition( DEFAULT_TAIL_POSITION ), + mContextualMode( Toolkit::Popup::NON_CONTEXTUAL ), + mAnimationDuration( DEFAULT_POPUP_ANIMATION_DURATION ), + mAnimationMode( Toolkit::Popup::FADE ), + mEntryAnimationData(), + mExitAnimationData(), + mAutoHideDelay( 0 ), + mBackingEnabled( true ), + mBackingColor( DEFAULT_BACKING_COLOR ), + mPopupBackgroundImage(), + mBackgroundOuterBorder(), + mMargin(), + mTailUpImage( DEFAULT_TAIL_UP_IMAGE_PATH ), + mTailDownImage( DEFAULT_TAIL_DOWN_IMAGE_PATH ), + mTailLeftImage( DEFAULT_TAIL_LEFT_IMAGE_PATH ), + mTailRightImage( DEFAULT_TAIL_RIGHT_IMAGE_PATH ) { SetKeyboardNavigationSupport( true ); } @@ -161,396 +265,1157 @@ Popup::Popup(PopupStyle& style) void Popup::OnInitialize() { Actor self = Self(); - self.SetSensitive(false); + self.SetName( "popup" ); + + // Apply some default resizing rules. + self.SetParentOrigin( ParentOrigin::CENTER ); + self.SetAnchorPoint( AnchorPoint::CENTER ); + + self.SetSizeModeFactor( DEFAULT_POPUP_PARENT_RELATIVE_SIZE ); + self.SetResizePolicy( ResizePolicy::SIZE_RELATIVE_TO_PARENT, Dimension::WIDTH ); + self.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT ); - // Create Layer + // Create a new layer so all Popup components can appear above all other actors. mLayer = Layer::New(); - mLayer.SetParentOrigin(ParentOrigin::CENTER); - mLayer.SetAnchorPoint(AnchorPoint::CENTER); - mLayer.RaiseToTop(); - self.Add(mLayer); - - mPopupBg = Actor::New(); - mPopupBg.SetParentOrigin(ParentOrigin::CENTER); - mPopupBg.SetAnchorPoint(AnchorPoint::CENTER); - mLayer.Add(mPopupBg); - - // Any content after this point which is added to Self() will be reparented to - // mContent. + mLayer.SetName( "popupLayer" ); + + mLayer.SetParentOrigin( ParentOrigin::CENTER ); + mLayer.SetAnchorPoint( AnchorPoint::CENTER ); + mLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS ); + + // Important to set as invisible as otherwise, if the popup is parented, + // but not shown yet it will appear statically on the screen. + mLayer.SetVisible( false ); + + // Add the layer to the hierarchy. + self.Add( mLayer ); + + // Add Backing (Dimmed effect). + mBacking = CreateBacking(); + mLayer.Add( mBacking ); + + mPopupContainer = Actor::New(); + mPopupContainer.SetName( "popupContainer" ); + mPopupContainer.SetParentOrigin( ParentOrigin::CENTER ); + mPopupContainer.SetAnchorPoint( AnchorPoint::CENTER ); + mPopupContainer.SetResizePolicy( ResizePolicy::FIT_TO_CHILDREN, Dimension::ALL_DIMENSIONS ); + mLayer.Add( mPopupContainer ); + + // Create the Popup layout to contain all main content. + mPopupLayout = Toolkit::TableView::New( 3, 1 ); + + // Adds the default background image. + SetPopupBackgroundImage( Toolkit::ImageView::New( ResourceImage::New( DEFAULT_BACKGROUND_IMAGE_PATH ) ) ); + + mPopupLayout.SetName( "popupLayoutTable" ); + mPopupLayout.SetParentOrigin( ParentOrigin::CENTER ); + mPopupLayout.SetAnchorPoint( AnchorPoint::CENTER ); + + mPopupLayout.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::WIDTH ); + mPopupLayout.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT ); + mPopupLayout.SetSize( Stage::GetCurrent().GetSize().x * DEFAULT_RELATIVE_PARENT_WIDTH, 0.0f ); + + mPopupLayout.SetFitHeight( 0 ); // Set row to fit. + mPopupLayout.SetFitHeight( 1 ); // Set row to fit. + + mPopupLayout.TouchedSignal().Connect( this, &Popup::OnDialogTouched ); + + mPopupContainer.Add( mPopupLayout ); + + // Any content after this point which is added to Self() will be re-parented to mContent. mAlterAddedChild = true; - // Add Backing (Dim effect) - CreateBacking(); + SetAsKeyboardFocusGroup( true ); +} - // Add Dialog ( background image, title, content container, button container and tail ) - CreateDialog(); +Popup::~Popup() +{ + mEntryAnimationData.Clear(); + mExitAnimationData.Clear(); +} - // Default content. - ShowTail(ParentOrigin::BOTTOM_CENTER); +void Popup::LayoutAnimation() +{ + // Perform setup based on the currently selected animation. + switch( mAnimationMode ) + { + case Toolkit::Popup::ZOOM: + { + // Zoom animations start fully zoomed out. + mPopupContainer.SetScale( Vector3::ZERO ); + break; + } - // Hide content by default. - SetState( Toolkit::Popup::POPUP_HIDE, 0.0f ); + case Toolkit::Popup::FADE: + { + // Fade animations start transparent. + mPopupContainer.SetOpacity( 0.0f ); + break; + } - mPropertyTitle = self.RegisterProperty( PROPERTY_TITLE, "", Property::READ_WRITE ); - mPropertyState = self.RegisterProperty( PROPERTY_STATE, "POPUP_HIDE", Property::READ_WRITE ); + case Toolkit::Popup::CUSTOM: + { + // Initialise the custom animation by playing to the end of it's exit animation instantly. + // EG. If it was zooming in, then we zoom out fully instantly so the zoom in works. + StartTransitionAnimation( false, true ); + break; + } - // Make self as keyboard focusable and focus group - self.SetKeyboardFocusable(true); - SetAsKeyboardFocusGroup(true); + case Toolkit::Popup::NONE: + { + break; + } + } } -void Popup::OnPropertySet( Property::Index index, Property::Value propertyValue ) +void Popup::StartTransitionAnimation( bool transitionIn, bool instantaneous /* false */ ) { - if( index == mPropertyTitle ) + // Stop and recreate animation. + if ( mAnimation ) + { + mAnimation.Stop(); + mAnimation.Clear(); + mAnimation.Reset(); + } + float duration = GetAnimationDuration(); + + // Setup variables ready to start the animations. + // If we are performing the animation instantaneously, we do not want to emit a signal. + if( !instantaneous ) { - SetTitle(propertyValue.Get()); + if( transitionIn ) + { + // Setup variables and signal that we are starting the transition. + // Note: We signal even if the transition is instant so signal order is consistent. + mShowingSignal.Emit(); + } + else + { + mHidingSignal.Emit(); + } } - else if ( index == mPropertyState ) + + // Perform chosen animation for the Popup. + switch( mAnimationMode ) { - std::string value( propertyValue.Get() ); - if(value == "POPUP_SHOW") + case Toolkit::Popup::NONE: + { + mAnimation = Animation::New( 0.0f ); + break; + } + + case Toolkit::Popup::ZOOM: + { + mAnimation = Animation::New( duration ); + if( duration > Math::MACHINE_EPSILON_0 ) + { + if( transitionIn ) + { + mAnimation.AnimateTo( Property( mPopupContainer, Actor::Property::SCALE ), Vector3::ONE, AlphaFunction::EASE_IN_OUT, TimePeriod( duration * 0.25f, duration * 0.75f ) ); + } + else + { + // Zoom out animation is twice the speed. Modify the duration variable so the backing animation speed is modified also. + duration /= 2.0f; + mAnimation.SetDuration( duration ); + mAnimation.AnimateTo( Property( mPopupContainer, Actor::Property::SCALE ), Vector3::ZERO, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.0f, duration ) ); + } + } + else + { + mPopupContainer.SetScale( transitionIn ? Vector3::ONE : Vector3::ZERO ); + } + break; + } + + case Toolkit::Popup::FADE: { - SetState( Toolkit::Popup::POPUP_SHOW, 0.0f ); + mAnimation = Animation::New( duration ); + if( duration > Math::MACHINE_EPSILON_0 ) + { + if( transitionIn ) + { + mAnimation.AnimateTo( Property( mPopupContainer, Actor::Property::COLOR_ALPHA ), 1.0f, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.30f, duration * 0.70f ) ); + } + else + { + mAnimation.AnimateTo( Property( mPopupContainer, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.0f, duration * 0.70f ) ); + } + } + else + { + mPopupContainer.SetOpacity( transitionIn ? 1.0f : 0.0f ); + } + break; } - else if( value == "POPUP_HIDE") + + case Toolkit::Popup::CUSTOM: { - SetState( Toolkit::Popup::POPUP_HIDE, 0.0f ); + // Use a user specified animation for in and out. + // Read the correct animation depending on entry or exit. + // Attempt to use animation data defined from script data. + Dali::AnimationData* animationData = transitionIn ? &mEntryAnimationData : &mExitAnimationData; + + // Create a new animation from the pre-defined data in the AnimationData class. + // If there is no data, mAnimation is invalidated. + mAnimation = animationData->CreateAnimation( mPopupContainer, duration ); + + // If we don't have a valid animation, provide a blank one so play() can still function generically. + if( !mAnimation ) + { + // No animation was configured (even though custom mode was specified). Create a dummy animation to avoid an exception. + mAnimation = Animation::New( 0.0f ); + } + + break; } } + + // Animate the backing, if enabled. + // This is set up last so that different animation modes can have an effect on the backing animation speed. + if( mBackingEnabled ) + { + // Use the alpha from the user-specified color. + float targetAlpha = mBackingColor.a; + if( duration > Math::MACHINE_EPSILON_0 ) + { + if( transitionIn ) + { + mAnimation.AnimateTo( Property( mBacking, Actor::Property::COLOR_ALPHA ), targetAlpha, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.0f, duration * 0.70f ) ); + } + else + { + mAnimation.AnimateTo( Property( mBacking, Actor::Property::COLOR_ALPHA ), 0.0f, AlphaFunction::EASE_IN_OUT, TimePeriod( 0.30f, duration * 0.70f ) ); + } + } + else + { + mBacking.SetProperty( Actor::Property::COLOR_ALPHA, transitionIn ? targetAlpha : 0.0f ); + } + } + + // If we are performing the animation instantaneously, jump to the position directly and do not signal. + if( instantaneous ) + { + mAnimation.SetCurrentProgress( 1.0f ); + mAnimation.Play(); + } + else if( duration > Math::MACHINE_EPSILON_0 ) + { + // Run the animation. + mAnimation.FinishedSignal().Connect( this, &Popup::OnDisplayChangeAnimationFinished ); + mAnimation.Play(); + } + else + { + // We did not use an animation to achive the transition. + // Trigger the state change directly. + DisplayStateChangeComplete(); + } } -Popup::~Popup() +void Popup::OnDisplayChangeAnimationFinished( Animation& source ) { + DisplayStateChangeComplete(); +} + +void Popup::DisplayStateChangeComplete() +{ + // Remove contents from stage if completely hidden. + if( mDisplayState == Toolkit::Popup::HIDING ) + { + mDisplayState = Toolkit::Popup::HIDDEN; + + mLayer.SetVisible( false ); + mPopupLayout.SetSensitive( false ); + + // Guard against destruction during signal emission. + Toolkit::Popup handle( GetOwner() ); + mHiddenSignal.Emit(); + } + else if( mDisplayState == Toolkit::Popup::SHOWING ) + { + mDisplayState = Toolkit::Popup::SHOWN; + Toolkit::Popup handle( GetOwner() ); + mShownSignal.Emit(); + + // Start a timer to auto-hide if enabled. + if( mAutoHideDelay > 0u ) + { + mAutoHideTimer = Timer::New( mAutoHideDelay ); + mAutoHideTimer.TickSignal().Connect( this, &Popup::OnAutoHideTimeReached ); + mAutoHideTimer.Start(); + } + } } -size_t Popup::GetButtonCount() const +bool Popup::OnAutoHideTimeReached() { - return mButtons.size(); + // Display timer has expired, auto hide the popup exactly as if the user had clicked outside. + SetDisplayState( Toolkit::Popup::HIDDEN ); + + if( mAutoHideTimer ) + { + mAutoHideTimer.Stop(); + mAutoHideTimer.TickSignal().Disconnect( this, &Popup::OnAutoHideTimeReached ); + mAutoHideTimer.Reset(); + } + return true; } -void Popup::SetBackgroundImage( Actor image ) +void Popup::SetPopupBackgroundImage( Actor image ) { // Removes any previous background. - if( mBackgroundImage && mPopupBg ) + if( mPopupBackgroundImage ) { - mPopupBg.Remove( mBackgroundImage ); + mPopupContainer.Remove( mPopupBackgroundImage ); } // Adds new background to the dialog. - mBackgroundImage = image; + mPopupBackgroundImage = image; + mPopupBackgroundImage.SetName( "popupBackgroundImage" ); + mPopupBackgroundImage.SetAnchorPoint( AnchorPoint::CENTER ); + mPopupBackgroundImage.SetParentOrigin( ParentOrigin::CENTER ); + + // OnDialogTouched only consumes the event. It prevents the touch event to be caught by the backing. + mPopupBackgroundImage.TouchedSignal().Connect( this, &Popup::OnDialogTouched ); + + // Set the popup border to be slightly larger than the layout contents. + mPopupBackgroundImage.SetResizePolicy( ResizePolicy::SIZE_FIXED_OFFSET_FROM_PARENT, Dimension::ALL_DIMENSIONS ); + mPopupBackgroundImage.SetSizeModeFactor( BACKGROUND_OUTER_BORDER ); - // OnDialogTouched only consume the event. It prevents the touch event to be caught by the backing. - mBackgroundImage.TouchedSignal().Connect( this, &Popup::OnDialogTouched ); + const bool prevAlter = mAlterAddedChild; + mAlterAddedChild = false; + mPopupContainer.Add( mPopupBackgroundImage ); + mAlterAddedChild = prevAlter; - mPopupBg.Add( mBackgroundImage ); + mLayoutDirty = true; } -void Popup::SetButtonAreaImage( Actor image ) +Actor Popup::GetPopupBackgroundImage() const { - // Removes any previous area image. - if( mButtonAreaImage && mPopupBg ) + return mPopupBackgroundImage; +} + +void Popup::SetTitle( Actor titleActor ) +{ + // Replaces the current title actor. + if( !mPopupLayout ) { - mPopupBg.Remove( mButtonAreaImage ); + return; } - // Adds new area image to the dialog. - mButtonAreaImage = image; + if( mTitle ) + { + mPopupLayout.RemoveChildAt( Toolkit::TableView::CellPosition( 0, 0) ); + } + mTitle = titleActor; - // OnDialogTouched only consume the event. It prevents the touch event to be caught by the backing. - mButtonAreaImage.TouchedSignal().Connect( this, &Popup::OnDialogTouched ); + if( mTitle ) + { + // Set up padding to give sensible default behaviour + // (an application developer can later override this if they wish). + mTitle.SetPadding( DEFAULT_TITLE_PADDING ); - mPopupBg.Add( mButtonAreaImage ); + mPopupLayout.AddChild( mTitle, Toolkit::TableView::CellPosition( 0, 0 ) ); + } + + mLayoutDirty = true; + RelayoutRequest(); } -void Popup::SetTitle( const std::string& text ) +Actor Popup::GetTitle() const { - Toolkit::TextView titleActor = Toolkit::TextView::New(); - titleActor.SetText( text ); - titleActor.SetColor( Color::BLACK ); - titleActor.SetMultilinePolicy( Toolkit::TextView::SplitByWord ); - titleActor.SetWidthExceedPolicy( Toolkit::TextView::Split ); - titleActor.SetLineJustification( Toolkit::TextView::Center ); - - SetTitle( titleActor ); + return mTitle; } -void Popup::SetTitle( Toolkit::TextView titleActor ) +void Popup::SetContent( Actor content ) { - // Replaces the current title actor. - if( mTitle && mPopupBg ) + // Remove previous content actor. + if( mPopupLayout ) { - mPopupBg.Remove( mTitle ); + mPopupLayout.RemoveChildAt( Toolkit::TableView::CellPosition( 1, 0 ) ); } - mTitle = titleActor; + // Keep a handle to the new content. + mContent = content; + + if( mContent ) + { + mContent.SetName( "popupContent" ); - mPopupBg.Add( mTitle ); + mPopupLayout.AddChild( mContent, Toolkit::TableView::CellPosition( 1, 0 ) ); + } + mLayoutDirty = true; RelayoutRequest(); } -Toolkit::TextView Popup::GetTitle() const +Actor Popup::GetContent() const { - return mTitle; + return mContent; } -void Popup::AddButton( Toolkit::Button button ) +void Popup::SetFooter( Actor footer ) { - mButtons.push_back( button ); - mBottomBg.Add( button ); + // Remove previous content actor. + if( mPopupLayout ) + { + mPopupLayout.RemoveChildAt( Toolkit::TableView::CellPosition( 2, 0 ) ); + } + + // Keep a handle to the new content. + mFooter = footer; + + if( mFooter ) + { + mFooter.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH ); + + // The control container has a fixed height. + mPopupLayout.SetFitHeight( 2u ); + mPopupLayout.AddChild( footer, Toolkit::TableView::CellPosition( 2, 0 ) ); + } + mLayoutDirty = true; RelayoutRequest(); } -void Popup::SetState( Toolkit::Popup::PopupState state ) +Actor Popup::GetFooter() const { - SetState( state, POPUP_ANIMATION_DURATION ); + return mFooter; } -void Popup::SetState( Toolkit::Popup::PopupState state, float duration ) +void Popup::SetDisplayState( Toolkit::Popup::DisplayState displayState ) { - // default animation behaviour. - HandleStateChange(state, duration); + // Convert the 4-way state to a bool, true for show, false for hide. + bool display = ( displayState == Toolkit::Popup::SHOWING ) || ( displayState == Toolkit::Popup::SHOWN ); + + // Ignore if we are already at the target display state. + if( display == ( ( mDisplayState == Toolkit::Popup::SHOWING ) || ( mDisplayState == Toolkit::Popup::SHOWN ) ) ) + { + return; + } + + // Convert the bool state to the actual display state to use. + mDisplayState = display ? Toolkit::Popup::SHOWING : Toolkit::Popup::HIDING; + + if ( display ) + { + // Update the state to indicate the current intent. + mDisplayState = Toolkit::Popup::SHOWING; + + // We are displaying so bring the popup layer to the front, and set it visible so it is rendered. + mLayer.RaiseToTop(); + mLayer.SetVisible( true ); + + // Set up the layout if this is the first display or the layout has become dirty. + if( mLayoutDirty ) + { + // Bake-in any style and layout options to create the Popup layout. + LayoutPopup(); + } + + // Allow the popup to catch events. + mPopupLayout.SetSensitive( true ); + + // Handle the keyboard focus when popup is shown. + Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get(); + if( keyboardFocusManager ) + { + mPreviousFocusedActor = keyboardFocusManager.GetCurrentFocusActor(); + + if( Self().IsKeyboardFocusable() ) + { + // Setup the actgor to start focus from. + Actor focusActor; + if( mContent && mContent.IsKeyboardFocusable() ) + { + // If the content is focusable, move the focus to the content. + focusActor = mContent; + } + else if( mFooter && mFooter.IsKeyboardFocusable() ) + { + // If the footer is focusable, move the focus to the footer. + focusActor = mFooter; + } + else + { + DALI_LOG_WARNING( "There is no focusable in popup\n" ); + } + + if( focusActor ) + { + SetKeyInputFocus(); + keyboardFocusManager.SetCurrentFocusActor( focusActor ); + } + } + } + } + else // Not visible. + { + mDisplayState = Toolkit::Popup::HIDING; + ClearKeyInputFocus(); + + // Restore the keyboard focus when popup is hidden. + if( mPreviousFocusedActor && mPreviousFocusedActor.IsKeyboardFocusable() ) + { + Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get(); + if( keyboardFocusManager ) + { + keyboardFocusManager.SetCurrentFocusActor( mPreviousFocusedActor ); + } + } + } + + // Perform animation. + StartTransitionAnimation( display ); } -Toolkit::Popup::PopupState Popup::GetState() const +Toolkit::Popup::DisplayState Popup::GetDisplayState() const { - return mState; + return mDisplayState; } -void Popup::ShowTail(const Vector3& position) +void Popup::LayoutPopup() { - // Replaces the tail actor. - if(mTailImage && mTailImage.GetParent()) + mLayoutDirty = false; + + /* When animating in, we want to respect the origin applied to Self(). + * For example, if zooming, not only will the final result be anchored to the + * selected point, but the zoom will originate from this point also. + * + * EG: ParentOrigin::TOP_LEFT, AnchorPoint::TOP_LEFT : + * + * -------- -------- + * |X| |XXX| + * |`` Animates |XXX| + * | to: |XXX| + * | |```` + * | | + */ + mPopupContainer.SetParentOrigin( Self().GetCurrentParentOrigin() ); + mPopupContainer.SetAnchorPoint( Self().GetCurrentAnchorPoint() ); + + // If there is only a title, use less padding. + if( mTitle ) + { + if( !mContent && !mFooter ) + { + mTitle.SetPadding( DEFAULT_TITLE_ONLY_PADDING ); + } + else + { + mTitle.SetPadding( DEFAULT_TITLE_PADDING ); + } + } + + // Allow derived classes to perform any layout they may need to do. + OnLayoutSetup(); + + // Update background visibility. + mPopupContainer.SetVisible( !( !mFooter && mPopupLayout.GetChildCount() == 0 ) ); + + // Create / destroy / position the tail as needed. + LayoutTail(); + + // Setup any layout and initialisation required for the selected animation. + LayoutAnimation(); + + RelayoutRequest(); +} + +void Popup::LayoutTail() +{ + // Removes the tail actor. + if( mTailImage && mTailImage.GetParent() ) { mTailImage.GetParent().Remove( mTailImage ); mTailImage.Reset(); } - std::string image = ""; + if( !mTailVisible ) + { + return; + } + + const Vector3& position = GetTailPosition(); + std::string image; // depending on position of tail around ParentOrigin, a different tail image is used... - if(position.y < Math::MACHINE_EPSILON_1) + if( position.y < Math::MACHINE_EPSILON_1 ) { - image = mPopupStyle->tailUpImage; + image = mTailUpImage; } - else if(position.y > 1.0f - Math::MACHINE_EPSILON_1) + else if( position.y > 1.0f - Math::MACHINE_EPSILON_1 ) { - image = mPopupStyle->tailDownImage; + image = mTailDownImage; } - else if(position.x < Math::MACHINE_EPSILON_1) + else if( position.x < Math::MACHINE_EPSILON_1 ) { - image = mPopupStyle->tailLeftImage; + image = mTailLeftImage; } - else if(position.x > 1.0f - Math::MACHINE_EPSILON_1) + else if( position.x > 1.0f - Math::MACHINE_EPSILON_1 ) { - image = mPopupStyle->tailRightImage; + image = mTailRightImage; } - if(image != "") + if( !image.empty() ) { + // Adds the tail actor. Image tail = ResourceImage::New( image ); - mTailImage = ImageActor::New(tail); + mTailImage = Toolkit::ImageView::New( tail ); + mTailImage.SetName( "tailImage" ); const Vector3 anchorPoint = AnchorPoint::BOTTOM_RIGHT - position; + mTailImage.SetParentOrigin( position ); + mTailImage.SetAnchorPoint( anchorPoint ); - mTailImage.SetParentOrigin(position); - mTailImage.SetAnchorPoint(anchorPoint); - - mBottomBg.Add(mTailImage); + mPopupContainer.Add( mTailImage ); } } -void Popup::HideTail() +void Popup::SetContextualMode( Toolkit::Popup::ContextualMode mode ) +{ + mContextualMode = mode; + mLayoutDirty = true; +} + +Toolkit::Popup::ContextualMode Popup::GetContextualMode() const +{ + return mContextualMode; +} + +Toolkit::Control Popup::CreateBacking() +{ + Toolkit::Control backing = Control::New(); + backing.SetBackgroundColor( Vector4( mBackingColor.r, mBackingColor.g, mBackingColor.b, 1.0f ) ); + backing.SetName( "popupBacking" ); + + // Must always be positioned top-left of stage, regardless of parent. + backing.SetInheritPosition(false); + backing.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + + // Always the full size of the stage. + backing.SetResizePolicy( ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS ); + backing.SetSize( Stage::GetCurrent().GetSize() ); + + // Catch events. + backing.SetSensitive( true ); + + // Default to being transparent. + backing.SetProperty( Actor::Property::COLOR_ALPHA, 0.0f ); + backing.TouchedSignal().Connect( this, &Popup::OnBackingTouched ); + backing.WheelEventSignal().Connect( this, &Popup::OnBackingWheelEvent ); + return backing; +} + +Toolkit::Popup::TouchedOutsideSignalType& Popup::OutsideTouchedSignal() +{ + return mTouchedOutsideSignal; +} + +Toolkit::Popup::DisplayStateChangeSignalType& Popup::ShowingSignal() +{ + return mShowingSignal; +} + +Toolkit::Popup::DisplayStateChangeSignalType& Popup::ShownSignal() +{ + return mShownSignal; +} + +Toolkit::Popup::DisplayStateChangeSignalType& Popup::HidingSignal() +{ + return mHidingSignal; +} + +Toolkit::Popup::DisplayStateChangeSignalType& Popup::HiddenSignal() { - ShowTail(ParentOrigin::CENTER); + return mHiddenSignal; } -void Popup::SetStyle(PopupStyle& style) +void Popup::SetTailVisibility( bool visible ) { - mPopupStyle = PopupStylePtr(&style); - // update // + mTailVisible = visible; + mLayoutDirty = true; } -PopupStylePtr Popup::GetStyle() const +const bool Popup::IsTailVisible() const { - return mPopupStyle; + return mTailVisible; } -void Popup::SetDefaultBackgroundImage() +void Popup::SetTailPosition( Vector3 position ) { - Image bg = ResourceImage::New( mPopupStyle->backgroundImage ); - ImageActor bgImage = ImageActor::New( bg ); - bgImage.SetStyle( ImageActor::STYLE_NINE_PATCH ); - bgImage.SetNinePatchBorder( mPopupStyle->backgroundScale9Border ); + mTailPosition = position; + mLayoutDirty = true; +} - Image buttonBg = ResourceImage::New( mPopupStyle->buttonAreaImage ); - ImageActor buttonBgImage = ImageActor::New( buttonBg ); - buttonBgImage.SetStyle( ImageActor::STYLE_NINE_PATCH ); - buttonBgImage.SetNinePatchBorder( mPopupStyle->buttonArea9PatchBorder ); +const Vector3& Popup::GetTailPosition() const +{ + return mTailPosition; +} - SetBackgroundImage( bgImage ); - SetButtonAreaImage( buttonBgImage ); +void Popup::SetAnimationDuration( float duration ) +{ + mAnimationDuration = duration; + mLayoutDirty = true; } -void Popup::CreateBacking() +float Popup::GetAnimationDuration() const { - mBacking = Dali::Toolkit::CreateSolidColorActor( mPopupStyle->backingColor ); + return mAnimationDuration; +} - mBacking.SetPositionInheritanceMode(DONT_INHERIT_POSITION); - mBacking.SetSensitive(true); +void Popup::SetAnimationMode( Toolkit::Popup::AnimationMode animationMode ) +{ + mAnimationMode = animationMode; + mLayoutDirty = true; +} - mLayer.Add(mBacking); - mBacking.SetOpacity(0.0f); - mBacking.SetPosition(0.0f, 0.0f, BACKING_DEPTH); - mBacking.TouchedSignal().Connect( this, &Popup::OnBackingTouched ); - mBacking.MouseWheelEventSignal().Connect(this, &Popup::OnBackingMouseWheelEvent); +Toolkit::Popup::AnimationMode Popup::GetAnimationMode() const +{ + return mAnimationMode; } -void Popup::CreateDialog() +void Popup::SetEntryAnimationData( const Property::Map& map ) { - // Adds default background image. - SetDefaultBackgroundImage(); + mEntryAnimationData.Clear(); + Scripting::NewAnimation( map, mEntryAnimationData ); +} - // Adds bottom background - mBottomBg = Actor::New(); - mPopupBg.Add( mBottomBg ); +void Popup::SetExitAnimationData( const Property::Map& map ) +{ + mExitAnimationData.Clear(); + Scripting::NewAnimation( map, mExitAnimationData ); } -void Popup::HandleStateChange( Toolkit::Popup::PopupState state, float duration ) +void Popup::SetAutoHideDelay( int delay ) { - const Vector2& stageSize( Stage::GetCurrent().GetSize() ); + mAutoHideDelay = delay; +} - Vector3 targetSize; - float targetBackingAlpha; - Vector3 targetBackingSize; +int Popup::GetAutoHideDelay() const +{ + return mAutoHideDelay; +} - if(mState == state) +void Popup::SetBackingEnabled( bool enabled ) +{ + mBackingEnabled = enabled; + mLayoutDirty = true; +} + +const bool Popup::IsBackingEnabled() const +{ + return mBackingEnabled; +} + +void Popup::SetBackingColor( Vector4 color ) +{ + mBackingColor = color; + mBacking.SetBackgroundColor( Vector4( color.r, color.g, color.b, 1.0f ) ); + mLayoutDirty = true; +} + +const Vector4& Popup::GetBackingColor() const +{ + return mBackingColor; +} + +void Popup::SetTailUpImage( std::string image ) +{ + mTailUpImage = image; + mLayoutDirty = true; +} + +const std::string& Popup::GetTailUpImage() const +{ + return mTailUpImage; +} + +void Popup::SetTailDownImage( std::string image ) +{ + mTailDownImage = image; + mLayoutDirty = true; +} + +const std::string& Popup::GetTailDownImage() const +{ + return mTailDownImage; +} + +void Popup::SetTailLeftImage( std::string image ) +{ + mTailLeftImage = image; + mLayoutDirty = true; +} + +const std::string& Popup::GetTailLeftImage() const +{ + return mTailLeftImage; +} + +void Popup::SetTailRightImage( std::string image ) +{ + mTailRightImage = image; + mLayoutDirty = true; +} + +const std::string& Popup::GetTailRightImage() const +{ + return mTailRightImage; +} + +void Popup::SetTouchTransparent( bool enabled ) +{ + mTouchTransparent = enabled; +} + +const bool Popup::IsTouchTransparent() const +{ + return mTouchTransparent; +} + +void Popup::SetProperty( BaseObject* object, Property::Index propertyIndex, const Property::Value& value ) +{ + Toolkit::Popup popup = Toolkit::Popup::DownCast( Dali::BaseHandle( object ) ); + + if ( popup ) { - return; + Popup& popupImpl( GetImpl( popup ) ); + + switch ( propertyIndex ) + { + case Toolkit::Popup::Property::TITLE: + { + Property::Map valueMap; + if( value.Get( valueMap ) ) + { + popupImpl.SetTitle( Scripting::NewActor( valueMap ) ); + } + break; + } + case Toolkit::Popup::Property::CONTENT: + { + Property::Map valueMap; + if( value.Get( valueMap ) ) + { + popupImpl.SetContent( Scripting::NewActor( valueMap ) ); + } + break; + } + case Toolkit::Popup::Property::FOOTER: + { + Property::Map valueMap; + if( value.Get( valueMap ) ) + { + popupImpl.SetFooter( Scripting::NewActor( valueMap ) ); + } + break; + } + case Toolkit::Popup::Property::DISPLAY_STATE: + { + std::string valueString; + if( value.Get( valueString ) ) + { + Toolkit::Popup::DisplayState displayState( Toolkit::Popup::HIDDEN ); + if( Scripting::GetEnumeration< Toolkit::Popup::DisplayState >( valueString.c_str(), DisplayStateTable, DisplayStateTableCount, displayState ) ) + { + popupImpl.SetDisplayState( displayState ); + } + } + break; + } + case Toolkit::Popup::Property::TOUCH_TRANSPARENT: + { + bool valueBool; + if( value.Get( valueBool ) ) + { + popupImpl.SetTouchTransparent( valueBool ); + } + break; + } + case Toolkit::Popup::Property::TAIL_VISIBILITY: + { + bool valueBool; + if( value.Get( valueBool ) ) + { + popupImpl.SetTailVisibility( valueBool ); + } + break; + } + case Toolkit::Popup::Property::TAIL_POSITION: + { + Vector3 valueVector3; + if( value.Get( valueVector3 ) ) + { + popupImpl.SetTailPosition( valueVector3 ); + } + break; + } + case Toolkit::Popup::Property::CONTEXTUAL_MODE: + { + std::string valueString; + if( value.Get( valueString ) ) + { + Toolkit::Popup::ContextualMode contextualMode( Toolkit::Popup::BELOW ); + if( Scripting::GetEnumeration< Toolkit::Popup::ContextualMode >( valueString.c_str(), ContextualModeTable, ContextualModeTableCount, contextualMode ) ) + { + popupImpl.SetContextualMode( contextualMode ); + } + } + break; + } + case Toolkit::Popup::Property::ANIMATION_DURATION: + { + float valueFloat; + if( value.Get( valueFloat ) ) + { + popupImpl.SetAnimationDuration( valueFloat ); + } + break; + } + case Toolkit::Popup::Property::ANIMATION_MODE: + { + std::string valueString; + if( value.Get( valueString ) ) + { + Toolkit::Popup::AnimationMode animationMode( Toolkit::Popup::FADE ); + if( Scripting::GetEnumeration< Toolkit::Popup::AnimationMode >( valueString.c_str(), AnimationModeTable, AnimationModeTableCount, animationMode ) ) + { + popupImpl.SetAnimationMode( animationMode ); + } + } + break; + } + case Toolkit::Popup::Property::ENTRY_ANIMATION: + { + Property::Map valueMap; + if( value.Get( valueMap ) ) + { + popupImpl.SetEntryAnimationData( valueMap ); + } + break; + } + case Toolkit::Popup::Property::EXIT_ANIMATION: + { + Property::Map valueMap; + if( value.Get( valueMap ) ) + { + popupImpl.SetExitAnimationData( valueMap ); + } + break; + } + case Toolkit::Popup::Property::AUTO_HIDE_DELAY: + { + int valueInt; + if( value.Get( valueInt ) ) + { + popupImpl.SetAutoHideDelay( valueInt ); + } + break; + } + case Toolkit::Popup::Property::BACKING_ENABLED: + { + bool valueBool; + if( value.Get( valueBool ) ) + { + popupImpl.SetBackingEnabled( valueBool ); + } + break; + } + case Toolkit::Popup::Property::BACKING_COLOR: + { + Vector4 valueVector4; + if( value.Get( valueVector4 ) ) + { + popupImpl.SetBackingColor( valueVector4 ); + } + break; + } + case Toolkit::Popup::Property::POPUP_BACKGROUND_IMAGE: + { + std::string valueString; + if( value.Get( valueString ) ) + { + Image image = ResourceImage::New( valueString ); + if( image ) + { + Toolkit::ImageView actor = Toolkit::ImageView::New( image ); + popupImpl.SetPopupBackgroundImage( actor ); + } + } + break; + } + case Toolkit::Popup::Property::TAIL_UP_IMAGE: + { + std::string valueString; + if( value.Get( valueString ) ) + { + popupImpl.SetTailUpImage( valueString ); + } + break; + } + case Toolkit::Popup::Property::TAIL_DOWN_IMAGE: + { + std::string valueString; + if( value.Get( valueString ) ) + { + popupImpl.SetTailDownImage( valueString ); + } + break; + } + case Toolkit::Popup::Property::TAIL_LEFT_IMAGE: + { + std::string valueString; + if( value.Get( valueString ) ) + { + popupImpl.SetTailLeftImage( valueString ); + } + break; + } + case Toolkit::Popup::Property::TAIL_RIGHT_IMAGE: + { + std::string valueString; + if( value.Get( valueString ) ) + { + popupImpl.SetTailRightImage( valueString ); + } + break; + } + } } - mState = state; - switch(state) +} + +Property::Value Popup::GetProperty( BaseObject* object, Property::Index propertyIndex ) +{ + Property::Value value; + + Toolkit::Popup popup = Toolkit::Popup::DownCast( Dali::BaseHandle( object ) ); + + if ( popup ) { - case Toolkit::Popup::POPUP_HIDE: + Popup& popupImpl( GetImpl( popup ) ); + + switch ( propertyIndex ) { - targetSize = Vector3(0.0f, 0.0f, 1.0f); - targetBackingAlpha = 0.0f; - targetBackingSize = Vector3(0.0f, 0.0f, 1.0f); - mShowing = false; - ClearKeyInputFocus(); - - // Retore the keyboard focus when popup is hidden - if(mPreviousFocusedActor && mPreviousFocusedActor.IsKeyboardFocusable() ) + case Toolkit::Popup::Property::TITLE: + { + Property::Map map; + Scripting::CreatePropertyMap( popupImpl.GetTitle(), map ); + value = map; + break; + } + case Toolkit::Popup::Property::CONTENT: + { + Property::Map map; + Scripting::CreatePropertyMap( popupImpl.GetContent(), map ); + value = map; + break; + } + case Toolkit::Popup::Property::FOOTER: + { + Property::Map map; + Scripting::CreatePropertyMap( popupImpl.GetFooter(), map ); + value = map; + break; + } + case Toolkit::Popup::Property::DISPLAY_STATE: + { + value = Scripting::GetLinearEnumerationName< Toolkit::Popup::DisplayState >( popupImpl.GetDisplayState(), DisplayStateTable, DisplayStateTableCount ); + break; + } + case Toolkit::Popup::Property::TOUCH_TRANSPARENT: + { + value = popupImpl.IsTouchTransparent(); + break; + } + case Toolkit::Popup::Property::TAIL_VISIBILITY: + { + value = popupImpl.IsTailVisible(); + break; + } + case Toolkit::Popup::Property::TAIL_POSITION: { - Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get(); - if( keyboardFocusManager ) - { - keyboardFocusManager.SetCurrentFocusActor(mPreviousFocusedActor); - } + value = popupImpl.GetTailPosition(); + break; } - - break; - } - - case Toolkit::Popup::POPUP_SHOW: - default: - { - targetSize = Vector3(1.0f, 1.0f, 1.0f); - targetBackingAlpha = 1.0f; - float length = (stageSize.width > stageSize.height) ? stageSize.width : stageSize.height; - targetBackingSize = Vector3( length, length, 1.0f ); - mShowing = true; - - // Add contents to stage for showing. - if( !mLayer.GetParent() ) + case Toolkit::Popup::Property::CONTEXTUAL_MODE: { - mAlterAddedChild = false; - Self().Add(mLayer); - mAlterAddedChild = true; + value = Scripting::GetLinearEnumerationName< Toolkit::Popup::ContextualMode >( popupImpl.GetContextualMode(), ContextualModeTable, ContextualModeTableCount ); + break; } - Self().SetSensitive(true); - SetKeyInputFocus(); - - // Handle the keyboard focus when popup is shown - Dali::Toolkit::KeyboardFocusManager keyboardFocusManager = Dali::Toolkit::KeyboardFocusManager::Get(); - if( keyboardFocusManager ) + case Toolkit::Popup::Property::ANIMATION_DURATION: { - mPreviousFocusedActor = keyboardFocusManager.GetCurrentFocusActor(); - - if( mContent && mContent.IsKeyboardFocusable() ) - { - // If content is focusable, move the focus to content - keyboardFocusManager.SetCurrentFocusActor(mContent); - } - else if( !mButtons.empty() ) - { - // Otherwise, movethe focus to the first button - keyboardFocusManager.SetCurrentFocusActor(mButtons[0]); - } - else + value = popupImpl.GetAnimationDuration(); + break; + } + case Toolkit::Popup::Property::ANIMATION_MODE: + { + value = Scripting::GetLinearEnumerationName< Toolkit::Popup::AnimationMode >( popupImpl.GetAnimationMode(), AnimationModeTable, AnimationModeTableCount ); + break; + } + case Toolkit::Popup::Property::ENTRY_ANIMATION: + { + // Note: Cannot retrieve property map from animation. + Property::Map map; + value = map; + break; + } + case Toolkit::Popup::Property::EXIT_ANIMATION: + { + // Note: Cannot retrieve property map from animation. + Property::Map map; + value = map; + break; + } + case Toolkit::Popup::Property::AUTO_HIDE_DELAY: + { + value = popupImpl.GetAutoHideDelay(); + break; + } + case Toolkit::Popup::Property::BACKING_ENABLED: + { + value = popupImpl.IsBackingEnabled(); + break; + } + case Toolkit::Popup::Property::BACKING_COLOR: + { + value = popupImpl.GetBackingColor(); + break; + } + case Toolkit::Popup::Property::POPUP_BACKGROUND_IMAGE: + { + ResourceImage image = ResourceImage::DownCast( popupImpl.GetPopupBackgroundImage() ); + if( image ) { - DALI_LOG_WARNING("There is no focusable in popup\n"); + value = image.GetUrl(); } + break; + } + case Toolkit::Popup::Property::TAIL_UP_IMAGE: + { + value = popupImpl.GetTailUpImage(); + break; + } + case Toolkit::Popup::Property::TAIL_DOWN_IMAGE: + { + value = popupImpl.GetTailDownImage(); + break; + } + case Toolkit::Popup::Property::TAIL_LEFT_IMAGE: + { + value = popupImpl.GetTailLeftImage(); + break; + } + case Toolkit::Popup::Property::TAIL_RIGHT_IMAGE: + { + value = popupImpl.GetTailRightImage(); + break; } - break; - } - } - - mBacking.SetSize( targetBackingSize ); - - if(duration > Math::MACHINE_EPSILON_1) - { - if ( mAnimation ) - { - mAnimation.Stop(); - mAnimation.Clear(); - mAnimation.Reset(); - } - mAnimation = Animation::New(duration); - - if(mShowing) - { - mAnimation.AnimateTo( Property(mBacking, Actor::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) ); - mAnimation.AnimateTo( Property(mPopupBg, Actor::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(duration * 0.5f, duration * 0.5f) ); - } - else - { - mAnimation.AnimateTo( Property(mBacking, Actor::COLOR_ALPHA), targetBackingAlpha, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) ); - mAnimation.AnimateTo( Property(mPopupBg, Actor::SCALE), targetSize, AlphaFunctions::EaseInOut, TimePeriod(0.0f, duration * 0.5f) ); } - mAnimation.Play(); - mAnimation.FinishedSignal().Connect(this, &Popup::OnStateAnimationFinished); - } - else - { - mBacking.SetOpacity( targetBackingAlpha ); - mPopupBg.SetScale( targetSize ); - - HandleStateChangeComplete(); - } -} - -void Popup::HandleStateChangeComplete() -{ - // Remove contents from stage if completely hidden. - if( (mState == Toolkit::Popup::POPUP_HIDE) && (mLayer.GetParent()) ) - { - Self().Remove(mLayer); - Self().SetSensitive( false ); - - // Guard against destruction during signal emission - Toolkit::Popup handle( GetOwner() ); - mHiddenSignal.Emit(); } -} - -Toolkit::Popup::TouchedOutsideSignalType& Popup::OutsideTouchedSignal() -{ - return mTouchedOutsideSignal; -} -Toolkit::Popup::HiddenSignalType& Popup::HiddenSignal() -{ - return mHiddenSignal; + return value; } bool Popup::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor ) @@ -564,6 +1429,18 @@ bool Popup::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tra { popup.OutsideTouchedSignal().Connect( tracker, functor ); } + else if( 0 == strcmp( signalName.c_str(), SIGNAL_SHOWING ) ) + { + popup.ShowingSignal().Connect( tracker, functor ); + } + else if( 0 == strcmp( signalName.c_str(), SIGNAL_SHOWN ) ) + { + popup.ShownSignal().Connect( tracker, functor ); + } + else if( 0 == strcmp( signalName.c_str(), SIGNAL_HIDING ) ) + { + popup.HidingSignal().Connect( tracker, functor ); + } else if( 0 == strcmp( signalName.c_str(), SIGNAL_HIDDEN ) ) { popup.HiddenSignal().Connect( tracker, functor ); @@ -577,224 +1454,293 @@ bool Popup::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tra return connected; } -void Popup::OnStateAnimationFinished( Animation& source ) +bool Popup::OnBackingTouched( Actor actor, const TouchEvent& event ) { - HandleStateChangeComplete(); -} + // Allow events to pass through if touch transparency is enabled. + if( mTouchTransparent ) + { + return false; + } -bool Popup::OnBackingTouched(Actor actor, const TouchEvent& event) -{ - if(event.GetPointCount()>0) + if( event.GetPointCount() > 0 ) { - const TouchPoint& point = event.GetPoint(0); + const TouchPoint& point = event.GetPoint( 0 ); - if(point.state == TouchPoint::Down) + if( point.state == TouchPoint::Down ) { - // Guard against destruction during signal emission + // Guard against destruction during signal emission. Toolkit::Popup handle( GetOwner() ); mTouchedOutsideSignal.Emit(); } } + // Block anything behind backing becoming touched. + mLayer.SetTouchConsumed( true ); return true; } -bool Popup::OnBackingMouseWheelEvent(Actor actor, const MouseWheelEvent& event) +bool Popup::OnBackingWheelEvent( Actor actor, const WheelEvent& event ) { - // consume mouse wheel event in dimmed backing actor + // Allow events to pass through if touch transparency is enabled. + if( mTouchTransparent ) + { + return false; + } + + // Consume wheel event in dimmed backing actor. + mLayer.SetTouchConsumed( true ); return true; } bool Popup::OnDialogTouched(Actor actor, const TouchEvent& event) { - // consume event (stops backing actor receiving touch events) + // Allow events to pass through if touch transparency is enabled. + if( mTouchTransparent ) + { + return false; + } + + // Consume event (stops backing actor receiving touch events) + mLayer.SetTouchConsumed( true ); return true; } -void Popup::OnControlChildAdd( Actor& child ) +void Popup::OnStageConnection( int depth ) { - // reparent any children added by user to the body layer. - if( mAlterAddedChild ) - { - // Removes previously added content. - if( mContent ) - { - mPopupBg.Remove( mContent ); - } - - // Reparent new content. - Self().Remove( child ); - - // keep a handle to the new content. - mContent = child; + Control::OnStageConnection( depth ); - mPopupBg.Add( mContent ); - } + mLayoutDirty = true; + RelayoutRequest(); } -void Popup::OnControlSizeSet( const Vector3& targetSize ) +void Popup::OnChildAdd( Actor& child ) { - mLayer.SetSize( targetSize ); - mPopupBg.SetSize( targetSize ); + Control::OnChildAdd( child ); - const Vector4 outerBorder = mPopupStyle->backgroundOuterBorder; - if( mBackgroundImage ) + // Re-parent any children added by user to the body layer. + if( mAlterAddedChild ) { - mBackgroundImage.SetSize( BackgroundSize( outerBorder,targetSize ) ); + SetContent( child ); } - if( mButtonAreaImage ) + else { - mButtonAreaImage.SetSize( ButtonAreaSize( outerBorder, targetSize ) ); + mLayoutDirty = true; + RelayoutRequest(); } - } -void Popup::OnRelayout( const Vector2& size, ActorSizeContainer& container ) +void Popup::LayoutContext( const Vector2& size ) { - // Set the popup size - Vector2 popupSize; - popupSize.width = size.width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ); - popupSize.height = size.height - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ); - - // Update sizes of all popup's components. - - // Relayout background image. - // Adjust background position and size relative to parent to cater to outer Border. - // Some backgrounds are intended to over-spill. That is some content - // should appear outside the Dialog on all sides i.e. Shadows, glow effects. - const Vector4 outerBorder = mPopupStyle->backgroundOuterBorder; - - if( mBackgroundImage ) + // Do nothing if not in a contextual mode (or there is no parent context). + Actor self = Self(); + Actor parent = self.GetParent(); + if( ( mContextualMode == Toolkit::Popup::NON_CONTEXTUAL ) || !parent ) { - mBackgroundImage.SetSize(BackgroundSize(outerBorder, Vector3(size))); - mBackgroundImage.SetAnchorPoint( AnchorPoint::TOP_LEFT ); - mBackgroundImage.SetParentOrigin( ParentOrigin::TOP_LEFT ); - mBackgroundImage.SetPosition( -outerBorder.x, -outerBorder.y, 0.0f ); + return; } - if( mPopupBg && mButtonAreaImage ) + mPopupContainer.SetParentOrigin( ParentOrigin::CENTER ); + // We always anchor to the CENTER, rather than a different anchor point for each contextual + // mode to allow code-reuse of the bound checking code (for maintainability). + mPopupContainer.SetAnchorPoint( AnchorPoint::CENTER ); + + // Setup with some pre-calculations for speed. + Vector3 halfStageSize( Stage().GetCurrent().GetSize() / 2.0f ); + Vector3 parentPosition( parent.GetCurrentPosition() ); + Vector2 halfSize( size / 2.0f ); + Vector2 halfParentSize( parent.GetRelayoutSize( Dimension::WIDTH ) / 2.0f, parent.GetRelayoutSize( Dimension::HEIGHT ) / 2.0f ); + Vector3 newPosition( Vector3::ZERO ); + + // Perform different positioning based on the specified contextual layout mode. + switch( mContextualMode ) { - // If there are no buttons, button background is also removed. - if ( mButtons.size() == 0 ) + case Toolkit::Popup::BELOW: { - mPopupBg.Remove( mButtonAreaImage ); + newPosition.x += halfSize.x - halfParentSize.x; + newPosition.y += halfSize.y + halfParentSize.y + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.y; + break; } - else + case Toolkit::Popup::ABOVE: { - mButtonAreaImage.SetSize( ButtonAreaSize(outerBorder, Vector3(size)) ); - mButtonAreaImage.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); - mButtonAreaImage.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); - mButtonAreaImage.SetY( -outerBorder.z - POPUP_OUT_MARGIN_HEIGHT ); - - mPopupBg.Add( mButtonAreaImage ); + newPosition.x += halfSize.x - halfParentSize.x; + newPosition.y -= halfSize.y + halfParentSize.y + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.y; + break; + } + case Toolkit::Popup::RIGHT: + { + newPosition.x += halfSize.x + halfParentSize.x + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.x; + newPosition.y += halfSize.y - halfParentSize.y; + break; + } + case Toolkit::Popup::LEFT: + { + newPosition.x -= halfSize.x + halfParentSize.x + DEFAULT_CONTEXTUAL_ADJACENCY_MARGIN.x; + newPosition.y += halfSize.y - halfParentSize.y; + break; + } + case Toolkit::Popup::NON_CONTEXTUAL: + { + // Code won't reach here (caught earlier). + break; } } - // Relayout title - Vector3 positionOffset( 0.0f, mPopupStyle->margin + POPUP_OUT_MARGIN_WIDTH, CONTENT_DEPTH ); - if( mTitle ) + // On-screen position checking. + // Check new position is not too far right. If so, correct it. + // Note: Check for right rather than left first, so if popup is too wide, the left check overrides + // the right check and we at least see the left portion of the popup (as this is more useful). + if( newPosition.x >= ( halfStageSize.x - parentPosition.x - halfSize.x - DEFAULT_CONTEXTUAL_STAGE_BORDER.x ) ) + { + newPosition.x = halfStageSize.x - parentPosition.x - halfSize.x - DEFAULT_CONTEXTUAL_STAGE_BORDER.x; + } + // Check new position is not too far left. If so, correct it. + if( newPosition.x < halfSize.x - ( parentPosition.x + halfStageSize.x ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.x ) + { + newPosition.x = halfSize.x - ( parentPosition.x + halfStageSize.x ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.x;// - parentSize.x; + } + // Check new position is not too far down. If so, correct it. + if( newPosition.y >= ( halfStageSize.y - parentPosition.y - halfSize.y - DEFAULT_CONTEXTUAL_STAGE_BORDER.y ) ) + { + newPosition.y = halfStageSize.y - parentPosition.y - halfSize.y - DEFAULT_CONTEXTUAL_STAGE_BORDER.y; + } + // Check new position is not too far up. If so, correct it. + if( newPosition.y < halfSize.y - ( parentPosition.y + halfStageSize.y ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.y ) { - Vector2 titleSize; - titleSize.width = popupSize.width; - titleSize.height = mTitle.GetHeightForWidth( titleSize.width ); + newPosition.y = halfSize.y - ( parentPosition.y + halfStageSize.y ) + DEFAULT_CONTEXTUAL_STAGE_BORDER.y; + } - // As the default size policy for text-view is Fixed & Fixed, a size needs to be set. - // Otherwise size-negotiation algorithm uses the GetNaturalSize() with doesn't take - // into account the multiline and exceed policies, giving as result a wrong size. - mTitle.SetSize( titleSize ); - Relayout( mTitle, titleSize, container ); + // Set the final position. + mPopupContainer.SetPosition( newPosition ); +} - mTitle.SetAnchorPoint( AnchorPoint::TOP_CENTER ); - mTitle.SetParentOrigin( ParentOrigin::TOP_CENTER ); - mTitle.SetPosition( positionOffset ); +void Popup::OnRelayout( const Vector2& size, RelayoutContainer& container ) +{ + Vector2 useSize( size ); - positionOffset.y += titleSize.height + mPopupStyle->margin; - } + // Use the Popup layouts size, unless requested to use a fixed size. + // In which case take the size set for the Popup itself. + ResizePolicy::Type widthPolicy = Self().GetResizePolicy( Dimension::WIDTH ); + ResizePolicy::Type heightPolicy = Self().GetResizePolicy( Dimension::HEIGHT ); - // Relayout content - if( mContent ) + // Width calculations: + if( widthPolicy == ResizePolicy::USE_NATURAL_SIZE || widthPolicy == ResizePolicy::FIT_TO_CHILDREN ) { - // If the content width is greater than popup width then scale it down/wrap text as needed - Vector2 contentSize( RelayoutHelper::GetNaturalSize( mContent ) ); - if( contentSize.width > popupSize.width ) - { - contentSize.width = popupSize.width; - contentSize.height = RelayoutHelper::GetHeightForWidth( mContent, contentSize.width ); - } + // If we using a child-based policy, take the size from the popup layout. + mPopupLayout.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::WIDTH ); + useSize.width = mPopupLayout.GetRelayoutSize( Dimension::WIDTH ); - mContent.SetSize( contentSize ); - Relayout( mContent, contentSize, container ); + mPopupLayout.SetFitWidth( 0u ); + } + else + { + // If we using a parent-based policy, take the size from the popup object itself (self). + mPopupLayout.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::WIDTH ); - mContent.SetParentOrigin(ParentOrigin::TOP_CENTER); - mContent.SetAnchorPoint(AnchorPoint::TOP_CENTER); + mPopupLayout.SetFixedWidth( 0u, useSize.width ); + } - mContent.SetPosition( positionOffset ); + // Height calculations: + // Title: Let the title be as high as it needs to be. + mPopupLayout.SetFitHeight( 0u ); - positionOffset.y += contentSize.height + mPopupStyle->margin; + // Footer: Convert the footer's resize policy to a TableView row policy. + if( mFooter ) + { + ResizePolicy::Type footerHeightPolicy = mFooter.GetResizePolicy( Dimension::HEIGHT ); + if( ( footerHeightPolicy == ResizePolicy::USE_NATURAL_SIZE ) || + ( footerHeightPolicy == ResizePolicy::FIT_TO_CHILDREN ) ) + { + mPopupLayout.SetFitHeight( 2u ); + } + else if( footerHeightPolicy == ResizePolicy::FIXED ) + { + mPopupLayout.SetFixedHeight( 2u, mFooter.GetRelayoutSize( Dimension::HEIGHT) ); + } + else + { + mPopupLayout.SetRelativeHeight( 2u, 1.0f ); + } + } + else + { + mPopupLayout.SetFixedHeight( 2u, 0.0f ); } - // Relayout Button Area - if( mBottomBg ) + // Popup contents: Adjust the tableview's policies based on the popup's policies. + if( heightPolicy == ResizePolicy::USE_NATURAL_SIZE || heightPolicy == ResizePolicy::FIT_TO_CHILDREN ) { - mBottomBg.SetSize( popupSize.width, mPopupStyle->bottomSize.height ); + mPopupLayout.SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT ); - mBottomBg.SetParentOrigin(ParentOrigin::TOP_CENTER); - mBottomBg.SetAnchorPoint(AnchorPoint::TOP_CENTER); + // Let both the contents expand as necessary. + mPopupLayout.SetFitHeight( 1u ); + useSize.height = mPopupLayout.GetRelayoutSize( Dimension::HEIGHT ); + } + else + { + mPopupLayout.SetResizePolicy( heightPolicy, Dimension::HEIGHT ); - mBottomBg.SetPosition( positionOffset ); + // Let the content expand to fill the remaining space. + mPopupLayout.SetRelativeHeight( 1u, 1.0f ); + mPopupLayout.SetResizePolicy( ResizePolicy::USE_ASSIGNED_SIZE, Dimension::HEIGHT ); } - // Relayout All buttons - if ( !mButtons.empty() ) + // Relayout the popup-layout to give it it's new size this frame. + container.Add( mPopupLayout, useSize ); + + if( mContent ) { - // All buttons should be the same size and fill the button area. The button spacing needs to be accounted for as well. - Vector2 buttonSize( ( ( popupSize.width - mPopupStyle->buttonSpacing * ( mButtons.size() - 1 ) ) / mButtons.size() ), - mPopupStyle->bottomSize.height - mPopupStyle->margin ); + container.Add( mContent, Vector2( mContent.GetRelayoutSize( Dimension::WIDTH ), mContent.GetRelayoutSize( Dimension::HEIGHT ) ) ); + } - Vector3 buttonPosition; + // Perform contextual layout setup if required. + // This is done each time in case the parent moves. + // This will have no effect if no contextual mode is selected. + LayoutContext( useSize ); +} - for ( ActorIter iter = mButtons.begin(), endIter = mButtons.end(); - iter != endIter; - ++iter, buttonPosition.x += mPopupStyle->buttonSpacing + buttonSize.width ) - { - iter->SetPosition( buttonPosition ); +void Popup::OnSetResizePolicy( ResizePolicy::Type policy, Dimension::Type dimension ) +{ + // To get the popup to emulate fit-to-children, we need to actually set use-natural-size. + if( ( dimension & Dimension::HEIGHT ) && ( policy == ResizePolicy::FIT_TO_CHILDREN ) ) + { + Self().SetResizePolicy( ResizePolicy::USE_NATURAL_SIZE, Dimension::HEIGHT ); + } - // If there is only one button, it needs to be laid out on center. - if ( mButtons.size() == 1 ) - { - iter->SetAnchorPoint( AnchorPoint::CENTER ); - iter->SetParentOrigin( ParentOrigin::CENTER ); - } - else - { - iter->SetAnchorPoint( AnchorPoint::CENTER_LEFT ); - iter->SetParentOrigin( ParentOrigin::CENTER_LEFT ); - } + mLayoutDirty = true; + return; +} - Relayout( *iter, buttonSize, container ); - } - } +Vector3 Popup::GetNaturalSize() +{ + return mPopupLayout.GetNaturalSize(); +} - if( mShowing && mBacking ) - { - Vector2 stageSize = Stage::GetCurrent().GetSize(); - float length = (stageSize.width > stageSize.height) ? stageSize.width : stageSize.height; - Vector3 targetBackingSize = Vector3( length, length, 1.0f ); +float Popup::GetHeightForWidth( float width ) +{ + return mPopupLayout.GetHeightForWidth( width ); +} - mBacking.SetSize( targetBackingSize ); - } +float Popup::GetWidthForHeight( float height ) +{ + return mPopupLayout.GetWidthForHeight( height ); } -bool Popup::OnKeyEvent(const KeyEvent& event) +bool Popup::OnKeyEvent( const KeyEvent& event ) { + // Allow events to pass through if touch transparency is enabled. + if( mTouchTransparent ) + { + return false; + } + bool consumed = false; - if(event.state == KeyEvent::Down) + if( event.state == KeyEvent::Down ) { if (event.keyCode == Dali::DALI_KEY_ESCAPE || event.keyCode == Dali::DALI_KEY_BACK) { - SetState(Toolkit::Popup::POPUP_HIDE); + SetDisplayState( Toolkit::Popup::HIDDEN ); consumed = true; } } @@ -802,221 +1748,145 @@ bool Popup::OnKeyEvent(const KeyEvent& event) return consumed; } -Vector3 Popup::GetNaturalSize() +void Popup::AddFocusableChildrenRecursive( Actor parent, std::vector< Actor >& focusableActors ) { - float margin = 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ); - const float maxWidth = Stage::GetCurrent().GetSize().width - margin; - - Vector3 naturalSize( 0.0f, 0.0f, 0.0f ); - - if ( mTitle ) + if( parent ) { - Vector3 titleNaturalSize = mTitle.GetImplementation().GetNaturalSize(); - // Buffer to avoid errors. The width of the popup could potentially be the width of the title text. - // It was observed in this case that text wrapping was then inconsistent when seen on device - const float titleBuffer = 0.5f; - titleNaturalSize.width += titleBuffer; + Toolkit::Control control = Toolkit::Control::DownCast( parent ); + bool layoutControl = control && GetImplementation( control ).IsKeyboardNavigationSupported(); - // As TextView GetNaturalSize does not take wrapping into account, limit the width - // to that of the stage - if( titleNaturalSize.width >= maxWidth) - { - naturalSize.width = maxWidth; - naturalSize.height = mTitle.GetImplementation().GetHeightForWidth( naturalSize.width ); - } - else + if( parent.IsKeyboardFocusable() || layoutControl ) { - naturalSize += titleNaturalSize; - } + focusableActors.push_back( parent ); - naturalSize.height += mPopupStyle->margin; + if( !layoutControl ) + { + for( unsigned int i = 0, numberChildren = parent.GetChildCount(); i < numberChildren; ++i ) + { + Actor child( parent.GetChildAt( i ) ); + AddFocusableChildrenRecursive( child, focusableActors ); + } + } + } } +} - if( mContent ) +void Popup::AddFocusableChildren( Actor parent, std::vector< Actor >& focusableActors ) +{ + if( parent ) { - Vector3 contentSize = RelayoutHelper::GetNaturalSize( mContent ); - // Choose the biggest width - naturalSize.width = std::max( naturalSize.width, contentSize.width ); - if( naturalSize.width > maxWidth ) + Toolkit::Control control = Toolkit::Control::DownCast( parent ); + if( !GetImplementation( control ).IsKeyboardNavigationSupported() ) { - naturalSize.width = maxWidth; - contentSize.height = RelayoutHelper::GetHeightForWidth( mContent, maxWidth ); + for( unsigned int i = 0, numberChildren = parent.GetChildCount(); i < numberChildren; ++i ) + { + Actor child( parent.GetChildAt( i ) ); + AddFocusableChildrenRecursive( child, focusableActors ); + } + } + else + { + focusableActors.push_back( parent ); } - naturalSize.height += contentSize.height + mPopupStyle->margin; - } - - if( !mButtons.empty() ) - { - naturalSize.height += mPopupStyle->bottomSize.height; } - - // Add the margins - naturalSize.width += margin; - naturalSize.height += margin; - - return naturalSize; } -float Popup::GetHeightForWidth( float width ) +Actor Popup::GetNextKeyboardFocusableActor( Actor currentFocusedActor, Toolkit::Control::KeyboardFocus::Direction direction, bool loopEnabled ) { - float height( 0.0f ); - float popupWidth( width - 2.f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) ); - - if ( mTitle ) - { - height += mTitle.GetImplementation().GetHeightForWidth( popupWidth ); - height += mPopupStyle->margin; - } - - if( mContent ) + std::string currentStr; + if( currentFocusedActor ) { - height += RelayoutHelper::GetHeightForWidth( mContent, popupWidth ) + mPopupStyle->margin; + currentStr = currentFocusedActor.GetName(); } - if( !mButtons.empty() ) + Actor nextFocusableActor( currentFocusedActor ); + Actor currentFocusGroup; + if( currentFocusedActor ) { - height += mPopupStyle->bottomSize.height; + currentFocusGroup = KeyboardFocusManager::Get().GetFocusGroup( currentFocusedActor ); } - // Add the margins - float margin( 2.0f * ( POPUP_OUT_MARGIN_WIDTH + mPopupStyle->margin ) ); - height += margin; - - return height; -} - -float Popup::GetWidthForHeight( float height ) -{ - return GetNaturalSize().width; -} - -Actor Popup::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::Control::KeyboardFocusNavigationDirection direction, bool loopEnabled) -{ - Actor nextFocusableActor( currentFocusedActor ); - // TODO: Needs to be optimised - - if ( !currentFocusedActor || ( currentFocusedActor && KeyboardFocusManager::Get().GetFocusGroup(currentFocusedActor) != Self() ) ) + // The following statement checks that if we have a current focused actor, then the current focus group is not the popup content or footer. + // This is to detect if the focus is currently outside the popup, and if so, move it inside. + if( !currentFocusedActor || + ( currentFocusedActor && ( ( !mContent || ( currentFocusGroup != mContent ) ) && ( !mFooter || ( currentFocusGroup != mFooter ) ) ) ) ) { - // The current focused actor is not within popup + // The current focused actor is not within popup. if( mContent && mContent.IsKeyboardFocusable() ) { - // If content is focusable, move the focus to content + // If the content is focusable, move the focus to the content. nextFocusableActor = mContent; } - else if( !mButtons.empty() ) + else if( mFooter && mFooter.IsKeyboardFocusable() ) { - // Otherwise, movethe focus to the first button - nextFocusableActor = mButtons[0]; + // If the footer is focusable, move the focus to the footer. + nextFocusableActor = mFooter; } } else { - // Rebuild the focus chain because button or content can be added or removed dynamically - ActorContainer focusableActors; - if( mContent && mContent.IsKeyboardFocusable() ) - { - focusableActors.push_back(mContent); - } + // Rebuild the focus chain because controls or content can be added or removed dynamically + std::vector< Actor > focusableActors; - for(unsigned int i = 0; i < mButtons.size(); i++) + AddFocusableChildren( mContent, focusableActors ); + AddFocusableChildren( mFooter, focusableActors ); + + std::vector< Actor >::iterator endIterator = focusableActors.end(); + std::vector< Actor >::iterator currentIterator = focusableActors.begin(); + for( std::vector< Actor >::iterator iterator = focusableActors.begin(); iterator != endIterator; ++iterator ) { - if( mButtons[i] && mButtons[i].IsKeyboardFocusable() ) + if( currentFocusedActor == *iterator ) { - focusableActors.push_back(mButtons[i]); + currentIterator = iterator; } } - for ( ActorContainer::iterator iter = focusableActors.begin(), end = focusableActors.end(); iter != end; ++iter ) + if( currentIterator != endIterator ) { - if ( currentFocusedActor == *iter ) + switch( direction ) { - switch ( direction ) + case Toolkit::Control::KeyboardFocus::LEFT: { - case Toolkit::Control::Left: + if( currentIterator == focusableActors.begin() ) { - if ( iter == focusableActors.begin() ) - { - nextFocusableActor = *( focusableActors.end() - 1 ); - } - else - { - nextFocusableActor = *( iter - 1 ); - } - break; + nextFocusableActor = *( endIterator - 1 ); } - case Toolkit::Control::Right: + else { - if ( iter == focusableActors.end() - 1 ) - { - nextFocusableActor = *( focusableActors.begin() ); - } - else - { - nextFocusableActor = *( iter + 1 ); - } - break; + nextFocusableActor = *( currentIterator - 1 ); } - - case Toolkit::Control::Up: + break; + } + case Toolkit::Control::KeyboardFocus::RIGHT: + { + if( currentIterator == endIterator - 1 ) { - if ( *iter == mContent ) - { - nextFocusableActor = *( focusableActors.end() - 1 ); - } - else - { - if ( mContent && mContent.IsKeyboardFocusable() ) - { - nextFocusableActor = mContent; - } - else - { - if ( iter == focusableActors.begin() ) - { - nextFocusableActor = *( focusableActors.end() - 1 ); - } - else - { - nextFocusableActor = *( iter - 1 ); - } - } - } - break; + nextFocusableActor = *( focusableActors.begin() ); } - - case Toolkit::Control::Down: + else { - if ( mContent && mContent.IsKeyboardFocusable() ) - { - nextFocusableActor = mContent; - } - else - { - if ( iter == focusableActors.end() - 1 ) - { - nextFocusableActor = *( focusableActors.begin() ); - } - else - { - nextFocusableActor = *( iter + 1 ); - } - } - - if ( *iter == mContent && !mButtons.empty() ) - { - nextFocusableActor = mButtons[0]; - } - break; + nextFocusableActor = *( currentIterator + 1 ); } + break; } - if(!nextFocusableActor) + case Toolkit::Control::KeyboardFocus::UP: { - DALI_LOG_WARNING("Can not decide next focusable actor\n"); + nextFocusableActor = *( focusableActors.begin() ); + break; } - break; + case Toolkit::Control::KeyboardFocus::DOWN: + { + nextFocusableActor = *( endIterator - 1 ); + break; + } + } + + if( !nextFocusableActor ) + { + DALI_LOG_WARNING( "Can not decide next focusable actor\n" ); } } } @@ -1024,6 +1894,7 @@ Actor Popup::GetNextKeyboardFocusableActor(Actor currentFocusedActor, Toolkit::C return nextFocusableActor; } + } // namespace Internal } // namespace Toolkit