From 066856d5c4c7a8bacd33f9a3ed49018e8dc41c4f Mon Sep 17 00:00:00 2001 From: Victor Cebollada Date: Wed, 24 Sep 2014 17:33:44 +0100 Subject: [PATCH] TextInput - Fixes the selection box for right to left text. Creates one quad per character to allow different boxes. TODO: the number of quads could be minimized. Change-Id: Ibbf0d3cb8ea2f55851c130708271cce561e9cd39 Signed-off-by: Victor Cebollada --- .../controls/text-input/text-input-impl.cpp | 260 +++++++++++++++++---- .../internal/controls/text-input/text-input-impl.h | 21 ++ 2 files changed, 240 insertions(+), 41 deletions(-) diff --git a/base/dali-toolkit/internal/controls/text-input/text-input-impl.cpp b/base/dali-toolkit/internal/controls/text-input/text-input-impl.cpp index 7cb426a..0264adc 100644 --- a/base/dali-toolkit/internal/controls/text-input/text-input-impl.cpp +++ b/base/dali-toolkit/internal/controls/text-input/text-input-impl.cpp @@ -1487,7 +1487,19 @@ void TextInput::OnTextTap(Dali::Actor actor, Dali::TapGesture tap) // otherwise the Grab handle will be shown when selecting. if ( createGrabHandle && IsGrabHandleEnabled() ) { - const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition); + Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position. + bool altPositionValid; // Alternate cursor validity flag. + bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently) + Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid ); + + if( altPositionValid ) + { + // Check which of the positions is the closest. + if( fabsf( altPosition.x - tap.localPoint.x ) < fabsf( cursorPosition.x - tap.localPoint.x ) ) + { + cursorPosition = altPosition; + } + } CreateGrabHandle(); @@ -1842,6 +1854,64 @@ bool TextInput::OnKeyUpEvent(const KeyEvent& event) return false; } +void TextInput::ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne, + const Vector3& cursorPositionTwo, + bool altPositionValidOne, + bool altPositionValidTwo, + const Vector3& altPositionOne, + const Vector3& altPositionTwo ) +{ + // TODO VCC Valid for one line. + // Try to place the selection handles. TODO think in something better. Probably need to know the direction of the paragraph. + if( cursorPositionOne != cursorPositionTwo ) + { + if( cursorPositionOne.x < cursorPositionTwo.x ) + { + mSelectionHandleOneActualPosition = cursorPositionOne; + mSelectionHandleTwoActualPosition = cursorPositionTwo; + } + else + { + mSelectionHandleOneActualPosition = cursorPositionTwo; + mSelectionHandleTwoActualPosition = cursorPositionOne; + } + } + else + { + mSelectionHandleOneActualPosition = cursorPositionOne; + if( altPositionValidOne ) + { + if( altPositionOne.x < mSelectionHandleOneActualPosition.x ) + { + mSelectionHandleOneActualPosition = altPositionOne; + } + } + if( altPositionValidTwo ) + { + if( altPositionTwo.x < mSelectionHandleOneActualPosition.x ) + { + mSelectionHandleOneActualPosition = altPositionTwo; + } + } + + mSelectionHandleTwoActualPosition = cursorPositionTwo; + if( altPositionValidTwo ) + { + if( altPositionTwo.x > mSelectionHandleTwoActualPosition.x ) + { + mSelectionHandleTwoActualPosition = altPositionTwo; + } + } + if( altPositionValidOne ) + { + if( altPositionOne.x > mSelectionHandleTwoActualPosition.x ) + { + mSelectionHandleTwoActualPosition = altPositionOne; + } + } + } +} + void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPosition ) { // Updates the stored scroll position. @@ -1854,7 +1924,20 @@ void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPo if( mGrabHandle || mCursor ) { cursorSize.height = GetRowRectFromCharacterPosition( mCursorPosition ).height; - const Vector3 cursorPosition = GetActualPositionFromCharacterPosition(mCursorPosition); + + Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position. + bool altPositionValid; // Alternate cursor validity flag. + bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently) + Vector3 cursorPosition = GetActualPositionFromCharacterPosition( mCursorPosition, directionRTL, altPosition, altPositionValid ); + + if( altPositionValid ) + { + // Check which of the positions is the closest. + if( fabsf( altPosition.x - mActualGrabHandlePosition.x ) < fabsf( cursorPosition.x - mActualGrabHandlePosition.x ) ) + { + cursorPosition = altPosition; + } + } mIsCursorInScrollArea = mIsGrabHandleInScrollArea = IsPositionInsideBoundaries( cursorPosition, cursorSize, controlSize ); @@ -1876,16 +1959,29 @@ void TextInput::OnTextViewScrolled( Toolkit::TextView textView, Vector2 scrollPo // Updates the selection handles and highlighted text position and visibility. if( mSelectionHandleOne && mSelectionHandleTwo ) { - const Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition(mSelectionHandleOnePosition); - const Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition(mSelectionHandleTwoPosition); + Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position. + bool altPositionValidOne; // Alternate cursor validity flag. + bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently) + Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne ); + + Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position. + bool altPositionValidTwo; // Alternate cursor validity flag. + bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently) + Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo ); + + // VCC TODO: This method is a hack for one line. + ChooseRtlSelectionHandlePosition( cursorPositionOne, + cursorPositionTwo, + altPositionValidOne, + altPositionValidTwo, + altPositionOne, + altPositionTwo ); + cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleOnePosition ) ).mSize.height; const bool isSelectionHandleOneVisible = IsPositionInsideBoundaries( cursorPositionOne, cursorSize, controlSize ); cursorSize.height = ( *( mTextLayoutInfo.mCharacterLayoutInfoTable.begin() + mSelectionHandleTwoPosition ) ).mSize.height; const bool isSelectionHandleTwoVisible = IsPositionInsideBoundaries( cursorPositionTwo, cursorSize, controlSize ); - mSelectionHandleOneActualPosition = cursorPositionOne.GetVectorXY(); - mSelectionHandleTwoActualPosition = cursorPositionTwo.GetVectorXY(); - mSelectionHandleOne.SetVisible( isSelectionHandleOneVisible ); mSelectionHandleTwo.SetVisible( isSelectionHandleTwoVisible ); mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset ); @@ -3226,8 +3322,23 @@ void TextInput::CreateSelectionHandles( std::size_t start, std::size_t end, Dali // update table as text may have changed. GetTextLayoutInfo(); - mSelectionHandleOneActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition ); - mSelectionHandleTwoActualPosition = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition ); + Vector3 altPositionOne; // Alternate (i.e. opposite direction) cursor position. + bool altPositionValidOne; // Alternate cursor validity flag. + bool directionRTLOne; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently) + Vector3 cursorPositionOne = GetActualPositionFromCharacterPosition( mSelectionHandleOnePosition, directionRTLOne, altPositionOne, altPositionValidOne ); + + Vector3 altPositionTwo; // Alternate (i.e. opposite direction) cursor position. + bool altPositionValidTwo; // Alternate cursor validity flag. + bool directionRTLTwo; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently) + Vector3 cursorPositionTwo = GetActualPositionFromCharacterPosition( mSelectionHandleTwoPosition, directionRTLTwo, altPositionTwo, altPositionValidTwo ); + + // VCC TODO: This method is a hack for one line. + ChooseRtlSelectionHandlePosition( cursorPositionOne, + cursorPositionTwo, + altPositionValidOne, + altPositionValidTwo, + altPositionOne, + altPositionTwo ); mSelectionHandleOne.SetPosition( mSelectionHandleOneActualPosition + UI_OFFSET + mSelectionHandleOneOffset ); mSelectionHandleTwo.SetPosition( mSelectionHandleTwoActualPosition + UI_OFFSET + mSelectionHandleTwoOffset ); @@ -3271,7 +3382,18 @@ Vector3 TextInput::MoveSelectionHandle( SelectionHandleId handleId, const Vector std::size_t newHandlePosition = 0; ReturnClosestIndex( actualSelectionHandlePosition.GetVectorXY(), newHandlePosition ); - actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition ); + Vector3 altPosition; // Alternate (i.e. opposite direction) cursor position. + bool altPositionValid; // Alternate cursor validity flag. + bool directionRTL; // Need to know direction of primary cursor (in case we have 2 cursors and need to show them differently) + actualHandlePosition = GetActualPositionFromCharacterPosition( newHandlePosition, directionRTL, altPosition, altPositionValid ); + if( altPositionValid ) + { + // Check which of the positions is the closest. + if( fabsf( altPosition.x - actualSelectionHandlePosition.x ) < fabsf( actualHandlePosition.x - actualSelectionHandlePosition.x ) ) + { + actualHandlePosition = altPosition; + } + } bool handleVisible = true; @@ -3370,34 +3492,28 @@ void TextInput::SetSelectionHandlePosition(SelectionHandleId handleId) } } -void TextInput::GetVisualTextSelection(std::vector& selectedVisualText, std::size_t startSelection, std::size_t endSelection) +void TextInput::GetVisualTextSelection( std::vector& selectedVisualText, std::size_t startSelection, std::size_t endSelection ) { - std::vector::iterator it = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin(); - std::vector::iterator startSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::min(startSelection, endSelection); - std::vector::iterator endSelectionIt = mTextLayoutInfo.mCharacterLogicalToVisualMap.begin() + std::max(startSelection, endSelection); - std::vector::iterator end = mTextLayoutInfo.mCharacterLogicalToVisualMap.end(); - - selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size() ); + selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false ); - // Deselect text prior to startSelectionIt - for(;it!=startSelectionIt;++it) - { - selectedVisualText[*it] = false; - } + // VCC Set true/false in logical order. TODO : It needs to be checked. - // Select text from startSelectionIt -> endSelectionIt - for(;it!=endSelectionIt;++it) + if( startSelection > endSelection ) { - selectedVisualText[*it] = true; + std::swap( startSelection, endSelection ); } - - // Deselect text after endSelection - for(;it!=end;++it) + std::size_t index = 0u; + for( std::vector::iterator it = selectedVisualText.begin(), endIt = selectedVisualText.end(); it != endIt; ++it, ++index ) { - selectedVisualText[*it] = false; + if( ( index < startSelection ) || ( endSelection <= index ) ) + { + *it = false; + } + else + { + *it = true; + } } - - selectedVisualText.resize( mTextLayoutInfo.mCharacterLogicalToVisualMap.size(), false ); } // Calculate the dimensions of the quads they will make the highlight mesh @@ -3415,9 +3531,9 @@ TextInput::HighlightInfo TextInput::CalculateHighlightInfo() // Get vector of flags representing characters that are selected (true) vs unselected (false). std::vector selectedVisualText; - GetVisualTextSelection(selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition); - std::vector::iterator selectedIt(selectedVisualText.begin()); - std::vector::iterator selectedEndIt(selectedVisualText.end()); + GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition ); + std::vector::iterator selectedIt = selectedVisualText.begin(); + std::vector::iterator selectedEndIt = selectedVisualText.end(); SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text. float rowLeft = 0.0f; @@ -3434,28 +3550,28 @@ TextInput::HighlightInfo TextInput::CalculateHighlightInfo() // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection. Toolkit::TextView::CharacterLayoutInfo& charInfo(*it); - bool charSelected( false ); + bool charSelected = false; if( selectedIt != selectedEndIt ) { charSelected = *selectedIt++; } - if(selectionState == SelectionNone) + if( selectionState == SelectionNone ) { - if(charSelected) + if( charSelected ) { selectionState = SelectionStarted; rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x; rowRight = rowLeft + charInfo.mSize.width; } } - else if(selectionState == SelectionStarted) + else if( selectionState == SelectionStarted ) { // break selection on: // 1. new line causing selection break. (\n or wordwrap) // 2. character not selected. - if(charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD || - !charSelected) + if( !charSelected || + ( charInfo.mPosition.y - lastIt->mPosition.y > CHARACTER_THRESHOLD ) ) { // finished selection. // TODO: TextView should have a table of visual rows, and each character a reference to the row @@ -3558,6 +3674,66 @@ TextInput::HighlightInfo TextInput::CalculateHighlightInfo() return mNewHighlightInfo; } +// VCC TODO: two methods are not needed. this one is a quick hack to fix PLMs. Should implement one which support both directions. +// This method creates one quad per character so different selection boxes for a mix of LTR and RTL languages are created. +TextInput::HighlightInfo TextInput::CalculateHighlightInfoRtl() +{ + // At the moment there is no public API to modify the block alignment option. + + mNewHighlightInfo.mQuadList.clear(); // clear last quad information. + + if ( !mTextLayoutInfo.mCharacterLayoutInfoTable.empty() && !mTextLayoutInfo.mCharacterLogicalToVisualMap.empty() ) + { + Toolkit::TextView::CharacterLayoutInfoContainer::iterator it = mTextLayoutInfo.mCharacterLayoutInfoTable.begin(); + Toolkit::TextView::CharacterLayoutInfoContainer::iterator end = mTextLayoutInfo.mCharacterLayoutInfoTable.end(); + + // Get vector of flags representing characters that are selected (true) vs unselected (false). + std::vector selectedVisualText; + GetVisualTextSelection( selectedVisualText, mSelectionHandleOnePosition, mSelectionHandleTwoPosition ); + std::vector::iterator selectedIt = selectedVisualText.begin(); + std::vector::iterator selectedEndIt = selectedVisualText.end(); + + // SelectionState selectionState = SelectionNone; ///< Current selection status of cursor over entire text. + float rowLeft = 0.0f; + float rowRight = 0.0f; + + // VCC TODO this is valid for one line. + Vector2 min, max; + const Size rowSize = GetRowRectFromCharacterPosition( 0, min, max ); + + // Scan through entire text. + while(it != end) + { + // selectionState: None when not in selection, Started when in selection, and Ended when reached end of selection. + + Toolkit::TextView::CharacterLayoutInfo& charInfo(*it); + bool charSelected = false; + if( selectedIt != selectedEndIt ) + { + charSelected = *selectedIt++; + } + + if( charSelected ) + { + rowLeft = charInfo.mPosition.x - mTextLayoutInfo.mScrollOffset.x; + rowRight = rowLeft + charInfo.mSize.width; + + float rowBottom = charInfo.mPosition.y - mTextLayoutInfo.mScrollOffset.y; + float rowTop = rowBottom - rowSize.height; + mNewHighlightInfo.AddQuad( rowLeft, rowTop, rowRight, rowBottom ); + } + + ++it; + } + + // Finally clamp quads again so they don't exceed the boundry of the control. + const Vector3& controlSize = GetControlSize(); + mNewHighlightInfo.Clamp2D( Vector2::ZERO, Vector2(controlSize.x, controlSize.y) ); + } // end if + + return mNewHighlightInfo; +} + void TextInput::UpdateHighlight() { // Construct a Mesh with a texture to be used as the highlight 'box' for selected text @@ -3588,7 +3764,7 @@ void TextInput::UpdateHighlight() if ( mHighlightMeshActor ) { // vertex and triangle buffers should always be present if MeshActor is alive. - HighlightInfo newHighlightInfo = CalculateHighlightInfo(); + HighlightInfo newHighlightInfo = CalculateHighlightInfoRtl(); MeshData::VertexContainer vertices; Dali::MeshData::FaceIndices faceIndices; @@ -4353,6 +4529,7 @@ Vector3 TextInput::GetActualPositionFromCharacterPosition( std::size_t character cursorPosition.x -= mTextLayoutInfo.mScrollOffset.x; cursorPosition.y -= mTextLayoutInfo.mScrollOffset.y; + if( alternatePositionValid ) { alternatePosition.x -= mTextLayoutInfo.mScrollOffset.x; @@ -4719,6 +4896,7 @@ bool TextInput::CopySelectedTextToClipboard() */ MarkupProcessor::StyledTextArray selectedText(mCurrentCopySelecton.begin(),mCurrentCopySelecton.end()); MarkupProcessor::GetPlainString( selectedText, stringToStore ); + bool success = mClipboard.SetItem( stringToStore ); return success; } diff --git a/base/dali-toolkit/internal/controls/text-input/text-input-impl.h b/base/dali-toolkit/internal/controls/text-input/text-input-impl.h index 5801137..9cd2dba 100644 --- a/base/dali-toolkit/internal/controls/text-input/text-input-impl.h +++ b/base/dali-toolkit/internal/controls/text-input/text-input-impl.h @@ -702,6 +702,22 @@ private: bool OnKeyUpEvent(const KeyEvent& event); /** + * Chooses from all handle position and alternative handle positions where to set the position of the two selection handles. + * + * @param[in] cursorPositionOne The initial position of the first selection handle. + * @param[in] cursorPositionTwo The initial position of the second selection handle. + * @param[in] altPositionValidOne Whether there is an alternative position for the first selection handle. + * @param[in] altPositionValidTwo Whether there is an alternative position for the second selection handle. + * @param[in] altPositionOne The alternative position of the first selection handle. + * @param[in] altPositionTwo The alternative position of the second selection handle. + */ + void ChooseRtlSelectionHandlePosition( const Vector3& cursorPositionOne, + const Vector3& cursorPositionTwo, + bool altPositionValidOne, + bool altPositionValidTwo, + const Vector3& altPositionOne, + const Vector3& altPositionTwo ); + /** * Callback called when the text-view is scrolled. * * Updates the selection and grab handles, and the highlighted text. @@ -1033,6 +1049,11 @@ public: // Public to allow internal testing. HighlightInfo CalculateHighlightInfo(); /** + * This method was added to fix a PLM. it creates one quad per character so the mesh can show different selection boxes when a mix of right to left and left to right text is selected. + */ + HighlightInfo CalculateHighlightInfoRtl(); + + /** * Calculates new Mesh data so highlight moves with selection handles. */ void UpdateHighlight(); -- 2.7.4