X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftext%2Ftext-controller-impl.cpp;h=01016163786b1c6ae4d59230bba5df0cfa36ae78;hp=b6efd5901a2b50848ce7a619137d580e6f2878d3;hb=60d99d04d7677a1b91d4f2fdd9e74bc5e1b5e7bf;hpb=260c832bfaa0294e74a4f96d321f149adf09a3ce diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp index b6efd59..9144ae3 100644 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ b/dali-toolkit/internal/text/text-controller-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2017 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. @@ -20,10 +20,12 @@ // EXTERNAL INCLUDES #include +#include #include #include // INTERNAL INCLUDES +#include #include #include #include @@ -31,8 +33,11 @@ #include #include #include +#include #include +using namespace Dali; + namespace { @@ -55,6 +60,50 @@ const float MAX_FLOAT = std::numeric_limits::max(); const float MIN_FLOAT = std::numeric_limits::min(); const Dali::Toolkit::Text::CharacterDirection LTR = false; ///< Left To Right direction +#define MAKE_SHADER(A)#A + +const char* VERTEX_SHADER_BACKGROUND = MAKE_SHADER( +attribute mediump vec2 aPosition; +attribute mediump vec4 aColor; +varying mediump vec4 vColor; +uniform highp mat4 uMvpMatrix; + +void main() +{ + mediump vec4 position = vec4( aPosition, 0.0, 1.0 ); + gl_Position = uMvpMatrix * position; + vColor = aColor; +} +); + +const char* FRAGMENT_SHADER_BACKGROUND = MAKE_SHADER( +varying mediump vec4 vColor; +uniform lowp vec4 uColor; + +void main() +{ + gl_FragColor = vColor * uColor; +} +); + +struct BackgroundVertex +{ + Vector2 mPosition; ///< Vertex posiiton + Vector4 mColor; ///< Vertex color +}; + +struct BackgroundMesh +{ + Vector< BackgroundVertex > mVertices; ///< container of vertices + Vector< unsigned short > mIndices; ///< container of indices +}; + +const Dali::Vector4 LIGHT_BLUE( 0.75f, 0.96f, 1.f, 1.f ); +const Dali::Vector4 BACKGROUND_SUB4( 0.58f, 0.87f, 0.96f, 1.f ); +const Dali::Vector4 BACKGROUND_SUB5( 0.83f, 0.94f, 0.98f, 1.f ); +const Dali::Vector4 BACKGROUND_SUB6( 1.f, 0.5f, 0.5f, 1.f ); +const Dali::Vector4 BACKGROUND_SUB7( 1.f, 0.8f, 0.8f, 1.f ); + } // namespace namespace Dali @@ -66,14 +115,16 @@ namespace Toolkit namespace Text { -EventData::EventData( DecoratorPtr decorator ) +EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext ) : mDecorator( decorator ), - mImfManager(), + mInputMethodContext( inputMethodContext ), + mPlaceholderFont( NULL ), mPlaceholderTextActive(), mPlaceholderTextInactive(), - mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), + mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h). mEventQueue(), mInputStyleChangedQueue(), + mPreviousState( INACTIVE ), mState( INACTIVE ), mPrimaryCursorPosition( 0u ), mLeftSelectionPosition( 0u ), @@ -81,6 +132,8 @@ EventData::EventData( DecoratorPtr decorator ) mPreEditStartPosition( 0u ), mPreEditLength( 0u ), mCursorHookPositionX( 0.f ), + mDoubleTapAction( Controller::NoTextTap::NO_ACTION ), + mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ), mIsShowingPlaceholderText( false ), mPreEditFlag( false ), mDecoratorUpdated( false ), @@ -88,18 +141,26 @@ EventData::EventData( DecoratorPtr decorator ) mGrabHandleEnabled( true ), mGrabHandlePopupEnabled( true ), mSelectionEnabled( true ), + mUpdateCursorHookPosition( false ), mUpdateCursorPosition( false ), mUpdateGrabHandlePosition( false ), mUpdateLeftSelectionPosition( false ), mUpdateRightSelectionPosition( false ), mIsLeftHandleSelected( false ), + mIsRightHandleSelected( false ), mUpdateHighlightBox( false ), mScrollAfterUpdatePosition( false ), mScrollAfterDelete( false ), mAllTextSelected( false ), - mUpdateInputStyle( false ) + mUpdateInputStyle( false ), + mPasswordInput( false ), + mCheckScrollAmount( false ), + mIsPlaceholderPixelSize( false ), + mIsPlaceholderElideEnabled( false ), + mPlaceholderEllipsisFlag( false ), + mShiftSelectionFlag( true ), + mUpdateAlignment( false ) { - mImfManager = ImfManager::Get(); } EventData::~EventData() @@ -160,6 +221,11 @@ bool Controller::Impl::ProcessInputEvents() OnSelectAllEvent(); break; } + case Event::SELECT_NONE: + { + OnSelectNoneEvent(); + break; + } } } } @@ -167,7 +233,7 @@ bool Controller::Impl::ProcessInputEvents() if( mEventData->mUpdateCursorPosition || mEventData->mUpdateHighlightBox ) { - NotifyImfManager(); + NotifyInputMethodContext(); } // The cursor must also be repositioned after inserts into the model @@ -222,10 +288,25 @@ bool Controller::Impl::ProcessInputEvents() if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) ) { - CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo; + if( mEventData->mIsLeftHandleSelected && mEventData->mIsRightHandleSelected ) + { + CursorInfo& infoLeft = leftHandleInfo; + + const Vector2 currentCursorPositionLeft( infoLeft.primaryPosition.x, infoLeft.lineOffset ); + ScrollToMakePositionVisible( currentCursorPositionLeft, infoLeft.lineHeight ); + + CursorInfo& infoRight = rightHandleInfo; - const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset ); - ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight ); + const Vector2 currentCursorPositionRight( infoRight.primaryPosition.x, infoRight.lineOffset ); + ScrollToMakePositionVisible( currentCursorPositionRight, infoRight.lineHeight ); + } + else + { + CursorInfo& info = mEventData->mIsLeftHandleSelected ? leftHandleInfo : rightHandleInfo; + + const Vector2 currentCursorPosition( info.primaryPosition.x, info.lineOffset ); + ScrollToMakePositionVisible( currentCursorPosition, info.lineHeight ); + } } } @@ -256,6 +337,8 @@ bool Controller::Impl::ProcessInputEvents() mEventData->mUpdateLeftSelectionPosition = false; mEventData->mUpdateRightSelectionPosition = false; mEventData->mUpdateHighlightBox = false; + mEventData->mIsLeftHandleSelected = false; + mEventData->mIsRightHandleSelected = false; } mEventData->mScrollAfterUpdatePosition = false; @@ -274,7 +357,7 @@ bool Controller::Impl::ProcessInputEvents() const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u; // Retrieve the style from the style runs stored in the logical model. - mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle ); + mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle ); // Compare if the input style has changed. const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle ); @@ -299,9 +382,9 @@ bool Controller::Impl::ProcessInputEvents() return decoratorUpdated; } -void Controller::Impl::NotifyImfManager() +void Controller::Impl::NotifyInputMethodContext() { - if( mEventData && mEventData->mImfManager ) + if( mEventData && mEventData->mInputMethodContext ) { CharacterIndex cursorPosition = GetLogicalCursorPosition(); @@ -317,17 +400,17 @@ void Controller::Impl::NotifyImfManager() cursorPosition -= numberOfWhiteSpaces; } - mEventData->mImfManager.SetCursorPosition( cursorPosition ); - mEventData->mImfManager.NotifyCursorPosition(); + mEventData->mInputMethodContext.SetCursorPosition( cursorPosition ); + mEventData->mInputMethodContext.NotifyCursorPosition(); } } -void Controller::Impl::NotifyImfMultiLineStatus() +void Controller::Impl::NotifyInputMethodContextMultiLineStatus() { - if ( mEventData ) + if ( mEventData && mEventData->mInputMethodContext ) { - LayoutEngine::Layout layout = mLayoutEngine.GetLayout(); - mEventData->mImfManager.NotifyTextInputMultiLine( layout == LayoutEngine::MULTI_LINE_BOX ); + Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout(); + mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX ); } } @@ -356,9 +439,9 @@ Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const Length numberOfWhiteSpaces = 0u; // Get the buffer to the text. - Character* utf32CharacterBuffer = mLogicalModel->mText.Begin(); + Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin(); - const Length totalNumberOfCharacters = mLogicalModel->mText.Count(); + const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count(); for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces ) { if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) ) @@ -373,12 +456,12 @@ Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const { // Get the total number of characters. - Length numberOfCharacters = mLogicalModel->mText.Count(); + Length numberOfCharacters = mModel->mLogicalModel->mText.Count(); // Retrieve the text. if( 0u != numberOfCharacters ) { - Utf32ToUtf8( mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text ); + Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text ); } } @@ -389,7 +472,7 @@ void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters ) mTextUpdateInfo.mStartLineIndex = 0u; numberOfCharacters = 0u; - const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count(); + const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count(); if( 0u == numberOfParagraphs ) { mTextUpdateInfo.mParagraphCharacterIndex = 0u; @@ -413,8 +496,8 @@ void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters ) numberOfCharacters = 0u; mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove; - mTextUpdateInfo.mStartGlyphIndex = mVisualModel->mGlyphs.Count(); - mTextUpdateInfo.mStartLineIndex = mVisualModel->mLines.Count() - 1u; + mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count(); + mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u; // Nothing else to do; return; @@ -433,19 +516,19 @@ void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters ) { numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u; } - mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex, - numberOfCharactersToUpdate, - paragraphsToBeUpdated ); + mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex, + numberOfCharactersToUpdate, + paragraphsToBeUpdated ); } if( 0u != paragraphsToBeUpdated.Count() ) { const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() ); - const ParagraphRun& firstParagraph = *( mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex ); + const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex ); mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex; ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u ); - const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex ); + const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex ); if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed. ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph. @@ -453,7 +536,7 @@ void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters ) ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) ) { // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one. - const ParagraphRun& lastParagraph = *( mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u ); + const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u ); numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex; } @@ -464,45 +547,40 @@ void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters ) } mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove; - mTextUpdateInfo.mStartGlyphIndex = *( mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex ); + mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex ); } void Controller::Impl::ClearFullModelData( OperationsMask operations ) { if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) ) { - mLogicalModel->mLineBreakInfo.Clear(); - mLogicalModel->mParagraphInfo.Clear(); - } - - if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) ) - { - mLogicalModel->mLineBreakInfo.Clear(); + mModel->mLogicalModel->mLineBreakInfo.Clear(); + mModel->mLogicalModel->mParagraphInfo.Clear(); } if( NO_OPERATION != ( GET_SCRIPTS & operations ) ) { - mLogicalModel->mScriptRuns.Clear(); + mModel->mLogicalModel->mScriptRuns.Clear(); } if( NO_OPERATION != ( VALIDATE_FONTS & operations ) ) { - mLogicalModel->mFontRuns.Clear(); + mModel->mLogicalModel->mFontRuns.Clear(); } - if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() ) + if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() ) { if( NO_OPERATION != ( BIDI_INFO & operations ) ) { - mLogicalModel->mBidirectionalParagraphInfo.Clear(); - mLogicalModel->mCharacterDirections.Clear(); + mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear(); + mModel->mLogicalModel->mCharacterDirections.Clear(); } if( NO_OPERATION != ( REORDER & operations ) ) { // Free the allocated memory used to store the conversion table in the bidirectional line info run. - for( Vector::Iterator it = mLogicalModel->mBidirectionalLineInfo.Begin(), - endIt = mLogicalModel->mBidirectionalLineInfo.End(); + for( Vector::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(), + endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End(); it != endIt; ++it ) { @@ -511,28 +589,29 @@ void Controller::Impl::ClearFullModelData( OperationsMask operations ) free( bidiLineInfo.visualToLogicalMap ); bidiLineInfo.visualToLogicalMap = NULL; } - mLogicalModel->mBidirectionalLineInfo.Clear(); + mModel->mLogicalModel->mBidirectionalLineInfo.Clear(); } } if( NO_OPERATION != ( SHAPE_TEXT & operations ) ) { - mVisualModel->mGlyphs.Clear(); - mVisualModel->mGlyphsToCharacters.Clear(); - mVisualModel->mCharactersToGlyph.Clear(); - mVisualModel->mCharactersPerGlyph.Clear(); - mVisualModel->mGlyphsPerCharacter.Clear(); - mVisualModel->mGlyphPositions.Clear(); + mModel->mVisualModel->mGlyphs.Clear(); + mModel->mVisualModel->mGlyphsToCharacters.Clear(); + mModel->mVisualModel->mCharactersToGlyph.Clear(); + mModel->mVisualModel->mCharactersPerGlyph.Clear(); + mModel->mVisualModel->mGlyphsPerCharacter.Clear(); + mModel->mVisualModel->mGlyphPositions.Clear(); } if( NO_OPERATION != ( LAYOUT & operations ) ) { - mVisualModel->mLines.Clear(); + mModel->mVisualModel->mLines.Clear(); } if( NO_OPERATION != ( COLOR & operations ) ) { - mVisualModel->mColorIndices.Clear(); + mModel->mVisualModel->mColorIndices.Clear(); + mModel->mVisualModel->mBackgroundColorIndices.Clear(); } } @@ -543,24 +622,15 @@ void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, Chara if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) ) { // Clear the line break info. - LineBreakInfo* lineBreakInfoBuffer = mLogicalModel->mLineBreakInfo.Begin(); + LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin(); - mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex, - lineBreakInfoBuffer + endIndexPlusOne ); + mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex, + lineBreakInfoBuffer + endIndexPlusOne ); // Clear the paragraphs. ClearCharacterRuns( startIndex, endIndex, - mLogicalModel->mParagraphInfo ); - } - - if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) ) - { - // Clear the word break info. - WordBreakInfo* wordBreakInfoBuffer = mLogicalModel->mWordBreakInfo.Begin(); - - mLogicalModel->mWordBreakInfo.Erase( wordBreakInfoBuffer + startIndex, - wordBreakInfoBuffer + endIndexPlusOne ); + mModel->mLogicalModel->mParagraphInfo ); } if( NO_OPERATION != ( GET_SCRIPTS & operations ) ) @@ -568,7 +638,7 @@ void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, Chara // Clear the scripts. ClearCharacterRuns( startIndex, endIndex, - mLogicalModel->mScriptRuns ); + mModel->mLogicalModel->mScriptRuns ); } if( NO_OPERATION != ( VALIDATE_FONTS & operations ) ) @@ -576,36 +646,36 @@ void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, Chara // Clear the fonts. ClearCharacterRuns( startIndex, endIndex, - mLogicalModel->mFontRuns ); + mModel->mLogicalModel->mFontRuns ); } - if( 0u != mLogicalModel->mBidirectionalParagraphInfo.Count() ) + if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() ) { if( NO_OPERATION != ( BIDI_INFO & operations ) ) { // Clear the bidirectional paragraph info. ClearCharacterRuns( startIndex, endIndex, - mLogicalModel->mBidirectionalParagraphInfo ); + mModel->mLogicalModel->mBidirectionalParagraphInfo ); // Clear the character's directions. - CharacterDirection* characterDirectionsBuffer = mLogicalModel->mCharacterDirections.Begin(); + CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin(); - mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex, - characterDirectionsBuffer + endIndexPlusOne ); + mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex, + characterDirectionsBuffer + endIndexPlusOne ); } if( NO_OPERATION != ( REORDER & operations ) ) { - uint32_t startRemoveIndex = mLogicalModel->mBidirectionalLineInfo.Count(); + uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count(); uint32_t endRemoveIndex = startRemoveIndex; ClearCharacterRuns( startIndex, endIndex, - mLogicalModel->mBidirectionalLineInfo, + mModel->mLogicalModel->mBidirectionalLineInfo, startRemoveIndex, endRemoveIndex ); - BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mLogicalModel->mBidirectionalLineInfo.Begin(); + BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(); // Free the allocated memory used to store the conversion table in the bidirectional line info run. for( Vector::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex, @@ -619,8 +689,8 @@ void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, Chara bidiLineInfo.visualToLogicalMap = NULL; } - mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex, - bidirectionalLineInfoBuffer + endRemoveIndex ); + mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex, + bidirectionalLineInfoBuffer + endRemoveIndex ); } } } @@ -631,8 +701,8 @@ void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, Character const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex; // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers. - GlyphIndex* charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin(); - Length* glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin(); + GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin(); + Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin(); const GlyphIndex endGlyphIndexPlusOne = *( charactersToGlyphBuffer + endIndex ) + *( glyphsPerCharacterBuffer + endIndex ); const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex; @@ -641,7 +711,7 @@ void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, Character { // Update the character to glyph indices. for( Vector::Iterator it = charactersToGlyphBuffer + endIndexPlusOne, - endIt = charactersToGlyphBuffer + mVisualModel->mCharactersToGlyph.Count(); + endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count(); it != endIt; ++it ) { @@ -650,23 +720,23 @@ void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, Character } // Clear the character to glyph conversion table. - mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex, - charactersToGlyphBuffer + endIndexPlusOne ); + mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex, + charactersToGlyphBuffer + endIndexPlusOne ); // Clear the glyphs per character table. - mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex, - glyphsPerCharacterBuffer + endIndexPlusOne ); + mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex, + glyphsPerCharacterBuffer + endIndexPlusOne ); // Clear the glyphs buffer. - GlyphInfo* glyphsBuffer = mVisualModel->mGlyphs.Begin(); - mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, - glyphsBuffer + endGlyphIndexPlusOne ); + GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin(); + mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, + glyphsBuffer + endGlyphIndexPlusOne ); - CharacterIndex* glyphsToCharactersBuffer = mVisualModel->mGlyphsToCharacters.Begin(); + CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin(); // Update the glyph to character indices. for( Vector::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne, - endIt = glyphsToCharactersBuffer + mVisualModel->mGlyphsToCharacters.Count(); + endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count(); it != endIt; ++it ) { @@ -675,55 +745,62 @@ void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, Character } // Clear the glyphs to characters buffer. - mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex, - glyphsToCharactersBuffer + endGlyphIndexPlusOne ); + mModel->mVisualModel->mGlyphsToCharacters.Erase( glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex, + glyphsToCharactersBuffer + endGlyphIndexPlusOne ); // Clear the characters per glyph buffer. - Length* charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin(); - mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex, - charactersPerGlyphBuffer + endGlyphIndexPlusOne ); + Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin(); + mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex, + charactersPerGlyphBuffer + endGlyphIndexPlusOne ); // Clear the positions buffer. - Vector2* positionsBuffer = mVisualModel->mGlyphPositions.Begin(); - mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex, - positionsBuffer + endGlyphIndexPlusOne ); + Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin(); + mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex, + positionsBuffer + endGlyphIndexPlusOne ); } if( NO_OPERATION != ( LAYOUT & operations ) ) { // Clear the lines. - uint32_t startRemoveIndex = mVisualModel->mLines.Count(); + uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count(); uint32_t endRemoveIndex = startRemoveIndex; ClearCharacterRuns( startIndex, endIndex, - mVisualModel->mLines, + mModel->mVisualModel->mLines, startRemoveIndex, endRemoveIndex ); // Will update the glyph runs. - startRemoveIndex = mVisualModel->mLines.Count(); + startRemoveIndex = mModel->mVisualModel->mLines.Count(); endRemoveIndex = startRemoveIndex; ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex, endGlyphIndexPlusOne - 1u, - mVisualModel->mLines, + mModel->mVisualModel->mLines, startRemoveIndex, endRemoveIndex ); // Set the line index from where to insert the new laid-out lines. mTextUpdateInfo.mStartLineIndex = startRemoveIndex; - LineRun* linesBuffer = mVisualModel->mLines.Begin(); - mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex, - linesBuffer + endRemoveIndex ); + LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin(); + mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex, + linesBuffer + endRemoveIndex ); } if( NO_OPERATION != ( COLOR & operations ) ) { - if( 0u != mVisualModel->mColorIndices.Count() ) + if( 0u != mModel->mVisualModel->mColorIndices.Count() ) + { + ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin(); + mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, + colorIndexBuffer + endGlyphIndexPlusOne ); + } + + if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() ) { - ColorIndex* colorIndexBuffer = mVisualModel->mColorIndices.Begin(); - mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, - colorIndexBuffer + endGlyphIndexPlusOne ); + ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin(); + mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, + backgroundColorIndexBuffer + endGlyphIndexPlusOne ); } } } @@ -746,9 +823,9 @@ void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex } // The estimated number of lines. Used to avoid reallocations when layouting. - mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() ); + mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() ); - mVisualModel->ClearCaches(); + mModel->mVisualModel->ClearCaches(); } bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) @@ -764,8 +841,16 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) return false; } - Vector& utf32Characters = mLogicalModel->mText; + Vector& srcCharacters = mModel->mLogicalModel->mText; + Vector displayCharacters; + bool useHiddenText = false; + if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText) + { + mHiddenInput->Substitute( srcCharacters,displayCharacters ); + useHiddenText = true; + } + Vector& utf32Characters = useHiddenText ? displayCharacters : srcCharacters; const Length numberOfCharacters = utf32Characters.Count(); // Index to the first character of the first paragraph to be updated. @@ -774,6 +859,36 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) Length paragraphCharacters = 0u; CalculateTextUpdateIndices( paragraphCharacters ); + + // Check whether the indices for updating the text is valid + if ( numberOfCharacters > 0u && + ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters || + mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) ) + { + std::string currentText; + Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText ); + + DALI_LOG_ERROR( "Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n" ); + DALI_LOG_ERROR( "Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str() ); + + // Dump mTextUpdateInfo + DALI_LOG_ERROR( "Dump mTextUpdateInfo:\n" ); + DALI_LOG_ERROR( " mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex ); + DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove ); + DALI_LOG_ERROR( " mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd ); + DALI_LOG_ERROR( " mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters ); + DALI_LOG_ERROR( " mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex ); + DALI_LOG_ERROR( " mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters ); + DALI_LOG_ERROR( " mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex ); + DALI_LOG_ERROR( " mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex ); + DALI_LOG_ERROR( " mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines ); + DALI_LOG_ERROR( " mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll ); + DALI_LOG_ERROR( " mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded ); + DALI_LOG_ERROR( " mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph ); + + return false; + } + startIndex = mTextUpdateInfo.mParagraphCharacterIndex; if( mTextUpdateInfo.mClearAll || @@ -787,7 +902,7 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) // Whether the model is updated. bool updated = false; - Vector& lineBreakInfo = mLogicalModel->mLineBreakInfo; + Vector& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo; const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters; if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) ) @@ -804,29 +919,16 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) lineBreakInfo ); // Create the paragraph info. - mLogicalModel->CreateParagraphInfo( startIndex, - requestedNumberOfCharacters ); - updated = true; - } - - Vector& wordBreakInfo = mLogicalModel->mWordBreakInfo; - if( NO_OPERATION != ( GET_WORD_BREAKS & operations ) ) - { - // Retrieves the word break info. The word break info is used to layout the text (where to wrap the text in lines). - wordBreakInfo.Resize( numberOfCharacters, TextAbstraction::WORD_NO_BREAK ); - - SetWordBreakInfo( utf32Characters, - startIndex, - requestedNumberOfCharacters, - wordBreakInfo ); + mModel->mLogicalModel->CreateParagraphInfo( startIndex, + requestedNumberOfCharacters ); updated = true; } const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations ); const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations ); - Vector& scripts = mLogicalModel->mScriptRuns; - Vector& validFonts = mLogicalModel->mFontRuns; + Vector& scripts = mModel->mLogicalModel->mScriptRuns; + Vector& validFonts = mModel->mLogicalModel->mFontRuns; if( getScripts || validateFonts ) { @@ -846,15 +948,34 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) if( validateFonts ) { // Validate the fonts set through the mark-up string. - Vector& fontDescriptionRuns = mLogicalModel->mFontDescriptionRuns; + Vector& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns; // Get the default font's description. TextAbstraction::FontDescription defaultFontDescription; TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; - if( NULL != mFontDefaults ) + + if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) ) + { + // If the placeholder font is set specifically, only placeholder font is changed. + defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription; + if( mEventData->mPlaceholderFont->sizeDefined ) + { + defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u; + } + } + else if( NULL != mFontDefaults ) { + // Set the normal font and the placeholder font. defaultFontDescription = mFontDefaults->mFontDescription; - defaultPointSize = mFontDefaults->mDefaultPointSize * 64u; + + if( mTextFitEnabled ) + { + defaultPointSize = mFontDefaults->mFitPointSize * 64u; + } + else + { + defaultPointSize = mFontDefaults->mDefaultPointSize * 64u; + } } // Validates the fonts. If there is a character with no assigned font it sets a default one. @@ -873,10 +994,10 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) Vector mirroredUtf32Characters; bool textMirrored = false; - const Length numberOfParagraphs = mLogicalModel->mParagraphInfo.Count(); + const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count(); if( NO_OPERATION != ( BIDI_INFO & operations ) ) { - Vector& bidirectionalInfo = mLogicalModel->mBidirectionalParagraphInfo; + Vector& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo; bidirectionalInfo.Reserve( numberOfParagraphs ); // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts. @@ -885,12 +1006,14 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) lineBreakInfo, startIndex, requestedNumberOfCharacters, - bidirectionalInfo ); + bidirectionalInfo, + mModel->mMatchSystemLanguageDirection, + mLayoutDirection ); if( 0u != bidirectionalInfo.Count() ) { // Only set the character directions if there is right to left characters. - Vector& directions = mLogicalModel->mCharacterDirections; + Vector& directions = mModel->mLogicalModel->mCharacterDirections; GetCharactersDirection( bidirectionalInfo, numberOfCharacters, startIndex, @@ -910,14 +1033,14 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) else { // There is no right to left characters. Clear the directions vector. - mLogicalModel->mCharacterDirections.Clear(); + mModel->mLogicalModel->mCharacterDirections.Clear(); } updated = true; } - Vector& glyphs = mVisualModel->mGlyphs; - Vector& glyphsToCharactersMap = mVisualModel->mGlyphsToCharacters; - Vector& charactersPerGlyph = mVisualModel->mCharactersPerGlyph; + Vector& glyphs = mModel->mVisualModel->mGlyphs; + Vector& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters; + Vector& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph; Vector newParagraphGlyphs; newParagraphGlyphs.Reserve( numberOfParagraphs ); @@ -939,8 +1062,8 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) newParagraphGlyphs ); // Create the 'number of glyphs' per character and the glyph to character conversion tables. - mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters ); - mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters ); + mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters ); + mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters ); updated = true; } @@ -964,44 +1087,164 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) updated = true; } + if( ( NULL != mEventData ) && + mEventData->mPreEditFlag && + ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) ) + { + Dali::InputMethodContext::PreEditAttributeDataContainer attrs; + mEventData->mInputMethodContext.GetPreeditStyle( attrs ); + Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE; + + // Check the type of preedit and run it. + for( Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++ ) + { + Dali::InputMethodContext::PreeditAttributeData attrData = *it; + DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex ); + type = attrData.preeditType; + + // Check the number of commit characters for the start position. + unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength; + Length numberOfIndices = attrData.endIndex - attrData.startIndex; + + switch( type ) + { + case Dali::InputMethodContext::PreeditStyle::UNDERLINE: + { + // Add the underline for the pre-edit text. + GlyphRun underlineRun; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.numberOfGlyphs = numberOfIndices; + mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + break; + } + case Dali::InputMethodContext::PreeditStyle::REVERSE: + { + Vector4 textColor = mModel->mVisualModel->GetTextColor(); + ColorRun backgroundColorRun; + backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; + backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; + backgroundColorRun.color = textColor; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + + Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor(); + Vector colorRuns; + colorRuns.Resize( 1u ); + ColorRun& colorRun = *( colorRuns.Begin() ); + colorRun.color = backgroundColor; + colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; + colorRun.characterRun.numberOfCharacters = numberOfIndices; + + mModel->mLogicalModel->mColorRuns.PushBack( colorRun ); + break; + } + case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT: + { + ColorRun backgroundColorRun; + backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; + backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; + backgroundColorRun.color = LIGHT_BLUE; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + break; + } + case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1: + { + // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together. + ColorRun backgroundColorRun; + backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; + backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; + backgroundColorRun.color = BACKGROUND_SUB4; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + + GlyphRun underlineRun; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.numberOfGlyphs = numberOfIndices; + mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + break; + } + case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2: + { + // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together. + ColorRun backgroundColorRun; + backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; + backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; + backgroundColorRun.color = BACKGROUND_SUB5; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + + GlyphRun underlineRun; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.numberOfGlyphs = numberOfIndices; + mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + break; + } + case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3: + { + // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together. + ColorRun backgroundColorRun; + backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; + backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; + backgroundColorRun.color = BACKGROUND_SUB6; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + + GlyphRun underlineRun; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.numberOfGlyphs = numberOfIndices; + mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + break; + } + case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4: + { + // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together. + ColorRun backgroundColorRun; + backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; + backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; + backgroundColorRun.color = BACKGROUND_SUB7; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + + GlyphRun underlineRun; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.numberOfGlyphs = numberOfIndices; + mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + break; + } + case Dali::InputMethodContext::PreeditStyle::NONE: + default: + { + break; + } + } + } + attrs.Clear(); + updated = true; + } + if( NO_OPERATION != ( COLOR & operations ) ) { // Set the color runs in glyphs. - SetColorSegmentationInfo( mLogicalModel->mColorRuns, - mVisualModel->mCharactersToGlyph, - mVisualModel->mGlyphsPerCharacter, + SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns, + mModel->mVisualModel->mCharactersToGlyph, + mModel->mVisualModel->mGlyphsPerCharacter, + startIndex, + mTextUpdateInfo.mStartGlyphIndex, + requestedNumberOfCharacters, + mModel->mVisualModel->mColors, + mModel->mVisualModel->mColorIndices ); + + // Set the background color runs in glyphs. + SetColorSegmentationInfo( mModel->mLogicalModel->mBackgroundColorRuns, + mModel->mVisualModel->mCharactersToGlyph, + mModel->mVisualModel->mGlyphsPerCharacter, startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters, - mVisualModel->mColors, - mVisualModel->mColorIndices ); + mModel->mVisualModel->mBackgroundColors, + mModel->mVisualModel->mBackgroundColorIndices ); updated = true; } - if( ( NULL != mEventData ) && - mEventData->mPreEditFlag && - ( 0u != mVisualModel->mCharactersToGlyph.Count() ) ) - { - // Add the underline for the pre-edit text. - const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin(); - const Length* const glyphsPerCharacterBuffer = mVisualModel->mGlyphsPerCharacter.Begin(); - - const GlyphIndex glyphStart = *( charactersToGlyphBuffer + mEventData->mPreEditStartPosition ); - const CharacterIndex lastPreEditCharacter = mEventData->mPreEditStartPosition + ( ( mEventData->mPreEditLength > 0u ) ? mEventData->mPreEditLength - 1u : 0u ); - const Length numberOfGlyphsLastCharacter = *( glyphsPerCharacterBuffer + lastPreEditCharacter ); - const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + lastPreEditCharacter ) + ( numberOfGlyphsLastCharacter > 1u ? numberOfGlyphsLastCharacter - 1u : 0u ); - - GlyphRun underlineRun; - underlineRun.glyphIndex = glyphStart; - underlineRun.numberOfGlyphs = 1u + glyphEnd - glyphStart; - - // TODO: At the moment the underline runs are only for pre-edit. - mVisualModel->mUnderlineRuns.PushBack( underlineRun ); - } // The estimated number of lines. Used to avoid reallocations when layouting. - mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mVisualModel->mLines.Count(), mLogicalModel->mParagraphInfo.Count() ); + mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() ); // Set the previous number of characters for the next time the text is updated. mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters; @@ -1097,30 +1340,49 @@ float Controller::Impl::GetDefaultFontLineHeight() void Controller::Impl::OnCursorKeyEvent( const Event& event ) { - if( NULL == mEventData ) + if( NULL == mEventData || !IsShowingRealText() ) { // Nothing to do if there is no text input. return; } int keyCode = event.p1.mInt; + bool isShiftModifier = event.p2.mBool; + + CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition; if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) { if( mEventData->mPrimaryCursorPosition > 0u ) { - mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u ); + if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() ) + { + mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); + } + else + { + mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u ); + } } } else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) { - if( mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition ) + if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition ) { - mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition ); + if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() ) + { + mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); + } + else + { + mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition ); + } } } - else if( Dali::DALI_KEY_CURSOR_UP == keyCode ) + else if( Dali::DALI_KEY_CURSOR_UP == keyCode && !isShiftModifier ) { + // Ignore Shift-Up for text selection for now. + // Get first the line index of the current cursor position index. CharacterIndex characterIndex = 0u; @@ -1129,31 +1391,34 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event ) characterIndex = mEventData->mPrimaryCursorPosition - 1u; } - const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex ); + const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex ); + const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex ); - if( lineIndex > 0u ) - { - // Retrieve the cursor position info. - CursorInfo cursorInfo; - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); + // Retrieve the cursor position info. + CursorInfo cursorInfo; + GetCursorPosition( mEventData->mPrimaryCursorPosition, + cursorInfo ); - // Get the line above. - const LineRun& line = *( mVisualModel->mLines.Begin() + ( lineIndex - 1u ) ); + // Get the line above. + const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + previousLineIndex ); - // Get the next hit 'y' point. - const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender ); + // Get the next hit 'y' point. + const float hitPointY = cursorInfo.lineOffset - 0.5f * ( line.ascender - line.descender ); - // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index. - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel, - mLogicalModel, - mMetrics, - mEventData->mCursorHookPositionX, - hitPointY ); - } + // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index. + bool matchedCharacter = false; + mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, + mModel->mLogicalModel, + mMetrics, + mEventData->mCursorHookPositionX, + hitPointY, + CharacterHitTest::TAP, + matchedCharacter ); } - else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode ) + else if( Dali::DALI_KEY_CURSOR_DOWN == keyCode && !isShiftModifier ) { + // Ignore Shift-Down for text selection for now. + // Get first the line index of the current cursor position index. CharacterIndex characterIndex = 0u; @@ -1162,9 +1427,9 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event ) characterIndex = mEventData->mPrimaryCursorPosition - 1u; } - const LineIndex lineIndex = mVisualModel->GetLineOfCharacter( characterIndex ); + const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex ); - if( lineIndex + 1u < mVisualModel->mLines.Count() ) + if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() ) { // Retrieve the cursor position info. CursorInfo cursorInfo; @@ -1172,21 +1437,81 @@ void Controller::Impl::OnCursorKeyEvent( const Event& event ) cursorInfo ); // Get the line below. - const LineRun& line = *( mVisualModel->mLines.Begin() + lineIndex + 1u ); + const LineRun& line = *( mModel->mVisualModel->mLines.Begin() + lineIndex + 1u ); // Get the next hit 'y' point. const float hitPointY = cursorInfo.lineOffset + cursorInfo.lineHeight + 0.5f * ( line.ascender - line.descender ); // Use the cursor hook position 'x' and the next hit 'y' position to calculate the new cursor index. - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel, - mLogicalModel, + bool matchedCharacter = false; + mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, + mModel->mLogicalModel, mMetrics, mEventData->mCursorHookPositionX, - hitPointY ); + hitPointY, + CharacterHitTest::TAP, + matchedCharacter ); + } + } + + if ( !isShiftModifier && mEventData->mState != EventData::SELECTING ) + { + // Update selection position after moving the cursor + mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition; + mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; + } + + if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag ) + { + // Handle text selection + bool selecting = false; + + if ( Dali::DALI_KEY_CURSOR_LEFT == keyCode || Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) + { + // Shift-Left/Right to select the text + int cursorPositionDelta = mEventData->mPrimaryCursorPosition - previousPrimaryCursorPosition; + if ( cursorPositionDelta > 0 || mEventData->mRightSelectionPosition > 0u ) // Check the boundary + { + mEventData->mRightSelectionPosition += cursorPositionDelta; + } + selecting = true; + } + else if ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ) + { + // Show no grab handles and text highlight if Shift-Up/Down pressed but no selected text + selecting = true; + } + + if ( selecting ) + { + // Notify the cursor position to the InputMethodContext. + if( mEventData->mInputMethodContext ) + { + mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition ); + mEventData->mInputMethodContext.NotifyCursorPosition(); + } + + ChangeState( EventData::SELECTING ); + + mEventData->mUpdateLeftSelectionPosition = true; + mEventData->mUpdateRightSelectionPosition = true; + mEventData->mUpdateGrabHandlePosition = true; + mEventData->mUpdateHighlightBox = true; + + // Hide the text selection popup if select the text using keyboard instead of moving grab handles + if( mEventData->mGrabHandlePopupEnabled ) + { + mEventData->mDecorator->SetPopupActive( false ); + } } } + else + { + // Handle normal cursor move + ChangeState( EventData::EDITING ); + mEventData->mUpdateCursorPosition = true; + } - mEventData->mUpdateCursorPosition = true; mEventData->mUpdateInputStyle = true; mEventData->mScrollAfterUpdatePosition = true; } @@ -1202,17 +1527,21 @@ void Controller::Impl::OnTapEvent( const Event& event ) if( IsShowingRealText() ) { // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mScrollPosition.x; - const float yPosition = event.p3.mFloat - mScrollPosition.y; + const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; + const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; // Keep the tap 'x' position. Used to move the cursor. mEventData->mCursorHookPositionX = xPosition; - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mVisualModel, - mLogicalModel, + // Whether to touch point hits on a glyph. + bool matchedCharacter = false; + mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, + mModel->mLogicalModel, mMetrics, xPosition, - yPosition ); + yPosition, + CharacterHitTest::TAP, + matchedCharacter ); // When the cursor position is changing, delay cursor blinking mEventData->mDecorator->DelayCursorBlink(); @@ -1222,16 +1551,34 @@ void Controller::Impl::OnTapEvent( const Event& event ) mEventData->mPrimaryCursorPosition = 0u; } + // Update selection position after tapping + mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition; + mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; + mEventData->mUpdateCursorPosition = true; mEventData->mUpdateGrabHandlePosition = true; mEventData->mScrollAfterUpdatePosition = true; mEventData->mUpdateInputStyle = true; - // Notify the cursor position to the imf manager. - if( mEventData->mImfManager ) + // Notify the cursor position to the InputMethodContext. + if( mEventData->mInputMethodContext ) + { + mEventData->mInputMethodContext.SetCursorPosition( mEventData->mPrimaryCursorPosition ); + mEventData->mInputMethodContext.NotifyCursorPosition(); + } + } + else if( 2u == tapCount ) + { + if( mEventData->mSelectionEnabled ) { - mEventData->mImfManager.SetCursorPosition( mEventData->mPrimaryCursorPosition ); - mEventData->mImfManager.NotifyCursorPosition(); + // Convert from control's coords to text's coords. + const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; + const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; + + // Calculates the logical position from the x,y coords. + RepositionSelectionHandles( xPosition, + yPosition, + mEventData->mDoubleTapAction ); } } } @@ -1254,25 +1601,24 @@ void Controller::Impl::OnPanEvent( const Event& event ) return; } - const int state = event.p1.mInt; - + const GestureState state = static_cast( event.p1.mInt ); switch( state ) { - case Gesture::Started: + case GestureState::STARTED: { // Will remove the cursor, handles or text's popup, ... ChangeState( EventData::TEXT_PANNING ); break; } - case Gesture::Continuing: + case GestureState::CONTINUING: { - const Vector2& layoutSize = mVisualModel->GetLayoutSize(); - const Vector2 currentScroll = mScrollPosition; + const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); + const Vector2 currentScroll = mModel->mScrollPosition; if( isHorizontalScrollEnabled ) { const float displacementX = event.p2.mFloat; - mScrollPosition.x += displacementX; + mModel->mScrollPosition.x += displacementX; ClampHorizontalScroll( layoutSize ); } @@ -1280,16 +1626,16 @@ void Controller::Impl::OnPanEvent( const Event& event ) if( isVerticalScrollEnabled ) { const float displacementY = event.p3.mFloat; - mScrollPosition.y += displacementY; + mModel->mScrollPosition.y += displacementY; ClampVerticalScroll( layoutSize ); } - mEventData->mDecorator->UpdatePositions( mScrollPosition - currentScroll ); + mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll ); break; } - case Gesture::Finished: - case Gesture::Cancelled: // FALLTHROUGH + case GestureState::FINISHED: + case GestureState::CANCELLED: // FALLTHROUGH { // Will go back to the previous state to show the cursor, handles, the text's popup, ... ChangeState( mEventData->mPreviousState ); @@ -1304,10 +1650,25 @@ void Controller::Impl::OnLongPressEvent( const Event& event ) { DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" ); - if( EventData::EDITING == mEventData->mState ) + if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) ) { - ChangeState ( EventData::EDITING_WITH_POPUP ); + ChangeState( EventData::EDITING_WITH_POPUP ); mEventData->mDecoratorUpdated = true; + mEventData->mUpdateInputStyle = true; + } + else + { + if( mEventData->mSelectionEnabled ) + { + // Convert from control's coords to text's coords. + const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; + const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; + + // Calculates the logical position from the x,y coords. + RepositionSelectionHandles( xPosition, + yPosition, + mEventData->mLongPressAction ); + } } } @@ -1326,15 +1687,18 @@ void Controller::Impl::OnHandleEvent( const Event& event ) if( HANDLE_PRESSED == state ) { // Convert from decorator's coords to text's coords. - const float xPosition = event.p2.mFloat - mScrollPosition.x; - const float yPosition = event.p3.mFloat - mScrollPosition.y; + const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; + const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; // Need to calculate the handle's new position. - const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mVisualModel, - mLogicalModel, + bool matchedCharacter = false; + const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, + mModel->mLogicalModel, mMetrics, xPosition, - yPosition ); + yPosition, + CharacterHitTest::SCROLL, + matchedCharacter ); if( Event::GRAB_HANDLE_EVENT == event.type ) { @@ -1371,6 +1735,7 @@ void Controller::Impl::OnHandleEvent( const Event& event ) // Will define the order to scroll the text to match the handle position. mEventData->mIsLeftHandleSelected = true; + mEventData->mIsRightHandleSelected = false; } else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) { @@ -1391,6 +1756,7 @@ void Controller::Impl::OnHandleEvent( const Event& event ) // Will define the order to scroll the text to match the handle position. mEventData->mIsLeftHandleSelected = false; + mEventData->mIsRightHandleSelected = true; } } // end ( HANDLE_PRESSED == state ) else if( ( HANDLE_RELEASED == state ) || @@ -1400,14 +1766,17 @@ void Controller::Impl::OnHandleEvent( const Event& event ) if( handleStopScrolling || isSmoothHandlePanEnabled ) { // Convert from decorator's coords to text's coords. - const float xPosition = event.p2.mFloat - mScrollPosition.x; - const float yPosition = event.p3.mFloat - mScrollPosition.y; + const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; + const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - handlePosition = Text::GetClosestCursorIndex( mVisualModel, - mLogicalModel, + bool matchedCharacter = false; + handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, + mModel->mLogicalModel, mMetrics, xPosition, - yPosition ); + yPosition, + CharacterHitTest::SCROLL, + matchedCharacter ); } if( Event::GRAB_HANDLE_EVENT == event.type ) @@ -1471,17 +1840,17 @@ void Controller::Impl::OnHandleEvent( const Event& event ) { const float xSpeed = event.p2.mFloat; const float ySpeed = event.p3.mFloat; - const Vector2& layoutSize = mVisualModel->GetLayoutSize(); - const Vector2 currentScrollPosition = mScrollPosition; + const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); + const Vector2 currentScrollPosition = mModel->mScrollPosition; - mScrollPosition.x += xSpeed; - mScrollPosition.y += ySpeed; + mModel->mScrollPosition.x += xSpeed; + mModel->mScrollPosition.y += ySpeed; ClampHorizontalScroll( layoutSize ); ClampVerticalScroll( layoutSize ); bool endOfScroll = false; - if( Vector2::ZERO == ( currentScrollPosition - mScrollPosition ) ) + if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) ) { // Notify the decorator there is no more text to scroll. // The decorator won't send more scroll events. @@ -1506,7 +1875,7 @@ void Controller::Impl::OnHandleEvent( const Event& event ) if( mEventData->mDecorator->IsHorizontalScrollEnabled() ) { // Position the grag handle close to either the left or right edge. - position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width; + position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width; } if( mEventData->mDecorator->IsVerticalScrollEnabled() ) @@ -1514,16 +1883,19 @@ void Controller::Impl::OnHandleEvent( const Event& event ) position.x = mEventData->mCursorHookPositionX; // Position the grag handle close to either the top or bottom edge. - position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height; + position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height; } // Get the new handle position. // The grab handle's position is in decorator's coords. Need to transforms to text's coords. - const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel, - mLogicalModel, + bool matchedCharacter = false; + const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, + mModel->mLogicalModel, mMetrics, - position.x - mScrollPosition.x, - position.y - mScrollPosition.y ); + position.x - mModel->mScrollPosition.x, + position.y - mModel->mScrollPosition.y, + CharacterHitTest::SCROLL, + matchedCharacter ); if( mEventData->mPrimaryCursorPosition != handlePosition ) { @@ -1547,7 +1919,7 @@ void Controller::Impl::OnHandleEvent( const Event& event ) if( mEventData->mDecorator->IsHorizontalScrollEnabled() ) { // Position the selection handle close to either the left or right edge. - position.x = scrollRightDirection ? 0.f : mVisualModel->mControlSize.width; + position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width; } if( mEventData->mDecorator->IsVerticalScrollEnabled() ) @@ -1555,16 +1927,19 @@ void Controller::Impl::OnHandleEvent( const Event& event ) position.x = mEventData->mCursorHookPositionX; // Position the grag handle close to either the top or bottom edge. - position.y = scrollBottomDirection ? 0.f : mVisualModel->mControlSize.height; + position.y = scrollBottomDirection ? 0.f : mModel->mVisualModel->mControlSize.height; } // Get the new handle position. // The selection handle's position is in decorator's coords. Need to transform to text's coords. - const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mVisualModel, - mLogicalModel, + bool matchedCharacter = false; + const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, + mModel->mLogicalModel, mMetrics, - position.x - mScrollPosition.x, - position.y - mScrollPosition.y ); + position.x - mModel->mScrollPosition.x, + position.y - mModel->mScrollPosition.y, + CharacterHitTest::SCROLL, + matchedCharacter ); if( leftSelectionHandleEvent ) { @@ -1612,12 +1987,13 @@ void Controller::Impl::OnSelectEvent( const Event& event ) if( mEventData->mSelectionEnabled ) { // Convert from control's coords to text's coords. - const float xPosition = event.p2.mFloat - mScrollPosition.x; - const float yPosition = event.p3.mFloat - mScrollPosition.y; + const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; + const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; // Calculates the logical position from the x,y coords. RepositionSelectionHandles( xPosition, - yPosition ); + yPosition, + Controller::NoTextTap::HIGHLIGHT ); } } @@ -1633,18 +2009,85 @@ void Controller::Impl::OnSelectAllEvent() if( mEventData->mSelectionEnabled ) { - ChangeState( EventData::SELECTING ); + // Calculates the logical position from the start. + RepositionSelectionHandles( 0.f - mModel->mScrollPosition.x, + 0.f - mModel->mScrollPosition.y, + Controller::NoTextTap::HIGHLIGHT ); mEventData->mLeftSelectionPosition = 0u; - mEventData->mRightSelectionPosition = mLogicalModel->mText.Count(); + mEventData->mRightSelectionPosition = mModel->mLogicalModel->mText.Count(); + } +} + +void Controller::Impl::OnSelectNoneEvent() +{ + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectNoneEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false"); + if( NULL == mEventData ) + { + // Nothing to do if there is no text. + return; + } + + if( mEventData->mSelectionEnabled && mEventData->mState == EventData::SELECTING) + { + mEventData->mPrimaryCursorPosition = 0u; + mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; + ChangeState( EventData::INACTIVE ); + mEventData->mUpdateCursorPosition = true; + mEventData->mUpdateInputStyle = true; mEventData->mScrollAfterUpdatePosition = true; - mEventData->mUpdateLeftSelectionPosition = true; - mEventData->mUpdateRightSelectionPosition = true; - mEventData->mUpdateHighlightBox = true; } } +void Controller::Impl::SetTextSelectionRange(const uint32_t *pStart, const uint32_t *pEnd) +{ + if( nullptr == mEventData ) + { + // Nothing to do if there is no text. + return; + } + + if( mEventData->mSelectionEnabled && (pStart || pEnd)) + { + uint32_t length = static_cast(mModel->mLogicalModel->mText.Count()); + + if (pStart) + { + mEventData->mLeftSelectionPosition = std::min(*pStart, length); + } + if (pEnd) + { + mEventData->mRightSelectionPosition = std::min(*pEnd, length); + } + + if (mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition) + { + ChangeState( EventData::EDITING ); + mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition; + mEventData->mUpdateCursorPosition = true; + } + else + { + ChangeState( EventData::SELECTING ); + mEventData->mUpdateHighlightBox = true; + } + } +} + +Uint32Pair Controller::Impl::GetTextSelectionRange() const +{ + Uint32Pair range; + + if( mEventData ) + { + range.first = mEventData->mLeftSelectionPosition; + range.second = mEventData->mRightSelectionPosition; + } + + return range; +} + void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval ) { if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition ) @@ -1660,7 +2103,7 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition; const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText; - Vector& utf32Characters = mLogicalModel->mText; + Vector& utf32Characters = mModel->mLogicalModel->mText; const Length numberOfCharacters = utf32Characters.Count(); // Validate the start and end selection points @@ -1676,7 +2119,7 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete currentInputStyle.Copy( mEventData->mInputStyle ); // Set as input style the style of the first deleted character. - mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle ); + mModel->mLogicalModel->RetrieveStyle( startOfSelectedText, mEventData->mInputStyle ); // Compare if the input style has changed. const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle ); @@ -1688,11 +2131,21 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask ); } - mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast( lengthOfSelectedText ) ); + mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast( lengthOfSelectedText ) ); // Mark the paragraphs to be updated. - mTextUpdateInfo.mCharacterIndex = startOfSelectedText; - mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() ) + { + mTextUpdateInfo.mCharacterIndex = 0; + mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters; + mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText; + mTextUpdateInfo.mClearAll = true; + } + else + { + mTextUpdateInfo.mCharacterIndex = startOfSelectedText; + mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; + } // Delete text between handles Vector::Iterator first = utf32Characters.Begin() + startOfSelectedText; @@ -1747,11 +2200,11 @@ void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending ) ChangeState( EventData::EDITING ); } -void Controller::Impl::GetTextFromClipboard( unsigned int itemIndex, std::string& retrievedString ) +void Controller::Impl::RequestGetTextFromClipboard() { if ( mClipboard ) { - retrievedString = mClipboard.GetItem( itemIndex ); + mClipboard.RequestItem(); } } @@ -1763,20 +2216,22 @@ void Controller::Impl::RepositionSelectionHandles() if( selectionStart == selectionEnd ) { // Nothing to select if handles are in the same place. + // So, deactive Highlight box. + mEventData->mDecorator->SetHighlightActive( false ); return; } mEventData->mDecorator->ClearHighlights(); - 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(); - const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin(); - const CharacterIndex* const glyphToCharacterBuffer = mVisualModel->mGlyphsToCharacters.Begin(); - const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mLogicalModel->mCharacterDirections.Count() ) ? mLogicalModel->mCharacterDirections.Begin() : NULL; + const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin(); + const Length* const glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin(); + const GlyphInfo* const glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin(); + const Vector2* const positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin(); + const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin(); + const CharacterIndex* const glyphToCharacterBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin(); + const CharacterDirection* const modelCharacterDirectionsBuffer = ( 0u != mModel->mLogicalModel->mCharacterDirections.Count() ) ? mModel->mLogicalModel->mCharacterDirections.Begin() : NULL; - const bool isLastCharacter = selectionEnd >= mLogicalModel->mText.Count(); + const bool isLastCharacter = selectionEnd >= mModel->mLogicalModel->mText.Count(); const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) ); const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) ); @@ -1798,14 +2253,14 @@ void Controller::Impl::RepositionSelectionHandles() const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u ); // Get the lines where the glyphs are laid-out. - const LineRun* lineRun = mVisualModel->mLines.Begin(); + const LineRun* lineRun = mModel->mVisualModel->mLines.Begin(); LineIndex lineIndex = 0u; Length numberOfLines = 0u; - mVisualModel->GetNumberOfLines( glyphStart, - 1u + glyphEnd - glyphStart, - lineIndex, - numberOfLines ); + mModel->mVisualModel->GetNumberOfLines( glyphStart, + 1u + glyphEnd - glyphStart, + lineIndex, + numberOfLines ); const LineIndex firstLineIndex = lineIndex; // Create the structure to store some selection box info. @@ -1825,11 +2280,11 @@ void Controller::Impl::RepositionSelectionHandles() // Retrieve the first line and get the line's vertical offset, the line's height and the index to the last glyph. // The line's vertical offset of all the lines before the line where the first glyph is laid-out. - selectionBoxInfo->lineOffset = CalculateLineOffset( mVisualModel->mLines, + selectionBoxInfo->lineOffset = CalculateLineOffset( mModel->mVisualModel->mLines, firstLineIndex ); // Transform to decorator's (control) coords. - selectionBoxInfo->lineOffset += mScrollPosition.y; + selectionBoxInfo->lineOffset += mModel->mScrollPosition.y; lineRun += firstLineIndex; @@ -1841,11 +2296,11 @@ void Controller::Impl::RepositionSelectionHandles() // Check if the first glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code. const Length numberOfCharactersStart = *( charactersPerGlyphBuffer + glyphStart ); - bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionStart ) ); + bool splitStartGlyph = ( numberOfCharactersStart > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionStart ) ); // Check if the last glyph is a ligature that must be broken like Latin ff, fi, or Arabic ï»», etc which needs special code. const Length numberOfCharactersEnd = *( charactersPerGlyphBuffer + glyphEnd ); - bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mLogicalModel->GetScript( selectionEndMinusOne ) ); + bool splitEndGlyph = ( glyphStart != glyphEnd ) && ( numberOfCharactersEnd > 1u ) && HasLigatureMustBreak( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) ); // The number of quads of the selection box. const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u ); @@ -1878,7 +2333,7 @@ void Controller::Impl::RepositionSelectionHandles() // Calculate the number of characters selected. const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - interGlyphIndex ); - quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + glyphAdvance * static_cast( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex ); + quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + glyphAdvance * static_cast( isCurrentRightToLeft ? ( numberOfCharactersStart - interGlyphIndex - numberOfCharacters ) : interGlyphIndex ); quad.y = selectionBoxInfo->lineOffset; quad.z = quad.x + static_cast( numberOfCharacters ) * glyphAdvance; quad.w = selectionBoxInfo->lineOffset + selectionBoxInfo->lineHeight; @@ -1909,7 +2364,7 @@ void Controller::Impl::RepositionSelectionHandles() const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex; - quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast( numberOfCharacters ) ) : 0.f ); + quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x + ( isCurrentRightToLeft ? ( glyphAdvance * static_cast( numberOfCharacters ) ) : 0.f ); quad.y = selectionBoxInfo->lineOffset; quad.z = quad.x + static_cast( interGlyphIndex ) * glyphAdvance; quad.w = quad.y + selectionBoxInfo->lineHeight; @@ -1926,7 +2381,7 @@ void Controller::Impl::RepositionSelectionHandles() continue; } - quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mScrollPosition.x; + quad.x = lineRun->alignmentOffset + position.x - glyph.xBearing + mModel->mScrollPosition.x; quad.y = selectionBoxInfo->lineOffset; quad.z = quad.x + glyph.advance; quad.w = quad.y + selectionBoxInfo->lineHeight; @@ -1942,15 +2397,15 @@ void Controller::Impl::RepositionSelectionHandles() // Whether to retrieve the next line. if( index == lastGlyphOfLine ) { - // Retrieve the next line. - ++lineRun; - - // Get the last glyph of the new line. - lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u; - ++lineIndex; if( lineIndex < firstLineIndex + numberOfLines ) { + // Retrieve the next line. + ++lineRun; + + // Get the last glyph of the new line. + lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u; + // Keep the offset and height of the current selection box. const float currentLineOffset = selectionBoxInfo->lineOffset; const float currentLineHeight = selectionBoxInfo->lineHeight; @@ -1991,7 +2446,7 @@ void Controller::Impl::RepositionSelectionHandles() if( 1u < numberOfLines ) { // Boxify the first line. - lineRun = mVisualModel->mLines.Begin() + firstLineIndex; + lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex; const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() ); bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection ); @@ -2017,7 +2472,7 @@ void Controller::Impl::RepositionSelectionHandles() { quad.x = firstSelectionBoxLineInfo.maxX; quad.y = firstSelectionBoxLineInfo.lineOffset; - quad.z = mVisualModel->mControlSize.width; + quad.z = mModel->mVisualModel->mControlSize.width; quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight; // Boxify at the end of the line. @@ -2026,7 +2481,7 @@ void Controller::Impl::RepositionSelectionHandles() ++actualNumberOfQuads; // Update the size of the highlighted text. - maxHighlightX = mVisualModel->mControlSize.width; + maxHighlightX = mModel->mVisualModel->mControlSize.width; } // Boxify the central lines. @@ -2050,7 +2505,7 @@ void Controller::Impl::RepositionSelectionHandles() quad.x = info.maxX; quad.y = info.lineOffset; - quad.z = mVisualModel->mControlSize.width; + quad.z = mModel->mVisualModel->mControlSize.width; quad.w = info.lineOffset + info.lineHeight; mEventData->mDecorator->AddHighlight( actualNumberOfQuads, @@ -2060,11 +2515,11 @@ void Controller::Impl::RepositionSelectionHandles() // Update the size of the highlighted text. minHighlightX = 0.f; - maxHighlightX = mVisualModel->mControlSize.width; + maxHighlightX = mModel->mVisualModel->mControlSize.width; } // Boxify the last line. - lineRun = mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u; + lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u; const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u ); boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection ); @@ -2090,7 +2545,7 @@ void Controller::Impl::RepositionSelectionHandles() { quad.x = lastSelectionBoxLineInfo.maxX; quad.y = lastSelectionBoxLineInfo.lineOffset; - quad.z = mVisualModel->mControlSize.width; + quad.z = mModel->mVisualModel->mControlSize.width; quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight; // Boxify at the end of the line. @@ -2099,7 +2554,7 @@ void Controller::Impl::RepositionSelectionHandles() ++actualNumberOfQuads; // Update the size of the highlighted text. - maxHighlightX = mVisualModel->mControlSize.width; + maxHighlightX = mModel->mVisualModel->mControlSize.width; } } @@ -2114,7 +2569,7 @@ void Controller::Impl::RepositionSelectionHandles() const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() ); highLightPosition.y = firstSelectionBoxLineInfo.lineOffset; - mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize ); + mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast( mModel->GetOutlineWidth() ) ); if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() ) { @@ -2122,33 +2577,30 @@ void Controller::Impl::RepositionSelectionHandles() GetCursorPosition( mEventData->mLeftSelectionPosition, primaryCursorInfo ); - const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mScrollPosition; + const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition; mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, primaryPosition.x, - primaryCursorInfo.lineOffset + mScrollPosition.y, + primaryCursorInfo.lineOffset + mModel->mScrollPosition.y, primaryCursorInfo.lineHeight ); CursorInfo secondaryCursorInfo; GetCursorPosition( mEventData->mRightSelectionPosition, secondaryCursorInfo ); - const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mScrollPosition; + const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition; mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, secondaryPosition.x, - secondaryCursorInfo.lineOffset + mScrollPosition.y, + secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y, secondaryCursorInfo.lineHeight ); } - // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection - mEventData->mPrimaryCursorPosition = ( indicesSwapped ) ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition; - // Set the flag to update the decorator. mEventData->mDecoratorUpdated = true; } -void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY ) +void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action ) { if( NULL == mEventData ) { @@ -2162,8 +2614,8 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY return; } - const Length numberOfGlyphs = mVisualModel->mGlyphs.Count(); - const Length numberOfLines = mVisualModel->mLines.Count(); + const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count(); + const Length numberOfLines = mModel->mVisualModel->mLines.Count(); if( ( 0 == numberOfGlyphs ) || ( 0 == numberOfLines ) ) { @@ -2174,16 +2626,18 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY // Find which word was selected CharacterIndex selectionStart( 0 ); CharacterIndex selectionEnd( 0 ); - const bool indicesFound = FindSelectionIndices( mVisualModel, - mLogicalModel, + CharacterIndex noTextHitIndex( 0 ); + const bool characterHit = FindSelectionIndices( mModel->mVisualModel, + mModel->mLogicalModel, mMetrics, visualX, visualY, selectionStart, - selectionEnd ); + selectionEnd, + noTextHitIndex ); DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd ); - if( indicesFound ) + if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) ) { ChangeState( EventData::SELECTING ); @@ -2194,14 +2648,33 @@ void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY mEventData->mUpdateRightSelectionPosition = true; mEventData->mUpdateHighlightBox = true; + // It may happen an InputMethodContext commit event arrives before the selection event + // if the InputMethodContext is in pre-edit state. The commit event will set the + // mEventData->mUpdateCursorPosition flag to true. If it's not set back + // to false, the highlight box won't be updated. + mEventData->mUpdateCursorPosition = false; + mEventData->mScrollAfterUpdatePosition = ( mEventData->mLeftSelectionPosition != mEventData->mRightSelectionPosition ); + + // Cursor to be positioned at end of selection so if selection interrupted and edit mode restarted the cursor will be at end of selection + mEventData->mPrimaryCursorPosition = std::max( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition ); } - else + else if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action ) { // Nothing to select. i.e. a white space, out of bounds - ChangeState( EventData::EDITING ); + ChangeState( EventData::EDITING_WITH_POPUP ); + + mEventData->mPrimaryCursorPosition = noTextHitIndex; - mEventData->mPrimaryCursorPosition = selectionEnd; + mEventData->mUpdateCursorPosition = true; + mEventData->mUpdateGrabHandlePosition = true; + mEventData->mScrollAfterUpdatePosition = true; + mEventData->mUpdateInputStyle = true; + } + else if( Controller::NoTextTap::NO_ACTION == action ) + { + // Nothing to select. i.e. a white space, out of bounds + mEventData->mPrimaryCursorPosition = noTextHitIndex; mEventData->mUpdateCursorPosition = true; mEventData->mUpdateGrabHandlePosition = true; @@ -2239,7 +2712,7 @@ void Controller::Impl::SetPopupButtons() } else if( EventData::EDITING_WITH_POPUP == mEventData->mState ) { - if( mLogicalModel->mText.Count() && !IsShowingPlaceholderText() ) + if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() ) { buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL ); } @@ -2289,7 +2762,6 @@ void Controller::Impl::ChangeState( EventData::State newState ) mEventData->mDecorator->SetHighlightActive( false ); mEventData->mDecorator->SetPopupActive( false ); mEventData->mDecoratorUpdated = true; - HideClipboard(); break; } case EventData::INTERRUPTED: @@ -2300,7 +2772,6 @@ void Controller::Impl::ChangeState( EventData::State newState ) mEventData->mDecorator->SetHighlightActive( false ); mEventData->mDecorator->SetPopupActive( false ); mEventData->mDecoratorUpdated = true; - HideClipboard(); break; } case EventData::SELECTING: @@ -2308,8 +2779,11 @@ void Controller::Impl::ChangeState( EventData::State newState ) mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE ); mEventData->mDecorator->StopCursorBlink(); mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true ); + if ( mEventData->mGrabHandleEnabled ) + { + mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true ); + mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true ); + } mEventData->mDecorator->SetHighlightActive( true ); if( mEventData->mGrabHandlePopupEnabled ) { @@ -2336,7 +2810,6 @@ void Controller::Impl::ChangeState( EventData::State newState ) mEventData->mDecorator->SetPopupActive( false ); } mEventData->mDecoratorUpdated = true; - HideClipboard(); break; } case EventData::EDITING_WITH_POPUP: @@ -2354,7 +2827,7 @@ void Controller::Impl::ChangeState( EventData::State newState ) mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); mEventData->mDecorator->SetHighlightActive( false ); } - else + else if ( mEventData->mGrabHandleEnabled ) { mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); } @@ -2363,7 +2836,6 @@ void Controller::Impl::ChangeState( EventData::State newState ) SetPopupButtons(); mEventData->mDecorator->SetPopupActive( true ); } - HideClipboard(); mEventData->mDecoratorUpdated = true; break; } @@ -2377,7 +2849,10 @@ void Controller::Impl::ChangeState( EventData::State newState ) mEventData->mDecorator->StartCursorBlink(); } // Grab handle is not shown until a tap is received whilst EDITING - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + if ( mEventData->mGrabHandleEnabled ) + { + mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + } mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); mEventData->mDecorator->SetHighlightActive( false ); @@ -2386,7 +2861,6 @@ void Controller::Impl::ChangeState( EventData::State newState ) mEventData->mDecorator->SetPopupActive( false ); } mEventData->mDecoratorUpdated = true; - HideClipboard(); break; } case EventData::SELECTION_HANDLE_PANNING: @@ -2394,8 +2868,11 @@ void Controller::Impl::ChangeState( EventData::State newState ) mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE ); mEventData->mDecorator->StopCursorBlink(); mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true ); + if ( mEventData->mGrabHandleEnabled ) + { + mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true ); + mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true ); + } mEventData->mDecorator->SetHighlightActive( true ); if( mEventData->mGrabHandlePopupEnabled ) { @@ -2413,7 +2890,10 @@ void Controller::Impl::ChangeState( EventData::State newState ) { mEventData->mDecorator->StartCursorBlink(); } - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + if ( mEventData->mGrabHandleEnabled ) + { + mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + } mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); mEventData->mDecorator->SetHighlightActive( false ); @@ -2434,7 +2914,10 @@ void Controller::Impl::ChangeState( EventData::State newState ) mEventData->mDecorator->StartCursorBlink(); } - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + if ( mEventData->mGrabHandleEnabled ) + { + mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + } mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); mEventData->mDecorator->SetHighlightActive( false ); @@ -2444,7 +2927,6 @@ void Controller::Impl::ChangeState( EventData::State newState ) SetPopupButtons(); mEventData->mDecorator->SetPopupActive( true ); } - HideClipboard(); mEventData->mDecoratorUpdated = true; break; } @@ -2488,21 +2970,41 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical, cursorInfo.lineHeight = GetDefaultFontLineHeight(); cursorInfo.primaryCursorHeight = cursorInfo.lineHeight; - switch( mLayoutEngine.GetHorizontalAlignment() ) + bool isRTL = false; + if( mModel->mMatchSystemLanguageDirection ) + { + isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT; + } + + switch( mModel->mHorizontalAlignment ) { - case LayoutEngine::HORIZONTAL_ALIGN_BEGIN: + case Text::HorizontalAlignment::BEGIN : { - cursorInfo.primaryPosition.x = 0.f; + if( isRTL ) + { + cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth(); + } + else + { + cursorInfo.primaryPosition.x = 0.f; + } break; } - case LayoutEngine::HORIZONTAL_ALIGN_CENTER: + case Text::HorizontalAlignment::CENTER: { - cursorInfo.primaryPosition.x = floorf( 0.5f * mVisualModel->mControlSize.width ); + cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width ); break; } - case LayoutEngine::HORIZONTAL_ALIGN_END: + case Text::HorizontalAlignment::END: { - cursorInfo.primaryPosition.x = mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth(); + if( isRTL ) + { + cursorInfo.primaryPosition.x = 0.f; + } + else + { + cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth(); + } break; } } @@ -2511,13 +3013,25 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical, return; } - Text::GetCursorPosition( mVisualModel, - mLogicalModel, - mMetrics, - logical, + const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() ); + GetCursorPositionParameters parameters; + parameters.visualModel = mModel->mVisualModel; + parameters.logicalModel = mModel->mLogicalModel; + parameters.metrics = mMetrics; + parameters.logical = logical; + parameters.isMultiline = isMultiLine; + + Text::GetCursorPosition( parameters, cursorInfo ); - if( LayoutEngine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() ) + // Adds Outline offset. + const float outlineWidth = static_cast( mModel->GetOutlineWidth() ); + cursorInfo.primaryPosition.x += outlineWidth; + cursorInfo.primaryPosition.y += outlineWidth; + cursorInfo.secondaryPosition.x += outlineWidth; + cursorInfo.secondaryPosition.y += outlineWidth; + + if( isMultiLine ) { // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control. @@ -2529,7 +3043,7 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical, cursorInfo.primaryPosition.x = 0.f; } - const float edgeWidth = mVisualModel->mControlSize.width - static_cast( mEventData->mDecorator->GetCursorWidth() ); + const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast( mEventData->mDecorator->GetCursorWidth() ); if( cursorInfo.primaryPosition.x > edgeWidth ) { cursorInfo.primaryPosition.x = edgeWidth; @@ -2547,18 +3061,18 @@ CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition; - const GlyphIndex* const charactersToGlyphBuffer = mVisualModel->mCharactersToGlyph.Begin(); - const Length* const charactersPerGlyphBuffer = mVisualModel->mCharactersPerGlyph.Begin(); + const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin(); + const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin(); GlyphIndex glyphIndex = *( charactersToGlyphBuffer + index ); Length numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex ); if( numberOfCharacters > 1u ) { - const Script script = mLogicalModel->GetScript( index ); + const Script script = mModel->mLogicalModel->GetScript( index ); if( HasLigatureMustBreak( script ) ) { - // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ... + // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ... numberOfCharacters = 1u; } } @@ -2596,7 +3110,9 @@ void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo ) return; } - const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition; + const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition; + + mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset ); // Sets the cursor position. mEventData->mDecorator->SetPosition( PRIMARY_CURSOR, @@ -2611,18 +3127,18 @@ void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo ) // Sets the grab handle position. mEventData->mDecorator->SetPosition( GRAB_HANDLE, cursorPosition.x, - cursorInfo.lineOffset + mScrollPosition.y, + cursorInfo.lineOffset + mModel->mScrollPosition.y, cursorInfo.lineHeight ); } if( cursorInfo.isSecondaryCursor ) { mEventData->mDecorator->SetPosition( SECONDARY_CURSOR, - cursorInfo.secondaryPosition.x + mScrollPosition.x, - cursorInfo.secondaryPosition.y + mScrollPosition.y, + cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, + cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y, cursorInfo.secondaryCursorHeight, cursorInfo.lineHeight ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mScrollPosition.x, cursorInfo.secondaryPosition.y + mScrollPosition.y ); + DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y ); } // Set which cursors are active according the state. @@ -2654,52 +3170,58 @@ void Controller::Impl::UpdateSelectionHandle( HandleType handleType, return; } - const Vector2 cursorPosition = cursorInfo.primaryPosition + mScrollPosition; + const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition; // Sets the handle's position. mEventData->mDecorator->SetPosition( handleType, cursorPosition.x, - cursorInfo.lineOffset + mScrollPosition.y, + cursorInfo.lineOffset + mModel->mScrollPosition.y, cursorInfo.lineHeight ); // If selection handle at start of the text and other at end of the text then all text is selected. const CharacterIndex startOfSelection = std::min( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition ); const CharacterIndex endOfSelection = std::max ( mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition ); - mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mLogicalModel->mText.Count() ); + mEventData->mAllTextSelected = ( startOfSelection == 0 ) && ( endOfSelection == mModel->mLogicalModel->mText.Count() ); } void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize ) { - // Clamp between -space & 0. + // Clamp between -space & -alignment offset. - if( layoutSize.width > mVisualModel->mControlSize.width ) + if( layoutSize.width > mModel->mVisualModel->mControlSize.width ) { - const float space = ( layoutSize.width - mVisualModel->mControlSize.width ); - mScrollPosition.x = ( mScrollPosition.x < -space ) ? -space : mScrollPosition.x; - mScrollPosition.x = ( mScrollPosition.x > 0.f ) ? 0.f : mScrollPosition.x; + const float space = ( layoutSize.width - mModel->mVisualModel->mControlSize.width ) + mModel->mAlignmentOffset; + mModel->mScrollPosition.x = ( mModel->mScrollPosition.x < -space ) ? -space : mModel->mScrollPosition.x; + mModel->mScrollPosition.x = ( mModel->mScrollPosition.x > -mModel->mAlignmentOffset ) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x; mEventData->mDecoratorUpdated = true; } else { - mScrollPosition.x = 0.f; + mModel->mScrollPosition.x = 0.f; } } void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize ) { + if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() ) + { + // Nothing to do if the text is single line. + return; + } + // Clamp between -space & 0. - if( layoutSize.height > mVisualModel->mControlSize.height ) + if( layoutSize.height > mModel->mVisualModel->mControlSize.height ) { - const float space = ( layoutSize.height - mVisualModel->mControlSize.height ); - mScrollPosition.y = ( mScrollPosition.y < -space ) ? -space : mScrollPosition.y; - mScrollPosition.y = ( mScrollPosition.y > 0.f ) ? 0.f : mScrollPosition.y; + const float space = ( layoutSize.height - mModel->mVisualModel->mControlSize.height ); + mModel->mScrollPosition.y = ( mModel->mScrollPosition.y < -space ) ? -space : mModel->mScrollPosition.y; + mModel->mScrollPosition.y = ( mModel->mScrollPosition.y > 0.f ) ? 0.f : mModel->mScrollPosition.y; mEventData->mDecoratorUpdated = true; } else { - mScrollPosition.y = 0.f; + mModel->mScrollPosition.y = 0.f; } } @@ -2712,28 +3234,31 @@ void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, flo const float positionEndY = position.y + lineHeight; // Transform the position to decorator coords. - const float decoratorPositionBeginX = position.x + mScrollPosition.x; - const float decoratorPositionEndX = positionEndX + mScrollPosition.x; + const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x; + const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x; - const float decoratorPositionBeginY = position.y + mScrollPosition.y; - const float decoratorPositionEndY = positionEndY + mScrollPosition.y; + const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y; + const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y; if( decoratorPositionBeginX < 0.f ) { - mScrollPosition.x = -position.x; + mModel->mScrollPosition.x = -position.x; } - else if( decoratorPositionEndX > mVisualModel->mControlSize.width ) + else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width ) { - mScrollPosition.x = mVisualModel->mControlSize.width - positionEndX; + mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX; } - if( decoratorPositionBeginY < 0.f ) - { - mScrollPosition.y = -position.y; - } - else if( decoratorPositionEndY > mVisualModel->mControlSize.height ) + if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() ) { - mScrollPosition.y = mVisualModel->mControlSize.height - positionEndY; + if( decoratorPositionBeginY < 0.f ) + { + mModel->mScrollPosition.y = -position.y; + } + else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height ) + { + mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY; + } } } @@ -2742,12 +3267,23 @@ void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo ) // Get the current cursor position in decorator coords. const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition( PRIMARY_CURSOR ); + const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( mEventData->mPrimaryCursorPosition ); + + + // Calculate the offset to match the cursor position before the character was deleted. - mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x; - mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset; + mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x; - ClampHorizontalScroll( mVisualModel->GetLayoutSize() ); - ClampVerticalScroll( mVisualModel->GetLayoutSize() ); + //If text control has more than two lines and current line index is not last, calculate scrollpositionY + if( mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() -1u ) + { + const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset( PRIMARY_CURSOR ); + mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset; + } + + + ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() ); + ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() ); // Makes the new cursor position visible if needed. ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight ); @@ -2755,7 +3291,174 @@ void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo ) void Controller::Impl::RequestRelayout() { - mControlInterface.RequestTextRelayout(); + if( NULL != mControlInterface ) + { + mControlInterface->RequestTextRelayout(); + } +} + +Actor Controller::Impl::CreateBackgroundActor() +{ + // NOTE: Currently we only support background color for one line left-to-right text, + // so the following calculation is based on one line left-to-right text only! + + Actor actor; + + Length numberOfGlyphs = mView.GetNumberOfGlyphs(); + if( numberOfGlyphs > 0u ) + { + Vector glyphs; + glyphs.Resize( numberOfGlyphs ); + + Vector positions; + positions.Resize( numberOfGlyphs ); + + // Get the line where the glyphs are laid-out. + const LineRun* lineRun = mModel->mVisualModel->mLines.Begin(); + float alignmentOffset = lineRun->alignmentOffset; + numberOfGlyphs = mView.GetGlyphs( glyphs.Begin(), + positions.Begin(), + alignmentOffset, + 0u, + numberOfGlyphs ); + + glyphs.Resize( numberOfGlyphs ); + positions.Resize( numberOfGlyphs ); + + const GlyphInfo* const glyphsBuffer = glyphs.Begin(); + const Vector2* const positionsBuffer = positions.Begin(); + + BackgroundMesh mesh; + mesh.mVertices.Reserve( 4u * glyphs.Size() ); + mesh.mIndices.Reserve( 6u * glyphs.Size() ); + + const Vector2 textSize = mView.GetLayoutSize(); + + const float offsetX = textSize.width * 0.5f; + const float offsetY = textSize.height * 0.5f; + + const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors(); + const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices(); + const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT; + + Vector4 quad; + uint32_t numberOfQuads = 0u; + + for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i ) + { + const GlyphInfo& glyph = *( glyphsBuffer + i ); + + // Get the background color of the character. + // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT) + const ColorIndex backgroundColorIndex = ( nullptr == backgroundColorsBuffer ) ? 0u : *( backgroundColorIndicesBuffer + i ); + const Vector4& backgroundColor = ( 0u == backgroundColorIndex ) ? defaultBackgroundColor : *( backgroundColorsBuffer + backgroundColorIndex - 1u ); + + // Only create quads for glyphs with a background color + if ( backgroundColor != Color::TRANSPARENT ) + { + const Vector2 position = *( positionsBuffer + i ); + + if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text + { + quad.x = position.x; + quad.y = 0.0f; + quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width ); + quad.w = textSize.height; + } + else if ( i == 0u ) // The first glyph in the whole text + { + quad.x = position.x; + quad.y = 0.0f; + quad.z = quad.x - glyph.xBearing + glyph.advance; + quad.w = textSize.height; + } + else if ( i == glyphSize - 1u ) // The last glyph in the whole text + { + quad.x = position.x - glyph.xBearing; + quad.y = 0.0f; + quad.z = quad.x + std::max( glyph.advance, glyph.xBearing + glyph.width ); + quad.w = textSize.height; + } + else // The glyph in the middle of the text + { + quad.x = position.x - glyph.xBearing; + quad.y = 0.0f; + quad.z = quad.x + glyph.advance; + quad.w = textSize.height; + } + + BackgroundVertex vertex; + + // Top left + vertex.mPosition.x = quad.x - offsetX; + vertex.mPosition.y = quad.y - offsetY; + vertex.mColor = backgroundColor; + mesh.mVertices.PushBack( vertex ); + + // Top right + vertex.mPosition.x = quad.z - offsetX; + vertex.mPosition.y = quad.y - offsetY; + vertex.mColor = backgroundColor; + mesh.mVertices.PushBack( vertex ); + + // Bottom left + vertex.mPosition.x = quad.x - offsetX; + vertex.mPosition.y = quad.w - offsetY; + vertex.mColor = backgroundColor; + mesh.mVertices.PushBack( vertex ); + + // Bottom right + vertex.mPosition.x = quad.z - offsetX; + vertex.mPosition.y = quad.w - offsetY; + vertex.mColor = backgroundColor; + mesh.mVertices.PushBack( vertex ); + + // Six indices in counter clockwise winding + mesh.mIndices.PushBack( 1u + 4 * numberOfQuads ); + mesh.mIndices.PushBack( 0u + 4 * numberOfQuads ); + mesh.mIndices.PushBack( 2u + 4 * numberOfQuads ); + mesh.mIndices.PushBack( 2u + 4 * numberOfQuads ); + mesh.mIndices.PushBack( 3u + 4 * numberOfQuads ); + mesh.mIndices.PushBack( 1u + 4 * numberOfQuads ); + + numberOfQuads++; + } + } + + // Only create the background actor if there are glyphs with background color + if ( mesh.mVertices.Count() > 0u ) + { + Property::Map quadVertexFormat; + quadVertexFormat[ "aPosition" ] = Property::VECTOR2; + quadVertexFormat[ "aColor" ] = Property::VECTOR4; + + VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat ); + quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() ); + + Geometry quadGeometry = Geometry::New(); + quadGeometry.AddVertexBuffer( quadVertices ); + quadGeometry.SetIndexBuffer( &mesh.mIndices[ 0 ], mesh.mIndices.Size() ); + + if( !mShaderBackground ) + { + mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND ); + } + + Dali::Renderer renderer = Dali::Renderer::New( quadGeometry, mShaderBackground ); + renderer.SetProperty( Dali::Renderer::Property::BLEND_MODE, BlendMode::ON ); + renderer.SetProperty( Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT ); + + actor = Actor::New(); + actor.SetProperty( Dali::Actor::Property::NAME, "TextBackgroundColorActor" ); + actor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT ); + actor.SetProperty( Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT ); + actor.SetProperty( Actor::Property::SIZE, textSize ); + actor.SetProperty( Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR ); + actor.AddRenderer( renderer ); + } + } + + return actor; } } // namespace Text