mActiveGrabHandle( false ),
mActiveSelection( false ),
mActiveCopyPastePopup( false ),
- mCursorBlinkStatus( true )
+ mCursorBlinkStatus( true ),
+ mPrimaryCursorVisible( false ),
+ mSecondaryCursorVisible( false )
{
}
CreateCursors();
if( mPrimaryCursor )
{
- mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].position.x,
- mCursor[PRIMARY_CURSOR].position.y );
- mPrimaryCursor.SetSize( Size( 1.0f, mCursor[PRIMARY_CURSOR].cursorHeight ) );
+ mPrimaryCursorVisible = ( mCursor[PRIMARY_CURSOR].position.x <= size.width ) && ( mCursor[PRIMARY_CURSOR].position.x >= 0.f );
+ if( mPrimaryCursorVisible )
+ {
+ mPrimaryCursor.SetPosition( mCursor[PRIMARY_CURSOR].position.x,
+ mCursor[PRIMARY_CURSOR].position.y );
+ mPrimaryCursor.SetSize( Size( 1.0f, mCursor[PRIMARY_CURSOR].cursorHeight ) );
+ }
+ mPrimaryCursor.SetVisible( mPrimaryCursorVisible );
}
if( mSecondaryCursor )
{
- mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].position.x,
- mCursor[SECONDARY_CURSOR].position.y );
- mSecondaryCursor.SetSize( Size( 1.0f, mCursor[SECONDARY_CURSOR].cursorHeight ) );
+ mSecondaryCursorVisible = ( mCursor[SECONDARY_CURSOR].position.x <= size.width ) && ( mCursor[SECONDARY_CURSOR].position.x >= 0.f );
+ if( mSecondaryCursorVisible )
+ {
+ mSecondaryCursor.SetPosition( mCursor[SECONDARY_CURSOR].position.x,
+ mCursor[SECONDARY_CURSOR].position.y );
+ mSecondaryCursor.SetSize( Size( 1.0f, mCursor[SECONDARY_CURSOR].cursorHeight ) );
+ }
+ mSecondaryCursor.SetVisible( mSecondaryCursorVisible );
}
// Show or hide the grab handle
if( mActiveGrabHandle )
{
- SetupTouchEvents();
+ const bool isVisible = ( mCursor[PRIMARY_CURSOR].position.x <= size.width ) && ( mCursor[PRIMARY_CURSOR].position.x >= 0.f );
+
+ if( isVisible )
+ {
+ SetupTouchEvents();
- CreateGrabHandle();
+ CreateGrabHandle();
- mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].position.x,
- mCursor[PRIMARY_CURSOR].position.y + mCursor[PRIMARY_CURSOR].lineHeight );
+ mGrabHandle.SetPosition( mCursor[PRIMARY_CURSOR].position.x,
+ mCursor[PRIMARY_CURSOR].position.y + mCursor[PRIMARY_CURSOR].lineHeight );
+ }
+ mGrabHandle.SetVisible( isVisible );
}
else if( mGrabHandle )
{
// Cursor blinking
if ( mPrimaryCursor )
{
- mPrimaryCursor.SetVisible( mCursorBlinkStatus );
+ mPrimaryCursor.SetVisible( mPrimaryCursorVisible && mCursorBlinkStatus );
}
if ( mSecondaryCursor )
{
- mSecondaryCursor.SetVisible( mCursorBlinkStatus );
+ mSecondaryCursor.SetVisible( mSecondaryCursorVisible && mCursorBlinkStatus );
}
mCursorBlinkStatus = !mCursorBlinkStatus;
float mGrabDisplacementX;
float mGrabDisplacementY;
- bool mActiveGrabHandle:1;
- bool mActiveSelection:1;
- bool mActiveCopyPastePopup:1;
- bool mCursorBlinkStatus:1; ///< Flag to switch between blink on and blink off
+ bool mActiveGrabHandle : 1;
+ bool mActiveSelection : 1;
+ bool mActiveCopyPastePopup : 1;
+ bool mCursorBlinkStatus : 1; ///< Flag to switch between blink on and blink off.
+ bool mPrimaryCursorVisible : 1; ///< Whether the primary cursor is visible.
+ bool mSecondaryCursorVisible : 1; ///< Whether the secondary cursor is visible.
};
DecoratorPtr Decorator::New( Internal::Control& parent, Observer& observer )
void Decorator::SetPosition( Cursor cursor, float x, float y, float cursorHeight, float lineHeight )
{
// Adjust grab handle displacement
- mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].position.x;
- mImpl->mGrabDisplacementY -= y - mImpl->mCursor[cursor].position.y;
+ if( PRIMARY_CURSOR == cursor )
+ {
+ mImpl->mGrabDisplacementX -= x - mImpl->mCursor[cursor].position.x;
+ mImpl->mGrabDisplacementY -= y - mImpl->mCursor[cursor].position.y;
+ }
mImpl->mCursor[cursor].position.x = x;
mImpl->mCursor[cursor].position.y = y;
lineHeight = mImpl->mCursor[cursor].lineHeight;
}
+const Vector2& Decorator::GetPosition( Cursor cursor ) const
+{
+ return mImpl->mCursor[cursor].position;
+}
+
void Decorator::SetColor( Cursor cursor, const Dali::Vector4& color )
{
mImpl->mCursor[cursor].color = color;
void GetPosition( Cursor cursor, float& x, float& y, float& cursorHeight, float& lineHeight ) const;
/**
+ * @brief Retrieves the position of a cursor.
+ *
+ * @param[in] cursor The cursor to get.
+ *
+ * @return The position.
+ */
+ const Vector2& GetPosition( Cursor cursor ) const;
+
+ /**
* @brief Sets the color for a cursor.
*
* @param[in] cursor Whether this color is for the primary or secondary cursor.
mSelectionEnabled( true ),
mHorizontalScrollingEnabled( true ),
mVerticalScrollingEnabled( false ),
- mUpdateCursorPosition( false )
+ mUpdateCursorPosition( false ),
+ mScrollAfterUpdateCursorPosition( false )
{}
EventData::~EventData()
// The cursor must also be repositioned after inserts into the model
if( mEventData->mUpdateCursorPosition )
{
+ // Updates the cursor position and scrolls the text to make it visible.
+
UpdateCursorPosition();
+
+ if( mEventData->mScrollAfterUpdateCursorPosition )
+ {
+ ScrollToMakeCursorVisible();
+ mEventData->mScrollAfterUpdateCursorPosition = false;
+ }
+
mEventData->mUpdateCursorPosition = false;
}
// TODO
}
- UpdateCursorPosition();
+ mEventData->mUpdateCursorPosition = true;
+ mEventData->mScrollAfterUpdateCursorPosition = true;
}
void Controller::Impl::HandleCursorKey( int keyCode )
mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition,
yPosition );
- UpdateCursorPosition();
+ mEventData->mUpdateCursorPosition = true;
+ mEventData->mScrollAfterUpdateCursorPosition = true;
}
else if( mEventData->mSelectionEnabled &&
( 2u == tapCount ) )
const float displacementX = event.p2.mFloat;
mEventData->mScrollPosition.x += displacementX;
- // Clamp between -space & 0 (and the text alignment).
- if( actualSize.width > mControlSize.width )
- {
- const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
- mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
- mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
-
- mEventData->mDecoratorUpdated = true;
- }
- else
- {
- mEventData->mScrollPosition.x = 0.f;
- }
+ ClampHorizontalScroll( actualSize );
}
if( mEventData->mVerticalScrollingEnabled )
const float displacementY = event.p3.mFloat;
mEventData->mScrollPosition.y += displacementY;
- // Clamp between -space & 0 (and the text alignment).
- if( actualSize.height > mControlSize.height )
- {
- const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
- mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
- mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
-
- mEventData->mDecoratorUpdated = true;
- }
- else
- {
- mEventData->mScrollPosition.y = 0.f;
- }
+ ClampVerticalScroll( actualSize );
}
if( mEventData->mDecorator )
const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
- mEventData->mPrimaryCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
-
- UpdateCursorPosition();
-
//mDecorator->HidePopup();
ChangeState ( EventData::EDITING );
+
+ const CharacterIndex newCursorPosition = GetClosestCursorIndex( xPosition, yPosition );
+
+ if( newCursorPosition != mEventData->mPrimaryCursorPosition )
+ {
+ mEventData->mPrimaryCursorPosition = newCursorPosition;
+ mEventData->mUpdateCursorPosition = true;
+ }
}
else if( mEventData->mGrabHandlePopupEnabled &&
( GRAB_HANDLE_RELEASED == state ) )
{
//mDecorator->ShowPopup();
ChangeState ( EventData::EDITING_WITH_POPUP );
+ mEventData->mUpdateCursorPosition = true;
mEventData->mDecoratorUpdated = true;
}
}
mEventData->mDecoratorUpdated = true;
}
+void Controller::Impl::ClampHorizontalScroll( const Vector2& actualSize )
+{
+ // Clamp between -space & 0 (and the text alignment).
+ if( actualSize.width > mControlSize.width )
+ {
+ const float space = ( actualSize.width - mControlSize.width ) + mAlignmentOffset.x;
+ mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x < -space ) ? -space : mEventData->mScrollPosition.x;
+ mEventData->mScrollPosition.x = ( mEventData->mScrollPosition.x > -mAlignmentOffset.x ) ? -mAlignmentOffset.x : mEventData->mScrollPosition.x;
+
+ mEventData->mDecoratorUpdated = true;
+ }
+ else
+ {
+ mEventData->mScrollPosition.x = 0.f;
+ }
+}
+
+void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
+{
+ // Clamp between -space & 0 (and the text alignment).
+ if( actualSize.height > mControlSize.height )
+ {
+ const float space = ( actualSize.height - mControlSize.height ) + mAlignmentOffset.y;
+ mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y < -space ) ? -space : mEventData->mScrollPosition.y;
+ mEventData->mScrollPosition.y = ( mEventData->mScrollPosition.y > -mAlignmentOffset.y ) ? -mAlignmentOffset.y : mEventData->mScrollPosition.y;
+
+ mEventData->mDecoratorUpdated = true;
+ }
+ else
+ {
+ mEventData->mScrollPosition.y = 0.f;
+ }
+}
+
+void Controller::Impl::ScrollToMakeCursorVisible()
+{
+ if( NULL == mEventData )
+ {
+ // Nothing to do if there is no text input.
+ return;
+ }
+
+ const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
+
+ Vector2 offset;
+ bool updateDecorator = false;
+ if( primaryCursorPosition.x < 0.f )
+ {
+ offset.x = -primaryCursorPosition.x;
+ mEventData->mScrollPosition.x += offset.x;
+ updateDecorator = true;
+ }
+ else if( primaryCursorPosition.x > mControlSize.width )
+ {
+ offset.x = mControlSize.width - primaryCursorPosition.x;
+ mEventData->mScrollPosition.x += offset.x;
+ updateDecorator = true;
+ }
+
+ if( updateDecorator && mEventData->mDecorator )
+ {
+ mEventData->mDecorator->UpdatePositions( offset );
+ }
+
+ // TODO : calculate the Y scroll.
+}
+
void Controller::Impl::RequestRelayout()
{
mControlInterface.RequestTextRelayout();
~EventData();
- DecoratorPtr mDecorator;
- std::string mPlaceholderText;
+ DecoratorPtr mDecorator; ///< Pointer to the decorator
+ std::string mPlaceholderText; ///< The plaxe holder text
/**
* This is used to delay handling events until after the model has been updated.
* The number of updates to the model is minimized to improve performance.
*/
- std::vector<Event> mEventQueue; ///< The queue of touch events etc.
+ std::vector<Event> mEventQueue; ///< The queue of touch events etc.
/**
* 0,0 means that the top-left corner of the layout matches the top-left corner of the UI control.
* Typically this will have a negative value with scrolling occurs.
*/
- Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
-
- State mState; ///< Selection mode, edit mode etc.
-
- CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor
- CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor
-
- bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing
- bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active
- bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled
- bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown
- bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled
- bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled
- bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled
- bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated
+ Vector2 mScrollPosition; ///< The text is offset by this position when scrolling.
+
+ State mState; ///< Selection mode, edit mode etc.
+
+ CharacterIndex mPrimaryCursorPosition; ///< Index into logical model for primary cursor.
+ CharacterIndex mSecondaryCursorPosition; ///< Index into logical model for secondary cursor.
+
+ bool mDecoratorUpdated : 1; ///< True if the decorator was updated during event processing.
+ bool mCursorBlinkEnabled : 1; ///< True if cursor should blink when active.
+ bool mGrabHandleEnabled : 1; ///< True if grab handle is enabled.
+ bool mGrabHandlePopupEnabled : 1; ///< True if the grab handle popu-up should be shown.
+ bool mSelectionEnabled : 1; ///< True if selection handles, highlight etc. are enabled.
+ bool mHorizontalScrollingEnabled : 1; ///< True if horizontal scrolling is enabled.
+ bool mVerticalScrollingEnabled : 1; ///< True if vertical scrolling is enabled.
+ bool mUpdateCursorPosition : 1; ///< True if the visual position of the cursor must be recalculated.
+ bool mScrollAfterUpdateCursorPosition : 1; ///< Whether to scroll after the cursor position is updated.
};
struct ModifyEvent
*/
CharacterIndex CalculateNewCursorIndex( CharacterIndex index ) const;
+ /**
+ * @brief Updates the cursor position.
+ *
+ * Retrieves the x,y position of the cursor logical position and sets it into the decorator.
+ * It sets the position of the secondary cursor if it's a valid one.
+ * Sets which cursors are active.
+ */
void UpdateCursorPosition();
+ /**
+ * @biref Clamps the horizontal scrolling to get the control always filled with text.
+ *
+ * @param[in] actualSize The size of the laid out text.
+ */
+ void ClampHorizontalScroll( const Vector2& actualSize );
+
+ /**
+ * @biref Clamps the vertical scrolling to get the control always filled with text.
+ *
+ * @param[in] actualSize The size of the laid out text.
+ */
+ void ClampVerticalScroll( const Vector2& actualSize );
+
+ /**
+ * @brief Scrolls the text to make the cursor visible.
+ *
+ * This method is called after inserting, deleting or moving the cursor with the keypad.
+ */
+ void ScrollToMakeCursorVisible();
+
ControlInterface& mControlInterface; ///< Reference to the text controller.
LogicalModelPtr mLogicalModel; ///< Pointer to the logical model.
VisualModelPtr mVisualModel; ///< Pointer to the visual model.
// Queue a cursor reposition event; this must wait until after DoRelayout()
mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
}
void Controller::DeleteTextEvent()
// Queue a cursor reposition event; this must wait until after DoRelayout()
mImpl->mEventData->mUpdateCursorPosition = true;
+ mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
}
void Controller::UpdateModel( OperationsMask operationsRequired )