From: Paul Wisbey
Date: Wed, 10 Jun 2015 14:24:11 +0000 (+0100)
Subject: Selection implementation.
X-Git-Tag: dali_1.0.46~20
X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F88%2F40988%2F8;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git
Selection implementation.
Change-Id: I06668e73f1cf1fba9c2dbf100b23a3668511fae0
---
diff --git a/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
index 6f868e6..305d61c 100644
--- a/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
+++ b/dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
@@ -960,6 +960,24 @@ void TextField::OnRelayout( const Vector2& size, RelayoutContainer& container )
Self().Add( mRenderableActor );
}
}
+
+ for( std::vector::const_iterator it = mClippingDecorationActors.begin(),
+ endIt = mClippingDecorationActors.end();
+ it != endIt;
+ ++it )
+ {
+ Actor actor = *it;
+
+ if( mClipper )
+ {
+ mClipper->GetRootActor().Add( actor );
+ }
+ else
+ {
+ Self().Add( actor );
+ }
+ }
+ mClippingDecorationActors.clear();
}
}
@@ -1045,11 +1063,18 @@ bool TextField::OnKeyEvent( const KeyEvent& event )
return mController->KeyEvent( event );
}
-void TextField::AddDecoration( Actor& actor )
+void TextField::AddDecoration( Actor& actor, bool needsClipping )
{
if( actor )
{
- Self().Add( actor );
+ if( needsClipping )
+ {
+ mClippingDecorationActors.push_back( actor );
+ }
+ else
+ {
+ Self().Add( actor );
+ }
}
}
diff --git a/dali-toolkit/internal/controls/text-controls/text-field-impl.h b/dali-toolkit/internal/controls/text-controls/text-field-impl.h
index 0e53053..876972d 100644
--- a/dali-toolkit/internal/controls/text-controls/text-field-impl.h
+++ b/dali-toolkit/internal/controls/text-controls/text-field-impl.h
@@ -144,7 +144,7 @@ private: // From Control
/**
* @copydoc Text::ControlInterface::AddDecoration()
*/
- virtual void AddDecoration( Actor& actor );
+ virtual void AddDecoration( Actor& actor, bool needsClipping );
/**
* @copydoc Text::ControlInterface::RequestTextRelayout()
@@ -215,6 +215,7 @@ private: // Data
Text::RendererPtr mRenderer;
Text::DecoratorPtr mDecorator;
Text::ClipperPtr mClipper; ///< For EXCEED_POLICY_CLIP
+ std::vector mClippingDecorationActors; ///< Decoration actors which need clipping.
RenderableActor mRenderableActor;
diff --git a/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp b/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
index de713ac..77da8f9 100644
--- a/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
+++ b/dali-toolkit/internal/controls/text-controls/text-label-impl.cpp
@@ -503,7 +503,7 @@ void TextLabel::OnRelayout( const Vector2& size, RelayoutContainer& container )
}
}
-void TextLabel::AddDecoration( Actor& actor )
+void TextLabel::AddDecoration( Actor& actor, bool needsClipping )
{
// TextLabel does not show decorations
}
diff --git a/dali-toolkit/internal/controls/text-controls/text-label-impl.h b/dali-toolkit/internal/controls/text-controls/text-label-impl.h
index 12dc56e..e45797a 100644
--- a/dali-toolkit/internal/controls/text-controls/text-label-impl.h
+++ b/dali-toolkit/internal/controls/text-controls/text-label-impl.h
@@ -95,7 +95,7 @@ private: // From Control
/**
* @copydoc Text::ControlInterface::AddDecoration()
*/
- virtual void AddDecoration( Actor& actor );
+ virtual void AddDecoration( Actor& actor, bool needsClipping );
/**
* @copydoc Text::ControlInterface::RequestTextRelayout()
diff --git a/dali-toolkit/internal/text/decorator/text-decorator.cpp b/dali-toolkit/internal/text/decorator/text-decorator.cpp
index 2961c52..b2fcda0 100644
--- a/dali-toolkit/internal/text/decorator/text-decorator.cpp
+++ b/dali-toolkit/internal/text/decorator/text-decorator.cpp
@@ -81,7 +81,7 @@ const char* DEFAULT_SELECTION_HANDLE_TWO( DALI_IMAGE_DIR "text-input-selection-h
const Dali::Vector3 DEFAULT_GRAB_HANDLE_RELATIVE_SIZE( 1.5f, 2.0f, 1.0f );
const Dali::Vector3 DEFAULT_SELECTION_HANDLE_RELATIVE_SIZE( 1.5f, 1.5f, 1.0f );
-const Dali::Vector4 LIGHT_BLUE( 0.07f, 0.41f, 0.59f, 1.0f ); // The text highlight color.
+const Dali::Vector4 LIGHT_BLUE( (0xb2 / 255.0f), (0xeb / 255.0f), (0xf2 / 255.0f), 0.5f ); // The text highlight color.
const unsigned int CURSOR_BLINK_INTERVAL = 500u; // Cursor blink interval
const float TO_MILLISECONDS = 1000.f;
@@ -224,7 +224,8 @@ struct Decorator::Impl : public ConnectionTracker
mActiveCopyPastePopup( false ),
mCursorBlinkStatus( true ),
mPrimaryCursorVisible( false ),
- mSecondaryCursorVisible( false )
+ mSecondaryCursorVisible( false ),
+ mSwapSelectionHandles( false )
{
}
@@ -318,15 +319,65 @@ struct Decorator::Impl : public ConnectionTracker
HandleImpl& secondary = mHandle[ RIGHT_SELECTION_HANDLE ];
if( primary.active || secondary.active )
{
- SetupTouchEvents();
+ Vector2 primaryPosition = primary.position;
+ Vector2 secondaryPosition = secondary.position;
- CreateSelectionHandles();
+ if( LEFT_SELECTION_HANDLE == mHandleScrolling )
+ {
+ if( mScrollDirection == SCROLL_RIGHT )
+ {
+ primaryPosition.x = 0.f;
+ }
+ else
+ {
+ primaryPosition.x = size.width;
+ }
+ }
+ else if( RIGHT_SELECTION_HANDLE == mHandleScrolling )
+ {
+ if( mScrollDirection == SCROLL_RIGHT )
+ {
+ secondaryPosition.x = 0.f;
+ }
+ else
+ {
+ secondaryPosition.x = size.width;
+ }
+ }
+
+ const bool isPrimaryVisible = ( primaryPosition.x <= size.width ) && ( primaryPosition.x >= 0.f );
+ const bool isSecondaryVisible = ( secondaryPosition.x <= size.width ) && ( secondaryPosition.x >= 0.f );
+
+ if( isPrimaryVisible || isSecondaryVisible )
+ {
+ SetupTouchEvents();
+
+ CreateSelectionHandles();
+
+ if( isPrimaryVisible )
+ {
+ primary.actor.SetPosition( primaryPosition.x,
+ primaryPosition.y + primary.lineHeight );
+
+ const bool flip = mSwapSelectionHandles ^ primary.flipped;
+ primary.actor.SetImage( flip ? mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] : mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
- primary.actor.SetPosition( primary.position.x,
- primary.position.y + primary.lineHeight );
+ primary.actor.SetAnchorPoint( flip ? AnchorPoint::TOP_LEFT : AnchorPoint::TOP_RIGHT );
+ }
+
+ if( isSecondaryVisible )
+ {
+ secondary.actor.SetPosition( secondaryPosition.x,
+ secondaryPosition.y + secondary.lineHeight );
- secondary.actor.SetPosition( secondary.position.x,
- secondary.position.y + secondary.lineHeight );
+ const bool flip = mSwapSelectionHandles ^ secondary.flipped;
+
+ secondary.actor.SetImage( ( mSwapSelectionHandles ^ secondary.flipped ) ? mHandleImages[LEFT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] : mHandleImages[RIGHT_SELECTION_HANDLE][HANDLE_IMAGE_RELEASED] );
+ secondary.actor.SetAnchorPoint( flip ? AnchorPoint::TOP_RIGHT : AnchorPoint::TOP_LEFT );
+ }
+ }
+ primary.actor.SetVisible( isPrimaryVisible );
+ secondary.actor.SetVisible( isSecondaryVisible );
CreateHighlight();
UpdateHighlight();
@@ -367,8 +418,7 @@ struct Decorator::Impl : public ConnectionTracker
mHandle[ GRAB_HANDLE ].position += scrollOffset;
mHandle[ LEFT_SELECTION_HANDLE ].position += scrollOffset;
mHandle[ RIGHT_SELECTION_HANDLE ].position += scrollOffset;
-
- // TODO Highlight box??
+ mHighlightPosition += scrollOffset;
}
void PopUpRelayoutComplete( Actor actor )
@@ -482,7 +532,8 @@ struct Decorator::Impl : public ConnectionTracker
mActiveLayer.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
mActiveLayer.SetPositionInheritanceMode( USE_PARENT_POSITION );
- mController.AddDecoration( mActiveLayer );
+ // Add the active layer telling the controller it doesn't need clipping.
+ mController.AddDecoration( mActiveLayer, false );
}
mActiveLayer.RaiseToTop();
@@ -620,10 +671,12 @@ struct Decorator::Impl : public ConnectionTracker
mHighlightMeshActor.SetName( "HighlightMeshActor" );
#endif
mHighlightMeshActor.SetAnchorPoint( AnchorPoint::TOP_LEFT );
- mHighlightMeshActor.SetPosition( 0.0f, 0.0f, DISPLAYED_HIGHLIGHT_Z_OFFSET );
- mController.AddDecoration( mHighlightMeshActor );
+ // Add the highlight box telling the controller it needs clipping.
+ mController.AddDecoration( mHighlightMeshActor, true );
}
+
+ mHighlightMeshActor.SetPosition( mHighlightPosition.x, mHighlightPosition.y, DISPLAYED_HIGHLIGHT_Z_OFFSET );
}
void UpdateHighlight()
@@ -992,9 +1045,9 @@ struct Decorator::Impl : public ConnectionTracker
if( HANDLE_TYPE_COUNT != mHandleScrolling )
{
mController.DecorationEvent( mHandleScrolling,
- HANDLE_SCROLLING,
- mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
- 0.f );
+ HANDLE_SCROLLING,
+ mScrollDirection == SCROLL_RIGHT ? mScrollDistance : -mScrollDistance,
+ 0.f );
}
return true;
@@ -1022,6 +1075,7 @@ struct Decorator::Impl : public ConnectionTracker
CursorImpl mCursor[CURSOR_COUNT];
HandleImpl mHandle[HANDLE_TYPE_COUNT];
QuadContainer mHighlightQuadList; ///< Sub-selections that combine to create the complete selection highlight
+ Vector2 mHighlightPosition; ///< The position of the highlight actor.
Rect mBoundingBox;
Vector4 mHighlightColor; ///< Color of the highlight
@@ -1040,6 +1094,7 @@ struct Decorator::Impl : public ConnectionTracker
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.
+ bool mSwapSelectionHandles : 1; ///< Whether to swap the selection handle images.
};
DecoratorPtr Decorator::New( ControllerInterface& controller )
@@ -1196,6 +1251,16 @@ void Decorator::GetPosition( HandleType handleType, float& x, float& y, float& h
height = handle.lineHeight;
}
+const Vector2& Decorator::GetPosition( HandleType handleType ) const
+{
+ return mImpl->mHandle[handleType].position;
+}
+
+void Decorator::SwapSelectionHandlesEnabled( bool enable )
+{
+ mImpl->mSwapSelectionHandles = enable;
+}
+
void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
{
mImpl->mHighlightQuadList.push_back( QuadCoordinates(x1, y1, x2, y2) );
@@ -1204,6 +1269,7 @@ void Decorator::AddHighlight( float x1, float y1, float x2, float y2 )
void Decorator::ClearHighlights()
{
mImpl->mHighlightQuadList.clear();
+ mImpl->mHighlightPosition = Vector2::ZERO;
}
void Decorator::SetHighlightColor( const Vector4& color )
diff --git a/dali-toolkit/internal/text/decorator/text-decorator.h b/dali-toolkit/internal/text/decorator/text-decorator.h
index b271b7b..873bdcc 100644
--- a/dali-toolkit/internal/text/decorator/text-decorator.h
+++ b/dali-toolkit/internal/text/decorator/text-decorator.h
@@ -132,7 +132,7 @@ public:
*
* @param[in] decoration The actor displaying a decoration.
*/
- virtual void AddDecoration( Actor& actor ) = 0;
+ virtual void AddDecoration( Actor& actor, bool needsClipping ) = 0;
/**
* @brief An input event from one of the handles.
@@ -355,6 +355,23 @@ public:
void GetPosition( HandleType handleType, float& x, float& y, float& lineHeight ) const;
/**
+ * @brief Retrieves the position of a selection handle.
+ *
+ * @param[in] handleType The handle to get.
+ *
+ * @return The position of the selection handle relative to the top-left of the parent control.
+ */
+ const Vector2& GetPosition( HandleType handleType ) const;
+
+ /**
+ * @brief Swaps the selection handle's images.
+ *
+ * This method is called by the text controller to swap the handles
+ * when the start index is bigger than the end one.
+ */
+ void SwapSelectionHandlesEnabled( bool enable );
+
+ /**
* @brief Adds a quad to the existing selection highlights.
*
* @param[in] x1 The top-left x position.
diff --git a/dali-toolkit/internal/text/text-control-interface.h b/dali-toolkit/internal/text/text-control-interface.h
index 4d74def..ede1531 100644
--- a/dali-toolkit/internal/text/text-control-interface.h
+++ b/dali-toolkit/internal/text/text-control-interface.h
@@ -50,8 +50,9 @@ public:
* @brief Add a decoration.
*
* @param[in] decoration The actor displaying a decoration.
+ * @param[in] needsClipping Whether the actor needs clipping.
*/
- virtual void AddDecoration( Actor& actor ) = 0;
+ virtual void AddDecoration( Actor& actor, bool needsClipping ) = 0;
/**
* @brief Called to request a text relayout.
diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp
index 2d84c2d..f41bddd 100644
--- a/dali-toolkit/internal/text/text-controller-impl.cpp
+++ b/dali-toolkit/internal/text/text-controller-impl.cpp
@@ -129,13 +129,13 @@ EventData::EventData( DecoratorPtr decorator )
mCursorBlinkEnabled( true ),
mGrabHandleEnabled( true ),
mGrabHandlePopupEnabled( true ),
- mSelectionEnabled( false ),
+ mSelectionEnabled( true ),
mHorizontalScrollingEnabled( true ),
mVerticalScrollingEnabled( false ),
mUpdateCursorPosition( false ),
mUpdateLeftSelectionPosition( false ),
mUpdateRightSelectionPosition( false ),
- mScrollAfterUpdateCursorPosition( false ),
+ mScrollAfterUpdatePosition( false ),
mScrollAfterDelete( false )
{}
@@ -193,10 +193,12 @@ bool Controller::Impl::ProcessInputEvents()
UpdateCursorPosition();
- if( mEventData->mScrollAfterUpdateCursorPosition )
+ if( mEventData->mScrollAfterUpdatePosition )
{
- ScrollToMakeCursorVisible();
- mEventData->mScrollAfterUpdateCursorPosition = false;
+ const Vector2& primaryCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR );
+
+ ScrollToMakePositionVisible( primaryCursorPosition );
+ mEventData->mScrollAfterUpdatePosition = false;
}
mEventData->mDecoratorUpdated = true;
@@ -208,39 +210,56 @@ bool Controller::Impl::ProcessInputEvents()
mEventData->mDecoratorUpdated = true;
mEventData->mScrollAfterDelete = false;
}
- else if( mEventData->mUpdateLeftSelectionPosition )
+ else
{
- UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
+ bool leftScroll = false;
+ bool rightScroll = false;
- if( mEventData->mScrollAfterUpdateCursorPosition )
+ if( mEventData->mUpdateLeftSelectionPosition )
{
- ScrollToMakeCursorVisible();
- mEventData->mScrollAfterUpdateCursorPosition = false;
- }
+ UpdateSelectionHandle( LEFT_SELECTION_HANDLE );
- mEventData->mDecoratorUpdated = true;
- mEventData->mUpdateLeftSelectionPosition = false;
- }
- else if( mEventData->mUpdateRightSelectionPosition )
- {
- UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
+ if( mEventData->mScrollAfterUpdatePosition )
+ {
+ const Vector2& leftHandlePosition = mEventData->mDecorator->GetPosition( LEFT_SELECTION_HANDLE );
+
+ ScrollToMakePositionVisible( leftHandlePosition );
+ leftScroll = true;
+ }
- if( mEventData->mScrollAfterUpdateCursorPosition )
+ mEventData->mDecoratorUpdated = true;
+ mEventData->mUpdateLeftSelectionPosition = false;
+ }
+
+ if( mEventData->mUpdateRightSelectionPosition )
{
- ScrollToMakeCursorVisible();
- mEventData->mScrollAfterUpdateCursorPosition = false;
+ UpdateSelectionHandle( RIGHT_SELECTION_HANDLE );
+
+ if( mEventData->mScrollAfterUpdatePosition )
+ {
+ const Vector2& rightHandlePosition = mEventData->mDecorator->GetPosition( RIGHT_SELECTION_HANDLE );
+
+ ScrollToMakePositionVisible( rightHandlePosition );
+ rightScroll = true;
+ }
+
+ mEventData->mDecoratorUpdated = true;
+ mEventData->mUpdateRightSelectionPosition = false;
}
- mEventData->mDecoratorUpdated = true;
- mEventData->mUpdateRightSelectionPosition = false;
+ if( leftScroll || rightScroll )
+ {
+ mEventData->mScrollAfterUpdatePosition = false;
+ }
}
mEventData->mEventQueue.clear();
DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" );
- bool decoratorUpdated = mEventData->mDecoratorUpdated;
+ const bool decoratorUpdated = mEventData->mDecoratorUpdated;
mEventData->mDecoratorUpdated = false;
+
return decoratorUpdated;
}
@@ -438,7 +457,7 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event )
}
mEventData->mUpdateCursorPosition = true;
- mEventData->mScrollAfterUpdateCursorPosition = true;
+ mEventData->mScrollAfterUpdatePosition = true;
}
void Controller::Impl::OnTapEvent( const Event& event )
@@ -463,12 +482,21 @@ void Controller::Impl::OnTapEvent( const Event& event )
}
mEventData->mUpdateCursorPosition = true;
- mEventData->mScrollAfterUpdateCursorPosition = true;
+ mEventData->mScrollAfterUpdatePosition = true;
}
else if( mEventData->mSelectionEnabled &&
( 2u == tapCount ) )
{
- RepositionSelectionHandles( event.p2.mFloat, event.p3.mFloat );
+ // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
+ const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
+ const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
+
+ RepositionSelectionHandles( xPosition,
+ yPosition );
+
+ mEventData->mScrollAfterUpdatePosition = true;
+ mEventData->mUpdateLeftSelectionPosition = true;
+ mEventData->mUpdateRightSelectionPosition = true;
}
}
}
@@ -521,6 +549,7 @@ void Controller::Impl::OnHandleEvent( const Event& event )
}
const unsigned int state = event.p1.mUint;
+ const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state );
if( HANDLE_PRESSED == state )
{
@@ -547,6 +576,10 @@ void Controller::Impl::OnHandleEvent( const Event& event )
if( handleNewPosition != mEventData->mLeftSelectionPosition )
{
mEventData->mLeftSelectionPosition = handleNewPosition;
+
+ RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+ mEventData->mRightSelectionPosition );
+
mEventData->mUpdateLeftSelectionPosition = true;
}
}
@@ -557,36 +590,76 @@ void Controller::Impl::OnHandleEvent( const Event& event )
if( handleNewPosition != mEventData->mRightSelectionPosition )
{
mEventData->mRightSelectionPosition = handleNewPosition;
+
+ RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+ mEventData->mRightSelectionPosition );
+
mEventData->mUpdateRightSelectionPosition = true;
}
}
- }
+ } // end ( HANDLE_PRESSED == state )
else if( ( HANDLE_RELEASED == state ) ||
- ( HANDLE_STOP_SCROLLING == state ) )
+ handleStopScrolling )
{
+ CharacterIndex handlePosition = 0u;
+ if( handleStopScrolling )
+ {
+ // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
+ const float xPosition = event.p2.mFloat - mEventData->mScrollPosition.x - mAlignmentOffset.x;
+ const float yPosition = event.p3.mFloat - mEventData->mScrollPosition.y - mAlignmentOffset.y;
+
+ handlePosition = GetClosestCursorIndex( xPosition, yPosition );
+ }
+
if( Event::GRAB_HANDLE_EVENT == event.type )
{
mEventData->mUpdateCursorPosition = true;
ChangeState( EventData::EDITING_WITH_POPUP );
- if( HANDLE_STOP_SCROLLING == state )
+ if( handleStopScrolling )
{
- // The event.p2 and event.p3 are in decorator coords. Need to transforms to text coords.
- 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 );
+ mEventData->mScrollAfterUpdatePosition = mEventData->mPrimaryCursorPosition != handlePosition;
+ mEventData->mPrimaryCursorPosition = handlePosition;
+ }
+ }
+ else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type )
+ {
+ ChangeState( EventData::SELECTING );
- mEventData->mScrollAfterUpdateCursorPosition = true;
+ if( handleStopScrolling )
+ {
+ mEventData->mUpdateLeftSelectionPosition = mEventData->mLeftSelectionPosition != handlePosition;
+ mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateLeftSelectionPosition;
+ mEventData->mLeftSelectionPosition = handlePosition;
+
+ if( mEventData->mUpdateLeftSelectionPosition )
+ {
+ RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+ mEventData->mRightSelectionPosition );
+ }
}
}
- else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type || Event::RIGHT_SELECTION_HANDLE_EVENT )
+ else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type )
{
ChangeState( EventData::SELECTING );
+
+ if( handleStopScrolling )
+ {
+ mEventData->mUpdateRightSelectionPosition = mEventData->mRightSelectionPosition != handlePosition;
+ mEventData->mScrollAfterUpdatePosition = mEventData->mUpdateRightSelectionPosition;
+ mEventData->mRightSelectionPosition = handlePosition;
+
+ if( mEventData->mUpdateRightSelectionPosition )
+ {
+ RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+ mEventData->mRightSelectionPosition );
+ }
+ }
}
+
mEventData->mDecoratorUpdated = true;
- }
+ } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) )
else if( HANDLE_SCROLLING == state )
{
const float xSpeed = event.p2.mFloat;
@@ -596,17 +669,105 @@ void Controller::Impl::OnHandleEvent( const Event& event )
ClampHorizontalScroll( actualSize );
+ const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type;
+ const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type;
+
if( Event::GRAB_HANDLE_EVENT == event.type )
{
ChangeState( EventData::GRAB_HANDLE_PANNING );
}
- else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type || Event::RIGHT_SELECTION_HANDLE_EVENT )
+ else if( leftSelectionHandleEvent || rightSelectionHandleEvent )
{
+ // TODO: This is recalculating the selection box every time the text is scrolled with the selection handles.
+ // Think if something can be done to save power.
+
ChangeState( EventData::SELECTION_HANDLE_PANNING );
- }
+ const Vector2& position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE );
+
+ // Get the new handle position.
+ // The selection handle's position is in decorator coords. Need to transforms to text coords.
+ const CharacterIndex handlePosition = GetClosestCursorIndex( position.x - mEventData->mScrollPosition.x - mAlignmentOffset.x,
+ position.y - mEventData->mScrollPosition.y - mAlignmentOffset.y );
+
+ if( leftSelectionHandleEvent )
+ {
+ mEventData->mUpdateLeftSelectionPosition = handlePosition != mEventData->mLeftSelectionPosition;
+ mEventData->mLeftSelectionPosition = handlePosition;
+ }
+ else
+ {
+ mEventData->mUpdateRightSelectionPosition = handlePosition != mEventData->mRightSelectionPosition;
+ mEventData->mRightSelectionPosition = handlePosition;
+ }
+
+ if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition )
+ {
+ RepositionSelectionHandles( mEventData->mLeftSelectionPosition,
+ mEventData->mRightSelectionPosition );
+ }
+ }
mEventData->mDecoratorUpdated = true;
+ } // end ( HANDLE_SCROLLING == state )
+}
+
+void Controller::Impl::RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd )
+{
+ mEventData->mDecorator->ClearHighlights();
+
+ mEventData->mLeftSelectionPosition = selectionStart;
+ mEventData->mRightSelectionPosition = selectionEnd;
+
+ const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin();
+ const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin();
+ const GlyphInfo* const glyphsBuffer = mVisualModel->mGlyphs.Begin();
+ const Vector2* const positionsBuffer = mVisualModel->mGlyphPositions.Begin();
+
+ // TODO: Better algorithm to create the highlight box.
+ // TODO: Multi-line.
+
+ const Vector& lines = mVisualModel->mLines;
+ const float height = lines[0].ascender + -lines[0].descender;
+
+ const bool indicesSwapped = selectionStart > selectionEnd;
+ if( indicesSwapped )
+ {
+ std::swap( selectionStart, selectionEnd );
}
+
+ GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart );
+ GlyphIndex glyphEnd = *( charactersToGlyphBuffer + ( selectionEnd - 1u ) ) + *( glyphsPerCharacterBuffer + ( selectionEnd - 1u ) ) - 1u;
+
+ mEventData->mDecorator->SwapSelectionHandlesEnabled( indicesSwapped );
+
+ const Vector2 offset = mEventData->mScrollPosition + mAlignmentOffset;
+
+ for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index )
+ {
+ const GlyphInfo& glyph = *( glyphsBuffer + index );
+ const Vector2& position = *( positionsBuffer + index );
+
+ const float xPosition = position.x - glyph.xBearing + offset.x;
+ mEventData->mDecorator->AddHighlight( xPosition, offset.y, xPosition + glyph.advance, height );
+ }
+
+ CursorInfo primaryCursorInfo;
+ GetCursorPosition( mEventData->mLeftSelectionPosition,
+ primaryCursorInfo );
+
+ CursorInfo secondaryCursorInfo;
+ GetCursorPosition( mEventData->mRightSelectionPosition,
+ secondaryCursorInfo );
+
+ const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + offset;
+ const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + offset;
+
+ mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, primaryPosition.y, primaryCursorInfo.lineHeight );
+
+ mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, secondaryPosition.y, secondaryCursorInfo.lineHeight );
+
+ // Set the flag to update the decorator.
+ mEventData->mDecoratorUpdated = true;
}
void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY )
@@ -617,32 +778,35 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY
return;
}
- // TODO - Find which word was selected
-
- const Vector& glyphs = mVisualModel->mGlyphs;
- const Vector::SizeType glyphCount = glyphs.Count();
-
- const Vector& positions = mVisualModel->mGlyphPositions;
- const Vector::SizeType positionCount = positions.Count();
-
- // Guard against glyphs which did not fit inside the layout
- const Vector::SizeType count = (positionCount < glyphCount) ? positionCount : glyphCount;
-
- if( count )
+ if( IsShowingPlaceholderText() )
{
- float primaryX = positions[0].x + mEventData->mScrollPosition.x;
- float secondaryX = positions[count-1].x + glyphs[count-1].width + mEventData->mScrollPosition.x;
+ // Nothing to do if there is the place-holder text.
+ return;
+ }
- // TODO - multi-line selection
- const Vector& lines = mVisualModel->mLines;
- float height = lines.Count() ? lines[0].ascender + -lines[0].descender : 0.0f;
+ const Length numberOfGlyphs = mVisualModel->mGlyphs.Count();
+ const Length numberOfLines = mVisualModel->mLines.Count();
+ if( 0 == numberOfGlyphs ||
+ 0 == numberOfLines )
+ {
+ // Nothing to do if there is no text.
+ return;
+ }
- mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryX, mEventData->mScrollPosition.y, height );
- mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryX, mEventData->mScrollPosition.y, height );
+ // Find which word was selected
+ CharacterIndex selectionStart( 0 );
+ CharacterIndex selectionEnd( 0 );
+ FindSelectionIndices( visualX, visualY, selectionStart, selectionEnd );
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd );
- mEventData->mDecorator->ClearHighlights();
- mEventData->mDecorator->AddHighlight( primaryX, mEventData->mScrollPosition.y, secondaryX, height + mEventData->mScrollPosition.y );
+ if( selectionStart == selectionEnd )
+ {
+ ChangeState( EventData::EDITING );
+ // Nothing to select. i.e. a white space, out of bounds
+ return;
}
+
+ RepositionSelectionHandles( selectionStart, selectionEnd );
}
void Controller::Impl::ChangeState( EventData::State newState )
@@ -777,6 +941,41 @@ LineIndex Controller::Impl::GetClosestLine( float y ) const
return lineIndex-1;
}
+void Controller::Impl::FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex )
+{
+ CharacterIndex hitCharacter = GetClosestCursorIndex( visualX, visualY );
+ if( hitCharacter >= mLogicalModel->mText.Count() )
+ {
+ // Selection out of bounds.
+ return;
+ }
+
+ startIndex = hitCharacter;
+ endIndex = hitCharacter;
+
+ if( !TextAbstraction::IsWhiteSpace( mLogicalModel->mText[hitCharacter] ) )
+ {
+ // Find the start and end of the text
+ for( startIndex = hitCharacter; startIndex > 0; --startIndex )
+ {
+ Character charCode = mLogicalModel->mText[ startIndex-1 ];
+ if( TextAbstraction::IsWhiteSpace( charCode ) )
+ {
+ break;
+ }
+ }
+ const CharacterIndex pastTheEnd = mLogicalModel->mText.Count();
+ for( endIndex = hitCharacter + 1u; endIndex < pastTheEnd; ++endIndex )
+ {
+ Character charCode = mLogicalModel->mText[ endIndex ];
+ if( TextAbstraction::IsWhiteSpace( charCode ) )
+ {
+ break;
+ }
+ }
+ }
+}
+
CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
float visualY )
{
@@ -847,9 +1046,10 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
const Vector2& position = *( positionsBuffer + glyphLogicalOrderIndex );
- const float glyphX = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
+ // Find the mid-point of the area containing the glyph
+ const float glyphCenter = -glyphMetrics.xBearing + position.x + 0.5f * glyphMetrics.advance;
- if( visualX < glyphX )
+ if( visualX < glyphCenter )
{
matched = true;
break;
@@ -863,7 +1063,9 @@ CharacterIndex Controller::Impl::GetClosestCursorIndex( float visualX,
visualIndex = endCharacter;
}
- return hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
+ logicalIndex = hasRightToLeftCharacters ? *( visualToLogicalCursorBuffer + visualIndex ) : visualIndex;
+ DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p closest visualIndex %d logicalIndex %d\n", this, visualIndex, logicalIndex );
+ return logicalIndex;
}
void Controller::Impl::GetCursorPosition( CharacterIndex logical,
@@ -1281,27 +1483,19 @@ void Controller::Impl::ClampVerticalScroll( const Vector2& actualSize )
}
}
-void Controller::Impl::ScrollToMakeCursorVisible()
+void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position )
{
- 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 )
+ if( position.x < 0.f )
{
- offset.x = -primaryCursorPosition.x;
+ offset.x = -position.x;
mEventData->mScrollPosition.x += offset.x;
updateDecorator = true;
}
- else if( primaryCursorPosition.x > mControlSize.width )
+ else if( position.x > mControlSize.width )
{
- offset.x = mControlSize.width - primaryCursorPosition.x;
+ offset.x = mControlSize.width - position.x;
mEventData->mScrollPosition.x += offset.x;
updateDecorator = true;
}
diff --git a/dali-toolkit/internal/text/text-controller-impl.h b/dali-toolkit/internal/text/text-controller-impl.h
index 1a797b3..81125da 100644
--- a/dali-toolkit/internal/text/text-controller-impl.h
+++ b/dali-toolkit/internal/text/text-controller-impl.h
@@ -135,20 +135,20 @@ struct EventData
CharacterIndex mPreEditStartPosition; ///< Used to remove the pre-edit text if necessary.
Length mPreEditLength; ///< Used to remove the pre-edit text if necessary.
- bool mIsShowingPlaceholderText : 1; ///< True if the place-holder text is being displayed.
- bool mPreEditFlag : 1; ///< True if the model contains text in pre-edit state.
- 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 mUpdateLeftSelectionPosition : 1; ///< True if the visual position of the left selection handle must be recalculated.
- bool mUpdateRightSelectionPosition : 1; ///< True if the visual position of the right selection handle must be recalculated.
- bool mScrollAfterUpdateCursorPosition : 1; ///< Whether to scroll after the cursor position is updated.
- bool mScrollAfterDelete : 1; ///< Whether to scroll after delete characters.
+ bool mIsShowingPlaceholderText : 1; ///< True if the place-holder text is being displayed.
+ bool mPreEditFlag : 1; ///< True if the model contains text in pre-edit state.
+ 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 mUpdateLeftSelectionPosition : 1; ///< True if the visual position of the left selection handle must be recalculated.
+ bool mUpdateRightSelectionPosition : 1; ///< True if the visual position of the right selection handle must be recalculated.
+ bool mScrollAfterUpdatePosition : 1; ///< Whether to scroll after the cursor position is updated.
+ bool mScrollAfterDelete : 1; ///< Whether to scroll after delete characters.
};
struct ModifyEvent
@@ -323,12 +323,15 @@ struct Controller::Impl
void OnHandleEvent( const Event& event );
+ void RepositionSelectionHandles( CharacterIndex selectionStart, CharacterIndex selectionEnd );
void RepositionSelectionHandles( float visualX, float visualY );
void ChangeState( EventData::State newState );
LineIndex GetClosestLine( float y ) const;
+ void FindSelectionIndices( float visualX, float visualY, CharacterIndex& startIndex, CharacterIndex& endIndex );
+
/**
* @brief Retrieves the cursor's logical position for a given touch point x,y
*
@@ -395,11 +398,16 @@ struct Controller::Impl
void ClampVerticalScroll( const Vector2& actualSize );
/**
- * @brief Scrolls the text to make the cursor visible.
+ * @brief Scrolls the text to make a position visible.
+ *
+ * @pre mEventData must not be NULL. (there is a text-input or selection capabilities).
+ *
+ * @param[in] position A position in decorator coords.
*
- * This method is called after inserting text or moving the cursor with the keypad.
+ * This method is called after inserting text, moving the cursor with the grab handle or the keypad,
+ * or moving the selection handles.
*/
- void ScrollToMakeCursorVisible();
+ void ScrollToMakePositionVisible( const Vector2& position );
/**
* @brief Scrolls the text to make the cursor visible.
diff --git a/dali-toolkit/internal/text/text-controller.cpp b/dali-toolkit/internal/text/text-controller.cpp
index 26d7ef0..3b36ed3 100644
--- a/dali-toolkit/internal/text/text-controller.cpp
+++ b/dali-toolkit/internal/text/text-controller.cpp
@@ -708,7 +708,7 @@ void Controller::ResetScrollPosition()
{
// Reset the scroll position.
mImpl->mEventData->mScrollPosition = Vector2::ZERO;
- mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
+ mImpl->mEventData->mScrollAfterUpdatePosition = true;
}
}
@@ -749,7 +749,7 @@ void Controller::TextInsertedEvent()
// Queue a cursor reposition event; this must wait until after DoRelayout()
mImpl->mEventData->mUpdateCursorPosition = true;
- mImpl->mEventData->mScrollAfterUpdateCursorPosition = true;
+ mImpl->mEventData->mScrollAfterUpdatePosition = true;
}
void Controller::TextDeletedEvent()
@@ -1305,12 +1305,12 @@ void Controller::TapEvent( unsigned int tapCount, float x, float y )
if( NULL != mImpl->mEventData )
{
+ const bool isShowingPlaceholderText = mImpl->IsShowingPlaceholderText();
if( 1u == tapCount )
{
bool tapDuringEditMode( EventData::EDITING == mImpl->mEventData->mState );
- if( ! mImpl->IsShowingPlaceholderText() &&
- EventData::EDITING == mImpl->mEventData->mState )
+ if( !isShowingPlaceholderText && tapDuringEditMode )
{
// Grab handle is not shown until a tap is received whilst EDITING
if( tapDuringEditMode )
@@ -1322,7 +1322,8 @@ void Controller::TapEvent( unsigned int tapCount, float x, float y )
mImpl->ChangeState( EventData::EDITING );
}
- else if( mImpl->mEventData->mSelectionEnabled &&
+ else if( !isShowingPlaceholderText &&
+ mImpl->mEventData->mSelectionEnabled &&
( 2u == tapCount ) )
{
mImpl->ChangeState( EventData::SELECTING );
@@ -1366,9 +1367,9 @@ void Controller::GetTargetSize( Vector2& targetSize )
targetSize = mImpl->mControlSize;
}
-void Controller::AddDecoration( Actor& actor )
+void Controller::AddDecoration( Actor& actor, bool needsClipping )
{
- mImpl->mControlInterface.AddDecoration( actor );
+ mImpl->mControlInterface.AddDecoration( actor, needsClipping );
}
void Controller::DecorationEvent( HandleType handleType, HandleState state, float x, float y )
diff --git a/dali-toolkit/internal/text/text-controller.h b/dali-toolkit/internal/text/text-controller.h
index 8428699..66bf3a0 100644
--- a/dali-toolkit/internal/text/text-controller.h
+++ b/dali-toolkit/internal/text/text-controller.h
@@ -532,7 +532,7 @@ public:
/**
* @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::AddDecoration()
*/
- virtual void AddDecoration( Actor& actor );
+ virtual void AddDecoration( Actor& actor, bool needsClipping );
/**
* @copydoc Dali::Toolkit::Text::Decorator::ControllerInterface::DecorationEvent()