void TextInput::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
{
Relayout( mDisplayedTextView, size, container );
+ Relayout( mPopupPanel.GetRootActor(), size, container );
+
GetTextLayoutInfo();
DrawCursor();
mPopupPanel.AddPopupOptions();
}
-void TextInput::SetPopupPosition( const Vector3& position )
+void TextInput::SetPopupPosition( const Vector3& position, const Vector2& alternativePosition )
{
- mPopupPanel.SetTailPosition( position );
- mPopupPanel.GetRootActor().SetPosition( position );
+ const Vector3& visiblePopUpSize = mPopupPanel.GetVisibileSize();
+
+ Vector3 clampedPosition ( position );
+ Vector3 tailOffsetPosition ( position );
+
+ float xOffSet( 0.0f );
+
+ Actor self = Self();
+ const Vector3 textViewTopLeftWorldPosition = self.GetCurrentWorldPosition() - self.GetCurrentSize()*0.5f;
+
+ const float popUpLeft = textViewTopLeftWorldPosition.x + position.x - visiblePopUpSize.width*0.5f;
+ const float popUpTop = textViewTopLeftWorldPosition.y + position.y - visiblePopUpSize.height;
+
+ // Clamp to left or right or of boundary
+ if( popUpLeft < mBoundingRectangleWorldCoordinates.x )
+ {
+ xOffSet = mBoundingRectangleWorldCoordinates.x - popUpLeft ;
+ }
+ else if ( popUpLeft + visiblePopUpSize.width > mBoundingRectangleWorldCoordinates.z )
+ {
+ xOffSet = mBoundingRectangleWorldCoordinates.z - ( popUpLeft + visiblePopUpSize.width );
+ }
+
+ clampedPosition.x = position.x + xOffSet;
+ tailOffsetPosition.x = -xOffSet;
+
+ // Check if top left of PopUp outside of top bounding rectangle, if so then flip to lower position.
+ bool flipTail( false );
+
+ if ( popUpTop < mBoundingRectangleWorldCoordinates.y )
+ {
+ clampedPosition.y = alternativePosition.y + visiblePopUpSize.height;
+ flipTail = true;
+ }
+
+ mPopupPanel.GetRootActor().SetPosition( clampedPosition );
+ mPopupPanel.SetTailPosition( tailOffsetPosition, flipTail );
}
void TextInput::HidePopup(bool animate, bool signalFinished )
}
}
-void TextInput::ShowPopup(bool animate)
+void TextInput::ShowPopup( bool animate )
{
Vector3 position;
+ Vector2 alternativePopupPosition;
if(mHighlightMeshActor && mState == StateEdit)
{
topHandle.y += -mPopupOffsetFromText.y - rowSize.height;
position = Vector3(topHandle.x, topHandle.y, 0.0f);
- bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
- mPopupPanel.SetAlternativeOffset(Vector2( mBoundingRectangleWorldCoordinates.x, bottomHandle.y - topHandle.y));
-
- float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( topHandle.x , bottomHandle.x );
+ float xPosition = ( fabsf( topHandle.x - bottomHandle.x ) )*0.5f + std::min( mSelectionHandleOneActualPosition.x , mSelectionHandleTwoActualPosition.x );
position.x = xPosition;
+
+ // Alternative position if no upper space
+ bottomHandle.y += GetSelectionHandleSize().y + mPopupOffsetFromText.w;
+ alternativePopupPosition = Vector2 ( position.x, bottomHandle.y );
}
else
{
const Size rowSize = GetRowRectFromCharacterPosition( mCursorPosition );
position.y -= ( mPopupOffsetFromText.y + rowSize.height );
// if can't be positioned above, then position below row.
- Vector2 alternativePopupPosition( mBoundingRectangleWorldCoordinates.x, position.y ); // default if no grab handle
+ alternativePopupPosition = Vector2( position.x, position.y ); // default if no grab handle
if ( mGrabHandle )
{
// If grab handle enabled then position pop-up below the grab handle.
alternativePopupPosition.y = rowSize.height + mGrabHandle.GetCurrentSize().height + mPopupOffsetFromText.w +50.0f;
}
- mPopupPanel.SetAlternativeOffset( alternativePopupPosition );
}
- // reposition popup above the desired cursor posiiton.
- Vector3 textViewSize = mDisplayedTextView.GetCurrentSize();
- textViewSize.z = 0.0f;
- // World position = world position of local position i.e. top-left corner of TextView
- Vector3 worldPosition = mDisplayedTextView.GetCurrentWorldPosition() - ( textViewSize * 0.5f ) + position;
-
- SetPopupPosition( worldPosition );
+ SetPopupPosition( position, alternativePopupPosition );
// Show popup
- mPopupPanel.Show(animate);
+ mPopupPanel.Show( Self(), animate );
StartMonitoringStageForTouch();
mPopupPanel.PressedSignal().Connect( this, &TextInput::OnPopupButtonPressed );
{
const Vector2 DEFAULT_POPUP_INDICATOR_OFFSET(0.0f, 60.0f);
-
-// TODO: This should be based on the content for example:
// 1. For selection: should be above top of highlighted selection, or below bottom of highlighted selection + end handle.
// 2. For cursor: should be above top of cursor, or below bottom of cursor + grab handle.
-const std::string POPUP_ALTERNATIVE_OFFSET("popup-alternative-offset"); ///< Alternative offset property for confinenment constraint.
-const std::string POPUP_REQUESTED_POSITION("popup-requested-position"); ///< Position the Popup was requested to be, for confinenment constraint.
/**
* Image resource paths
const std::string POPUP_BACKGROUND( DALI_IMAGE_DIR "popup_bubble_bg.#.png" );
const std::string POPUP_BACKGROUND_EFFECT( DALI_IMAGE_DIR "popup_bubble_bg_ef.#.png" );
const std::string POPUP_BACKGROUND_LINE( DALI_IMAGE_DIR "popup_bubble_bg_line.#.png" );
+
const std::string POPUP_TAIL_BOTTOM( DALI_IMAGE_DIR "popup_bubble_tail_bottom.png" );
const std::string POPUP_TAIL_BOTTOM_EFFECT( DALI_IMAGE_DIR "popup_bubble_tail_bottom_ef.png" );
const std::string POPUP_TAIL_BOTTOM_LINE( DALI_IMAGE_DIR "popup_bubble_tail_bottom_line.png" );
+const std::string POPUP_TAIL_TOP( DALI_IMAGE_DIR "popup_bubble_tail_top.png" );
+const std::string POPUP_TAIL_TOP_EFFECT( DALI_IMAGE_DIR "popup_bubble_tail_top_ef.png" );
+const std::string POPUP_TAIL_TOP_LINE( DALI_IMAGE_DIR "popup_bubble_tail_top_line.png" );
+
const std::string OPTION_ICON_CLIPBOARD( DALI_IMAGE_DIR "copy_paste_icon_clipboard.png" );
const std::string OPTION_ICON_COPY( DALI_IMAGE_DIR "copy_paste_icon_copy.png" );
const std::string OPTION_ICON_CUT( DALI_IMAGE_DIR "copy_paste_icon_cut.png" );
const Vector4 DEFAULT_OPTION_TEXT( Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
const Vector4 DEFAULT_OPTION_TEXT_PRESSED( Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
-
-/**
- * Confine Actor to boundaries of reference actor (e.g. Parent)
- * Actor bounds (top-left position + size) are confined to reference Actor's
- * bounds.
- */
-struct ConfinementConstraint
-{
- /**
- * Confinement constraint constructor.
- * @param[in] topLeftMargin (optional) Top-Left margins (defaults to 0.0f, 0.0f)
- * @param[in] bottomRightMargin (optional) Bottom-Right margins (defaults to 0.0f, 0.0f)
- * @paran[in[ flipHorizontal (optional) whether to flip Actor to other side if near edge
- * @param[in] flipVertical (optional) whether to flip Actor to the other side if near edge
- * @param[in] boundingRect Rectangle to bound Popup to.
- *
- */
- ConfinementConstraint(Vector2 topLeftMargin = Vector2::ZERO, Vector2 bottomRightMargin = Vector2::ZERO, bool flipHorizontal = false, bool flipVertical = false, Rect<float> boundingRect = Rect<float>(0.0f, 0.0f, 0.0f, 0.0f) )
- : mMinIndent(topLeftMargin),
- mMaxIndent(bottomRightMargin),
- mFlipHorizontal(flipHorizontal),
- mFlipVertical(flipVertical),
- mBoundingRect( boundingRect )
- {
- }
-
- Vector3 operator()(const Vector3& constPosition,
- const PropertyInput& sizeProperty,
- const PropertyInput& parentOriginProperty,
- const PropertyInput& anchorPointProperty,
- const PropertyInput& referenceSizeProperty,
- const PropertyInput& alternativeOffsetProperty,
- const PropertyInput& requestedPositionProperty )
- {
- const Vector3& size = sizeProperty.GetVector3();
- const Vector3& origin = parentOriginProperty.GetVector3();
- const Vector3& anchor = anchorPointProperty.GetVector3();
- const Vector3& referenceSize = referenceSizeProperty.GetVector3();
- const Vector2& alternativeOffset = alternativeOffsetProperty.GetVector2();
- const Vector3& requestedPosition = requestedPositionProperty.GetVector3();
-
- Vector3 newPosition( requestedPosition );
-
- // Get actual position of Actor relative to parent's Top-Left.
- Vector3 position(constPosition + origin * referenceSize);
-
- // if top-left corner is outside of Top-Left bounds, then push back in screen.
-
- Vector3 corner(position - size * anchor - mMinIndent);
-
- if ( mFlipHorizontal )
- {
- if( corner.x < mBoundingRect.x )
- {
- // Snap Popup to left hand boundary so stays visible
- corner.x = mBoundingRect.x - ( origin.x * referenceSize.x ) + ( size.x * anchor.x );
- newPosition.x = corner.x;
- }
- else if ( ( corner.x + size.x ) > ( mBoundingRect.x + mBoundingRect.width ))
- {
- // Calculate offset from left boundary Popup must be placed at so it does not exceed right side boundary.
- float requiredOffSetFromLeftBoundaryToFit = mBoundingRect.width - size.x;
- corner.x = mBoundingRect.x + requiredOffSetFromLeftBoundaryToFit - ( origin.x * referenceSize.x ) + ( size.x * anchor.x );
- newPosition.x = corner.x;
- }
- }
-
- if(mFlipVertical && corner.y < 0.0f)
- {
- corner.y = 0.0f;
- newPosition.y += size.height + alternativeOffset.height;
- }
-
- newPosition.y -= std::min(corner.y, 0.0f);
-
- // if bottom-right corner is outside of Bottom-Right bounds, then push back in screen.
- corner += size - referenceSize + mMinIndent + mMaxIndent;
-
- if(mFlipVertical && corner.y > 0.0f)
- {
- corner.y = 0.0f;
- newPosition.y -= size.height + alternativeOffset.height;
- }
-
- return newPosition;
- }
-
- Vector3 mMinIndent; ///< Top-Left Margin
- Vector3 mMaxIndent; ///< Bottom-Right Margin.
- bool mFlipHorizontal; ///< Whether to flip actor's position if exceeds horizontal screen bounds
- bool mFlipVertical; ///< Whether to flip actor's position if exceeds vertical screen bounds
- Rect<float> mBoundingRect; ///< Bounding Rect Popup must stay within
-};
-
-/**
- * Confine actor to the x axis boundaries of reference actor (e.g. Parent)
- */
-struct ParentXAxisConstraint
-{
- /**
- * Confinement constraint constructor.
- */
- ParentXAxisConstraint( float handlesMidPoint = 0.0f )
- : mHandlesMidPoint( handlesMidPoint )
- {
- }
-
- float operator()( const float constXPosition,
- const PropertyInput& localSizeWidthProperty,
- const PropertyInput& parentWidthProperty,
- const PropertyInput& anchorPointXProperty )
- {
- const float localSizeProperty = localSizeWidthProperty.GetFloat();
- const float size = parentWidthProperty.GetFloat();
-
- float newPosition = std::max( mHandlesMidPoint, -size/2 + localSizeProperty );
- newPosition = std::min( newPosition, size/2 - localSizeProperty );
- return newPosition;
- }
-
- float mHandlesMidPoint;
-};
-
-
} // unnamed namespace
namespace Dali
TextInputPopup::TextInputPopup()
: mState(StateHidden),
mRoot(Layer::New()),
+ mVisiblePopUpSize(),
mPopupTailXPosition( 0.0f ),
mContentSize( POPUP_MIN_SIZE ),
mBackgroundColor( DEFAULT_POPUP_BACKGROUND ),
mHideFinishedSignal(),
mShowFinishedSignal()
{
- mAlternativeOffsetProperty = mRoot.RegisterProperty( POPUP_ALTERNATIVE_OFFSET, Vector2::ZERO );
- mRequestionPositionProperty = mRoot.RegisterProperty( POPUP_REQUESTED_POSITION, Vector3::ZERO );
- mRoot.SetParentOrigin( ParentOrigin::CENTER );
+ mRoot.SetParentOrigin( ParentOrigin::TOP_LEFT );
mRoot.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
- // constrain popup to size of parent.
}
-void TextInputPopup::AddToStage()
+void TextInputPopup::AddToParent( Actor parent )
{
- // TODO: Confinement constraint borders should be defined by the application.
- // It should also not use the stage directly, instead it should add to parent container.
- Stage::GetCurrent().Add(mRoot);
+ Actor existingParent = mRoot.GetParent();
- ApplyConfinementConstraint();
+ if ( !existingParent )
+ {
+ parent.Add( mRoot );
+ }
}
-void TextInputPopup::ApplyConfinementConstraint()
+void TextInputPopup::RemoveFromParent()
{
- mRoot.RemoveConstraints();
- Constraint constraint = Constraint::New<Vector3>( Actor::POSITION,
- LocalSource( Actor::SIZE ),
- LocalSource( Actor::PARENT_ORIGIN ),
- LocalSource( Actor::ANCHOR_POINT ),
- ParentSource( Actor::SIZE ),
- LocalSource( mAlternativeOffsetProperty ),
- LocalSource( mRequestionPositionProperty),
- ConfinementConstraint( DEFAULT_POPUP_INDICATOR_OFFSET,
- Vector2::ZERO,
- true,
- true, mBoundingRect ) );
- mRoot.ApplyConstraint(constraint);
-}
+ Actor parent = mRoot.GetParent();
-void TextInputPopup::ApplyTailConstraint()
-{
- mTail.RemoveConstraints();
- Constraint constraint = Constraint::New<float>( Actor::POSITION_X,
- LocalSource( Actor::SIZE_WIDTH ),
- ParentSource( Actor::SIZE_WIDTH ),
- LocalSource( Actor::PARENT_ORIGIN_X ),
- ParentXAxisConstraint( mPopupTailXPosition ));
- mTail.ApplyConstraint( constraint );
+ if ( parent )
+ {
+ parent.Remove( mRoot );
+ }
}
-void TextInputPopup::CreateStencil( const Vector2& size)
+void TextInputPopup::CreateStencil( const Vector2& size )
{
mStencil = CreateSolidColorActor( Color::BLUE );
mStencil.SetParentOrigin( ParentOrigin::CENTER );
mScrollView.ScrollCompletedSignal().Connect( this, &TextInputPopup::OnScrollCompleted );
}
-void TextInputPopup::UpdateScrollViewProperty( const Vector2& visibleSize )
+void TextInputPopup::UpdateScrollViewRulerAndSize( const Vector2& visibleSize )
{
mScrollView.SetSize( visibleSize.x, visibleSize.y );
mScrollView.SetRulerY(rulerY);
}
-void TextInputPopup::RemoveFromStage()
-{
- Stage::GetCurrent().Remove( mRoot );
-}
void TextInputPopup::Clear()
{
UnparentAndReset( mScrollView );
mButtonContainer.clear();
mDividerContainer.clear();
-
- RemoveFromStage();
mRoot.RemoveConstraints();
-
+ RemoveFromParent();
mState = StateHidden;
}
}
mBackgroundEffect.Add( mBackgroundLine );
Hide(false);
- AddToStage();
+ GetRootActor().Add( mBackground );
}
}
ImageActor TextInputPopup::CreateDivider()
{
- ImageActor divider = Toolkit::CreateSolidColorActor( mLineColor );
- divider.SetParentOrigin( ParentOrigin::TOP_LEFT );
- divider.SetAnchorPoint( AnchorPoint::TOP_LEFT );
- divider.SetSize( POPUP_DIVIDER_SIZE );
- divider.SetPosition( mContentSize.width - POPUP_DIVIDER_SIZE.width, 0.0f );
+ ImageActor divider = Toolkit::CreateSolidColorActor( mLineColor );
+ divider.SetParentOrigin( ParentOrigin::TOP_LEFT );
+ divider.SetAnchorPoint( AnchorPoint::TOP_LEFT );
+ divider.SetSize( POPUP_DIVIDER_SIZE );
+ divider.SetPosition( mContentSize.width - POPUP_DIVIDER_SIZE.width, 0.0f );
- // Keep track of all the dividers. As their height's need to be updated to the max. of all
- // buttons currently added.
- mDividerContainer.push_back( divider );
+ // Keep track of all the dividers. As their height's need to be updated to the max of all
+ // buttons currently added.
+ mDividerContainer.push_back( divider );
- return divider;
+ return divider;
}
ImageActor TextInputPopup::CreatePressedBackground( const Vector2& requiredSize )
}
}
-void TextInputPopup::Show(bool animate)
+void TextInputPopup::Show( Actor target, bool animate )
{
if( mRoot )
{
mAnimation.Reset();
}
+ if ( target )
+ {
+ AddToParent( target );
+ }
+
if(animate)
{
mAnimation = Animation::New( SHOW_POPUP_ANIMATION_DURATION );
}
}
-void TextInputPopup::SetAlternativeOffset(Vector2 offset)
-{
- mRoot.SetProperty( mAlternativeOffsetProperty, offset );
- ApplyConfinementConstraint();
-}
-
TextInputPopup::State TextInputPopup::GetState(void) const
{
return mState;
}
}
- // 5. Calcurate a lot of size to make the layout.
+ // 5. Calculate size of content and of popup including borders
const Vector2 visibleContentSize = Vector2( std::min( mContentSize.x, POPUP_MAX_SIZE.x - POPUP_BORDER.x - POPUP_BORDER.y ), mContentSize.y );
const Vector2 popupSize = Vector2( POPUP_BORDER.x + visibleContentSize.x + POPUP_BORDER.y,
POPUP_BORDER.z + visibleContentSize.y + POPUP_BORDER.w );
+ mVisiblePopUpSize = Vector3( popupSize.x, popupSize.y, 1.0f);
- // 6. Set the scroll view ruller.
- UpdateScrollViewProperty( visibleContentSize );
+ // 6. Set the scroll view ruler.
+ UpdateScrollViewRulerAndSize( visibleContentSize );
- // 8. Create stencil
+ // 7. Create stencil
const Vector2 stencilSize = Vector2( popupSize.x, popupSize.y + POPUP_TAIL_SIZE.x + POPUP_TAIL_Y_OFFSET );
CreateStencil( stencilSize );
mRoot.Add( mStencil );
- // 7. Set the root size.
+ // 8. Set the root size.
mRoot.SetSize( popupSize ); // Make Root Actor reflect the size of its content
-
}
void TextInputPopup::SetPopupBoundary( const Rect<float>& boundingRectangle )
mBoundingRect = boundingRectangle;
}
-void TextInputPopup::SetTailPosition( const Vector3& position )
+const Vector3& TextInputPopup::GetVisibileSize() const
+{
+ return mVisiblePopUpSize;
+}
+
+
+void TextInputPopup::SetTailPosition( const Vector3& position, bool yAxisFlip )
{
- mRoot.SetProperty( mRequestionPositionProperty, position );
mPopupTailXPosition = position.x;
- ApplyConfinementConstraint();
- ApplyTailConstraint();
+ mTail.SetX( position.x );
+
+ if ( yAxisFlip )
+ {
+ Image tail = Image::New( POPUP_TAIL_TOP );
+ mTail.SetImage( tail );
+ mTail.SetParentOrigin( ParentOrigin::TOP_CENTER );
+ mTail.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER );
+ mTail.SetY( POPUP_BORDER.y - POPUP_TAIL_Y_OFFSET );
+ Image tailEffect = Image::New( POPUP_TAIL_TOP_EFFECT );
+ mTailEffect.SetImage( tailEffect );
+
+ Image tailLine = Image::New( POPUP_TAIL_TOP_LINE );
+ mTailLine.SetImage( tailLine );
+ }
}
bool TextInputPopup::OnButtonPressed( Toolkit::Button button )
/**
* Shows the popup
* @param[in] animate (optional) whether to animate popup to show state over time (i.e. tween).
+ * @param[in] target Actor to parent popup.
*/
- void Show(bool animate = true);
+ void Show( Actor target, bool animate = true );
/**
* Sets Alternative offset property.
void SetPopupBoundary( const Rect<float>& boundingRectangle );
/**
- * Sets the positon of the Popup tail relative to TextInput
- * @param position Position to set
+ * Get Visible size of the Popup, excludes content that needs scrolling
+ * @return Vector3 size of Popup
*/
- void SetTailPosition( const Vector3& position );
+ const Vector3& GetVisibileSize() const;
+
+ /**
+ * Sets the positon of the PopUp tail relative to TextInput
+ * @param[in] position Position to set
+ * @param[in] yAxisFlip If tail should be flipped in y axis
+ */
+ void SetTailPosition( const Vector3& position, const bool yAxisFlip );
private:
const std::string& name, const std::string& caption, Image iconImage, bool enabled );
/**
- * Adds Popup to the stage (ideally on a separate top-most layer and as an overlay)
+ * @brief Adds popup to the given parent
+ * @paran[in] parent target to add Popup to
*/
- void AddToStage();
+ void AddToParent( Actor parent );
+
+ /**
+ * @brief Removes Popup from Parent
+ */
+ void RemoveFromParent();
/**
* Applies constraint to keep Popup in view within the desired area.
* Set the scroll view size and ruler.
* @param[in] visibleSize size of the visible scroll view
*/
- void UpdateScrollViewProperty( const Vector2& visibleSize );
-
- /**
- * Removes Popup from the stage.
- */
- void RemoveFromStage();
+ void UpdateScrollViewRulerAndSize( const Vector2& visibleSize );
/**
* Called when a button is pressed in the Popup
State mState; ///< Popup State.
Layer mRoot; ///< The actor which all popup content is added to (i.e. panel and buttons)
- Property::Index mAlternativeOffsetProperty; ///< Property [Vector3] how much to offset the popup if it goes out of the screen
- Property::Index mRequestionPositionProperty; ///< Prperty [Vector3] Requested position to place popup
ImageActor mBackground; ///< The background popup panel
ImageActor mBackgroundEffect; ///< The background effect
ImageActor mBackgroundLine; ///< The background line
ImageActor mTailEffect; ///< the tail effect
ImageActor mTailLine; ///< The border/outline around the tail
- float mPopupTailXPosition; ///< X position of Popup tail.
+ Vector3 mVisiblePopUpSize; ///< Visible Size of Popup excluding content that needs scrolling.
+ float mPopupTailXPosition; ///< X position of PopUp tail.
Vector2 mContentSize; ///< Size of Content (i.e. Buttons)
ActorContainer mButtonContainer; ///< List of buttons added to popup.