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=b298bf02d58e3dc44c392fda113494f102ac767f;hp=dd59fc8b74280283cf6b0090a2b4d4a1fa3ccf1d;hb=a2e89745e6dc47b21b9714de87fc230a67addf5e;hpb=01c086f17e2af89f6c6b1bd30a0240da2fda1e75 diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp old mode 100755 new mode 100644 index dd59fc8..b298bf0 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ b/dali-toolkit/internal/text/text-controller-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2021 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. @@ -19,379 +19,133 @@ #include // EXTERNAL INCLUDES -#include -#include #include -#include +#include // INTERNAL INCLUDES #include +#include #include #include #include #include +#include #include #include #include #include +#include +#include +#include #include +#include using namespace Dali; namespace { - -/** - * @brief Struct used to calculate the selection box. - */ -struct SelectionBoxInfo -{ - float lineOffset; - float lineHeight; - float minX; - float maxX; -}; - #if defined(DEBUG_ENABLED) - Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); +Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); #endif -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 + Vector2 mPosition; ///< Vertex posiiton + Vector4 mColor; ///< Vertex color }; struct BackgroundMesh { - Vector< BackgroundVertex > mVertices; ///< container of vertices - Vector< unsigned short > mIndices; ///< container of indices + Vector mVertices; ///< container of vertices + Vector 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 ); +// The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B) +// based on W3C Recommendations (https://www.w3.org/TR/WCAG20/) +const float BRIGHTNESS_THRESHOLD = 0.179f; +const float CONSTANT_R = 0.2126f; +const float CONSTANT_G = 0.7152f; +const float CONSTANT_B = 0.0722f; +const Dali::Vector4 BLACK(0.f, 0.f, 0.f, 1.f); +const Dali::Vector4 WHITE(1.f, 1.f, 1.f, 1.f); +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 { - namespace Toolkit { - namespace Text { - -EventData::EventData( DecoratorPtr decorator, InputMethodContext& inputMethodContext ) -: mDecorator( decorator ), - mInputMethodContext( inputMethodContext ), - mPlaceholderFont( NULL ), +EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext) +: mDecorator(decorator), + mInputMethodContext(inputMethodContext), + mPlaceholderFont(nullptr), mPlaceholderTextActive(), mPlaceholderTextInactive(), - mPlaceholderTextColor( 0.8f, 0.8f, 0.8f, 0.8f ), // This color has been published in the Public API (placeholder-properties.h). + 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 ), - mRightSelectionPosition( 0u ), - mPreEditStartPosition( 0u ), - mPreEditLength( 0u ), - mCursorHookPositionX( 0.f ), - mDoubleTapAction( Controller::NoTextTap::NO_ACTION ), - mLongPressAction( Controller::NoTextTap::SHOW_SELECTION_POPUP ), - mIsShowingPlaceholderText( false ), - mPreEditFlag( false ), - mDecoratorUpdated( false ), - mCursorBlinkEnabled( true ), - 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 ), - mPasswordInput( false ), - mCheckScrollAmount( false ), - mIsPlaceholderPixelSize( false ), - mIsPlaceholderElideEnabled( false ), - mPlaceholderEllipsisFlag( false ), - mShiftSelectionFlag( true ), - mUpdateAlignment( false ) + mPreviousState(INACTIVE), + mState(INACTIVE), + mPrimaryCursorPosition(0u), + mLeftSelectionPosition(0u), + mRightSelectionPosition(0u), + mPreEditStartPosition(0u), + mPreEditLength(0u), + mCursorHookPositionX(0.f), + mDoubleTapAction(Controller::NoTextTap::NO_ACTION), + mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP), + mIsShowingPlaceholderText(false), + mPreEditFlag(false), + mDecoratorUpdated(false), + mCursorBlinkEnabled(true), + 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), + mPasswordInput(false), + mCheckScrollAmount(false), + mIsPlaceholderPixelSize(false), + mIsPlaceholderElideEnabled(false), + mPlaceholderEllipsisFlag(false), + mShiftSelectionFlag(true), + mUpdateAlignment(false), + mEditingEnabled(true) { } -EventData::~EventData() -{} - bool Controller::Impl::ProcessInputEvents() { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::ProcessInputEvents\n" ); - if( NULL == mEventData ) - { - // Nothing to do if there is no text input. - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents no event data\n" ); - return false; - } - - if( mEventData->mDecorator ) - { - for( std::vector::iterator iter = mEventData->mEventQueue.begin(); - iter != mEventData->mEventQueue.end(); - ++iter ) - { - switch( iter->type ) - { - case Event::CURSOR_KEY_EVENT: - { - OnCursorKeyEvent( *iter ); - break; - } - case Event::TAP_EVENT: - { - OnTapEvent( *iter ); - break; - } - case Event::LONG_PRESS_EVENT: - { - OnLongPressEvent( *iter ); - break; - } - case Event::PAN_EVENT: - { - OnPanEvent( *iter ); - break; - } - case Event::GRAB_HANDLE_EVENT: - case Event::LEFT_SELECTION_HANDLE_EVENT: - case Event::RIGHT_SELECTION_HANDLE_EVENT: // Fall through - { - OnHandleEvent( *iter ); - break; - } - case Event::SELECT: - { - OnSelectEvent( *iter ); - break; - } - case Event::SELECT_ALL: - { - OnSelectAllEvent(); - break; - } - case Event::SELECT_NONE: - { - OnSelectNoneEvent(); - break; - } - } - } - } - - if( mEventData->mUpdateCursorPosition || - mEventData->mUpdateHighlightBox ) - { - NotifyInputMethodContext(); - } - - // The cursor must also be repositioned after inserts into the model - if( mEventData->mUpdateCursorPosition ) - { - // Updates the cursor position and scrolls the text to make it visible. - CursorInfo cursorInfo; - // Calculate the cursor position from the new cursor index. - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); - - if( mEventData->mUpdateCursorHookPosition ) - { - // Update the cursor hook position. Used to move the cursor with the keys 'up' and 'down'. - mEventData->mCursorHookPositionX = cursorInfo.primaryPosition.x; - mEventData->mUpdateCursorHookPosition = false; - } - - // Scroll first the text after delete ... - if( mEventData->mScrollAfterDelete ) - { - ScrollTextToMatchCursor( cursorInfo ); - } - - // ... then, text can be scrolled to make the cursor visible. - if( mEventData->mScrollAfterUpdatePosition ) - { - const Vector2 currentCursorPosition( cursorInfo.primaryPosition.x, cursorInfo.lineOffset ); - ScrollToMakePositionVisible( currentCursorPosition, cursorInfo.lineHeight ); - } - mEventData->mScrollAfterUpdatePosition = false; - mEventData->mScrollAfterDelete = false; - - UpdateCursorPosition( cursorInfo ); - - mEventData->mDecoratorUpdated = true; - mEventData->mUpdateCursorPosition = false; - mEventData->mUpdateGrabHandlePosition = false; - } - else - { - CursorInfo leftHandleInfo; - CursorInfo rightHandleInfo; - - if( mEventData->mUpdateHighlightBox ) - { - GetCursorPosition( mEventData->mLeftSelectionPosition, - leftHandleInfo ); - - GetCursorPosition( mEventData->mRightSelectionPosition, - rightHandleInfo ); - - if( mEventData->mScrollAfterUpdatePosition && ( mEventData->mIsLeftHandleSelected ? mEventData->mUpdateLeftSelectionPosition : mEventData->mUpdateRightSelectionPosition ) ) - { - 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 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 ); - } - } - } - - if( mEventData->mUpdateLeftSelectionPosition ) - { - UpdateSelectionHandle( LEFT_SELECTION_HANDLE, - leftHandleInfo ); - - SetPopupButtons(); - mEventData->mDecoratorUpdated = true; - mEventData->mUpdateLeftSelectionPosition = false; - } - - if( mEventData->mUpdateRightSelectionPosition ) - { - UpdateSelectionHandle( RIGHT_SELECTION_HANDLE, - rightHandleInfo ); - - SetPopupButtons(); - mEventData->mDecoratorUpdated = true; - mEventData->mUpdateRightSelectionPosition = false; - } - - if( mEventData->mUpdateHighlightBox ) - { - RepositionSelectionHandles(); - - mEventData->mUpdateLeftSelectionPosition = false; - mEventData->mUpdateRightSelectionPosition = false; - mEventData->mUpdateHighlightBox = false; - mEventData->mIsLeftHandleSelected = false; - mEventData->mIsRightHandleSelected = false; - } - - mEventData->mScrollAfterUpdatePosition = false; - } - - if( mEventData->mUpdateInputStyle ) - { - // Keep a copy of the current input style. - InputStyle currentInputStyle; - currentInputStyle.Copy( mEventData->mInputStyle ); - - // Set the default style first. - RetrieveDefaultInputStyle( mEventData->mInputStyle ); - - // Get the character index from the cursor index. - const CharacterIndex styleIndex = ( mEventData->mPrimaryCursorPosition > 0u ) ? mEventData->mPrimaryCursorPosition - 1u : 0u; - - // Retrieve the style from the style runs stored in the logical model. - mModel->mLogicalModel->RetrieveStyle( styleIndex, mEventData->mInputStyle ); - - // Compare if the input style has changed. - const bool hasInputStyleChanged = !currentInputStyle.Equal( mEventData->mInputStyle ); - - if( hasInputStyleChanged ) - { - const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle ); - // Queue the input style changed signal. - mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask ); - } - - mEventData->mUpdateInputStyle = false; - } - - mEventData->mEventQueue.clear(); - - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::ProcessInputEvents\n" ); - - const bool decoratorUpdated = mEventData->mDecoratorUpdated; - mEventData->mDecoratorUpdated = false; - - return decoratorUpdated; + return ControllerImplEventHandler::ProcessInputEvents(*this); } void Controller::Impl::NotifyInputMethodContext() { - if( mEventData && mEventData->mInputMethodContext ) + if(mEventData && mEventData->mInputMethodContext) { CharacterIndex cursorPosition = GetLogicalCursorPosition(); - const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces( 0u ); + const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u); // Update the cursor position by removing the initial white spaces. - if( cursorPosition < numberOfWhiteSpaces ) + if(cursorPosition < numberOfWhiteSpaces) { cursorPosition = 0u; } @@ -400,17 +154,17 @@ void Controller::Impl::NotifyInputMethodContext() cursorPosition -= numberOfWhiteSpaces; } - mEventData->mInputMethodContext.SetCursorPosition( cursorPosition ); + mEventData->mInputMethodContext.SetCursorPosition(cursorPosition); mEventData->mInputMethodContext.NotifyCursorPosition(); } } void Controller::Impl::NotifyInputMethodContextMultiLineStatus() { - if ( mEventData && mEventData->mInputMethodContext ) + if(mEventData && mEventData->mInputMethodContext) { Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout(); - mEventData->mInputMethodContext.NotifyTextInputMultiLine( layout == Text::Layout::Engine::MULTI_LINE_BOX ); + mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX); } } @@ -418,12 +172,12 @@ CharacterIndex Controller::Impl::GetLogicalCursorPosition() const { CharacterIndex cursorPosition = 0u; - if( mEventData ) + if(mEventData) { - if( ( EventData::SELECTING == mEventData->mState ) || - ( EventData::SELECTION_HANDLE_PANNING == mEventData->mState ) ) + if((EventData::SELECTING == mEventData->mState) || + (EventData::SELECTION_HANDLE_PANNING == mEventData->mState)) { - cursorPosition = std::min( mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition ); + cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition); } else { @@ -434,7 +188,7 @@ CharacterIndex Controller::Impl::GetLogicalCursorPosition() const return cursorPosition; } -Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const +Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const { Length numberOfWhiteSpaces = 0u; @@ -442,9 +196,9 @@ Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin(); const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count(); - for( ; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces ) + for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces) { - if( !TextAbstraction::IsWhiteSpace( *( utf32CharacterBuffer + index ) ) ) + if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index))) { break; } @@ -453,30 +207,30 @@ Length Controller::Impl::GetNumberOfWhiteSpaces( CharacterIndex index ) const return numberOfWhiteSpaces; } -void Controller::Impl::GetText( CharacterIndex index, std::string& text ) const +void Controller::Impl::GetText(CharacterIndex index, std::string& text) const { // Get the total number of characters. Length numberOfCharacters = mModel->mLogicalModel->mText.Count(); // Retrieve the text. - if( 0u != numberOfCharacters ) + if(0u != numberOfCharacters) { - Utf32ToUtf8( mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text ); + Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text); } } -void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters ) +void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters) { mTextUpdateInfo.mParagraphCharacterIndex = 0u; - mTextUpdateInfo.mStartGlyphIndex = 0u; - mTextUpdateInfo.mStartLineIndex = 0u; - numberOfCharacters = 0u; + mTextUpdateInfo.mStartGlyphIndex = 0u; + mTextUpdateInfo.mStartLineIndex = 0u; + numberOfCharacters = 0u; const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count(); - if( 0u == numberOfParagraphs ) + if(0u == numberOfParagraphs) { mTextUpdateInfo.mParagraphCharacterIndex = 0u; - numberOfCharacters = 0u; + numberOfCharacters = 0u; mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove; @@ -486,57 +240,57 @@ void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters ) // Find the paragraphs to be updated. Vector paragraphsToBeUpdated; - if( mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters ) + if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters) { // Text is being added at the end of the current text. - if( mTextUpdateInfo.mIsLastCharacterNewParagraph ) + if(mTextUpdateInfo.mIsLastCharacterNewParagraph) { // Text is being added in a new paragraph after the last character of the text. - mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters; - numberOfCharacters = 0u; + mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters; + numberOfCharacters = 0u; mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove; mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count(); - mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u; + mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u; // Nothing else to do; return; } - paragraphsToBeUpdated.PushBack( numberOfParagraphs - 1u ); + paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u); } else { Length numberOfCharactersToUpdate = 0u; - if( mTextUpdateInfo.mFullRelayoutNeeded ) + if(mTextUpdateInfo.mFullRelayoutNeeded) { numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters; } else { - numberOfCharactersToUpdate = ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u; + numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u; } - mModel->mLogicalModel->FindParagraphs( mTextUpdateInfo.mCharacterIndex, - numberOfCharactersToUpdate, - paragraphsToBeUpdated ); + mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex, + numberOfCharactersToUpdate, + paragraphsToBeUpdated); } - if( 0u != paragraphsToBeUpdated.Count() ) + if(0u != paragraphsToBeUpdated.Count()) { - const ParagraphRunIndex firstParagraphIndex = *( paragraphsToBeUpdated.Begin() ); - const ParagraphRun& firstParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex ); - mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex; + const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin()); + const ParagraphRun& firstParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex); + mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex; - ParagraphRunIndex lastParagraphIndex = *( paragraphsToBeUpdated.End() - 1u ); - const ParagraphRun& lastParagraph = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex ); + ParagraphRunIndex lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u); + const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex); - if( ( mTextUpdateInfo.mNumberOfCharactersToRemove > 0u ) && // Some character are removed. - ( lastParagraphIndex < numberOfParagraphs - 1u ) && // There is a next paragraph. - ( ( lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters ) == // The last removed character is the new paragraph character. - ( mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove ) ) ) + if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) && // Some character are removed. + (lastParagraphIndex < numberOfParagraphs - 1u) && // There is a next paragraph. + ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character. + (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 = *( mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u ); + const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u); numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex; } @@ -547,53 +301,53 @@ void Controller::Impl::CalculateTextUpdateIndices( Length& numberOfCharacters ) } mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove; - mTextUpdateInfo.mStartGlyphIndex = *( mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex ); + mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex); } -void Controller::Impl::ClearFullModelData( OperationsMask operations ) +void Controller::Impl::ClearFullModelData(OperationsMask operations) { - if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) ) + if(NO_OPERATION != (GET_LINE_BREAKS & operations)) { mModel->mLogicalModel->mLineBreakInfo.Clear(); mModel->mLogicalModel->mParagraphInfo.Clear(); } - if( NO_OPERATION != ( GET_SCRIPTS & operations ) ) + if(NO_OPERATION != (GET_SCRIPTS & operations)) { mModel->mLogicalModel->mScriptRuns.Clear(); } - if( NO_OPERATION != ( VALIDATE_FONTS & operations ) ) + if(NO_OPERATION != (VALIDATE_FONTS & operations)) { mModel->mLogicalModel->mFontRuns.Clear(); } - if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() ) + if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count()) { - if( NO_OPERATION != ( BIDI_INFO & operations ) ) + if(NO_OPERATION != (BIDI_INFO & operations)) { mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear(); mModel->mLogicalModel->mCharacterDirections.Clear(); } - if( NO_OPERATION != ( REORDER & operations ) ) + 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 = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(), - endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End(); - it != endIt; - ++it ) + for(Vector::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(), + endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End(); + it != endIt; + ++it) { BidirectionalLineInfoRun& bidiLineInfo = *it; - free( bidiLineInfo.visualToLogicalMap ); + free(bidiLineInfo.visualToLogicalMap); bidiLineInfo.visualToLogicalMap = NULL; } mModel->mLogicalModel->mBidirectionalLineInfo.Clear(); } } - if( NO_OPERATION != ( SHAPE_TEXT & operations ) ) + if(NO_OPERATION != (SHAPE_TEXT & operations)) { mModel->mVisualModel->mGlyphs.Clear(); mModel->mVisualModel->mGlyphsToCharacters.Clear(); @@ -603,298 +357,298 @@ void Controller::Impl::ClearFullModelData( OperationsMask operations ) mModel->mVisualModel->mGlyphPositions.Clear(); } - if( NO_OPERATION != ( LAYOUT & operations ) ) + if(NO_OPERATION != (LAYOUT & operations)) { mModel->mVisualModel->mLines.Clear(); } - if( NO_OPERATION != ( COLOR & operations ) ) + if(NO_OPERATION != (COLOR & operations)) { mModel->mVisualModel->mColorIndices.Clear(); mModel->mVisualModel->mBackgroundColorIndices.Clear(); } } -void Controller::Impl::ClearCharacterModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations ) +void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations) { const CharacterIndex endIndexPlusOne = endIndex + 1u; - if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) ) + if(NO_OPERATION != (GET_LINE_BREAKS & operations)) { // Clear the line break info. LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin(); - mModel->mLogicalModel->mLineBreakInfo.Erase( lineBreakInfoBuffer + startIndex, - lineBreakInfoBuffer + endIndexPlusOne ); + mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex, + lineBreakInfoBuffer + endIndexPlusOne); // Clear the paragraphs. - ClearCharacterRuns( startIndex, - endIndex, - mModel->mLogicalModel->mParagraphInfo ); + ClearCharacterRuns(startIndex, + endIndex, + mModel->mLogicalModel->mParagraphInfo); } - if( NO_OPERATION != ( GET_SCRIPTS & operations ) ) + if(NO_OPERATION != (GET_SCRIPTS & operations)) { // Clear the scripts. - ClearCharacterRuns( startIndex, - endIndex, - mModel->mLogicalModel->mScriptRuns ); + ClearCharacterRuns(startIndex, + endIndex, + mModel->mLogicalModel->mScriptRuns); } - if( NO_OPERATION != ( VALIDATE_FONTS & operations ) ) + if(NO_OPERATION != (VALIDATE_FONTS & operations)) { // Clear the fonts. - ClearCharacterRuns( startIndex, - endIndex, - mModel->mLogicalModel->mFontRuns ); + ClearCharacterRuns(startIndex, + endIndex, + mModel->mLogicalModel->mFontRuns); } - if( 0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count() ) + if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count()) { - if( NO_OPERATION != ( BIDI_INFO & operations ) ) + if(NO_OPERATION != (BIDI_INFO & operations)) { // Clear the bidirectional paragraph info. - ClearCharacterRuns( startIndex, - endIndex, - mModel->mLogicalModel->mBidirectionalParagraphInfo ); + ClearCharacterRuns(startIndex, + endIndex, + mModel->mLogicalModel->mBidirectionalParagraphInfo); // Clear the character's directions. CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin(); - mModel->mLogicalModel->mCharacterDirections.Erase( characterDirectionsBuffer + startIndex, - characterDirectionsBuffer + endIndexPlusOne ); + mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex, + characterDirectionsBuffer + endIndexPlusOne); } - if( NO_OPERATION != ( REORDER & operations ) ) + if(NO_OPERATION != (REORDER & operations)) { uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count(); - uint32_t endRemoveIndex = startRemoveIndex; - ClearCharacterRuns( startIndex, - endIndex, - mModel->mLogicalModel->mBidirectionalLineInfo, - startRemoveIndex, - endRemoveIndex ); + uint32_t endRemoveIndex = startRemoveIndex; + ClearCharacterRuns(startIndex, + endIndex, + mModel->mLogicalModel->mBidirectionalLineInfo, + startRemoveIndex, + endRemoveIndex); 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, - endIt = bidirectionalLineInfoBuffer + endRemoveIndex; - it != endIt; - ++it ) + for(Vector::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex, + endIt = bidirectionalLineInfoBuffer + endRemoveIndex; + it != endIt; + ++it) { BidirectionalLineInfoRun& bidiLineInfo = *it; - free( bidiLineInfo.visualToLogicalMap ); + free(bidiLineInfo.visualToLogicalMap); bidiLineInfo.visualToLogicalMap = NULL; } - mModel->mLogicalModel->mBidirectionalLineInfo.Erase( bidirectionalLineInfoBuffer + startRemoveIndex, - bidirectionalLineInfoBuffer + endRemoveIndex ); + mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex, + bidirectionalLineInfoBuffer + endRemoveIndex); } } } -void Controller::Impl::ClearGlyphModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations ) +void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations) { - const CharacterIndex endIndexPlusOne = endIndex + 1u; - const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex; + const CharacterIndex endIndexPlusOne = endIndex + 1u; + 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 = mModel->mVisualModel->mCharactersToGlyph.Begin(); - Length* glyphsPerCharacterBuffer = mModel->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; + const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex); + const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex; - if( NO_OPERATION != ( SHAPE_TEXT & operations ) ) + if(NO_OPERATION != (SHAPE_TEXT & operations)) { // Update the character to glyph indices. - for( Vector::Iterator it = charactersToGlyphBuffer + endIndexPlusOne, - endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count(); - it != endIt; - ++it ) + for(Vector::Iterator it = charactersToGlyphBuffer + endIndexPlusOne, + endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count(); + it != endIt; + ++it) { CharacterIndex& index = *it; index -= numberOfGlyphsRemoved; } // Clear the character to glyph conversion table. - mModel->mVisualModel->mCharactersToGlyph.Erase( charactersToGlyphBuffer + startIndex, - charactersToGlyphBuffer + endIndexPlusOne ); + mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex, + charactersToGlyphBuffer + endIndexPlusOne); // Clear the glyphs per character table. - mModel->mVisualModel->mGlyphsPerCharacter.Erase( glyphsPerCharacterBuffer + startIndex, - glyphsPerCharacterBuffer + endIndexPlusOne ); + mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex, + glyphsPerCharacterBuffer + endIndexPlusOne); // Clear the glyphs buffer. GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin(); - mModel->mVisualModel->mGlyphs.Erase( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, - glyphsBuffer + endGlyphIndexPlusOne ); + mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, + glyphsBuffer + endGlyphIndexPlusOne); CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin(); // Update the glyph to character indices. - for( Vector::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne, - endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count(); - it != endIt; - ++it ) + for(Vector::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne, + endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count(); + it != endIt; + ++it) { CharacterIndex& index = *it; index -= numberOfCharactersRemoved; } // Clear the glyphs to characters buffer. - mModel->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 = mModel->mVisualModel->mCharactersPerGlyph.Begin(); - mModel->mVisualModel->mCharactersPerGlyph.Erase( charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex, - charactersPerGlyphBuffer + endGlyphIndexPlusOne ); + mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex, + charactersPerGlyphBuffer + endGlyphIndexPlusOne); // Clear the positions buffer. Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin(); - mModel->mVisualModel->mGlyphPositions.Erase( positionsBuffer + mTextUpdateInfo.mStartGlyphIndex, - positionsBuffer + endGlyphIndexPlusOne ); + mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex, + positionsBuffer + endGlyphIndexPlusOne); } - if( NO_OPERATION != ( LAYOUT & operations ) ) + if(NO_OPERATION != (LAYOUT & operations)) { // Clear the lines. uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count(); - uint32_t endRemoveIndex = startRemoveIndex; - ClearCharacterRuns( startIndex, - endIndex, - mModel->mVisualModel->mLines, - startRemoveIndex, - endRemoveIndex ); + uint32_t endRemoveIndex = startRemoveIndex; + ClearCharacterRuns(startIndex, + endIndex, + mModel->mVisualModel->mLines, + startRemoveIndex, + endRemoveIndex); // Will update the glyph runs. startRemoveIndex = mModel->mVisualModel->mLines.Count(); - endRemoveIndex = startRemoveIndex; - ClearGlyphRuns( mTextUpdateInfo.mStartGlyphIndex, - endGlyphIndexPlusOne - 1u, - mModel->mVisualModel->mLines, - startRemoveIndex, - endRemoveIndex ); + endRemoveIndex = startRemoveIndex; + ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex, + endGlyphIndexPlusOne - 1u, + mModel->mVisualModel->mLines, + startRemoveIndex, + endRemoveIndex); // Set the line index from where to insert the new laid-out lines. mTextUpdateInfo.mStartLineIndex = startRemoveIndex; LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin(); - mModel->mVisualModel->mLines.Erase( linesBuffer + startRemoveIndex, - linesBuffer + endRemoveIndex ); + mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex, + linesBuffer + endRemoveIndex); } - if( NO_OPERATION != ( COLOR & operations ) ) + if(NO_OPERATION != (COLOR & operations)) { - if( 0u != mModel->mVisualModel->mColorIndices.Count() ) + if(0u != mModel->mVisualModel->mColorIndices.Count()) { ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin(); - mModel->mVisualModel->mColorIndices.Erase( colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, - colorIndexBuffer + endGlyphIndexPlusOne ); + mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, + colorIndexBuffer + endGlyphIndexPlusOne); } - if( 0u != mModel->mVisualModel->mBackgroundColorIndices.Count() ) + if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count()) { ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin(); - mModel->mVisualModel->mBackgroundColorIndices.Erase( backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, - backgroundColorIndexBuffer + endGlyphIndexPlusOne ); + mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, + backgroundColorIndexBuffer + endGlyphIndexPlusOne); } } } -void Controller::Impl::ClearModelData( CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations ) +void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations) { - if( mTextUpdateInfo.mClearAll || - ( ( 0u == startIndex ) && - ( mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u ) ) ) + if(mTextUpdateInfo.mClearAll || + ((0u == startIndex) && + (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u))) { - ClearFullModelData( operations ); + ClearFullModelData(operations); } else { // Clear the model data related with characters. - ClearCharacterModelData( startIndex, endIndex, operations ); + ClearCharacterModelData(startIndex, endIndex, operations); // Clear the model data related with glyphs. - ClearGlyphModelData( startIndex, endIndex, operations ); + ClearGlyphModelData(startIndex, endIndex, operations); } // The estimated number of lines. Used to avoid reallocations when layouting. - mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count() ); + mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count()); mModel->mVisualModel->ClearCaches(); } -bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) +bool Controller::Impl::UpdateModel(OperationsMask operationsRequired) { - DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::UpdateModel\n" ); + DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n"); // Calculate the operations to be done. - const OperationsMask operations = static_cast( mOperationsPending & operationsRequired ); + const OperationsMask operations = static_cast(mOperationsPending & operationsRequired); - if( NO_OPERATION == operations ) + if(NO_OPERATION == operations) { // Nothing to do if no operations are pending and required. return false; } Vector& srcCharacters = mModel->mLogicalModel->mText; - Vector displayCharacters; - bool useHiddenText = false; - if ( mHiddenInput && mEventData != NULL && !mEventData->mIsShowingPlaceholderText) + Vector displayCharacters; + bool useHiddenText = false; + if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText) { - mHiddenInput->Substitute( srcCharacters,displayCharacters ); + mHiddenInput->Substitute(srcCharacters, displayCharacters); useHiddenText = true; } - Vector& utf32Characters = useHiddenText ? displayCharacters : srcCharacters; - const Length numberOfCharacters = utf32Characters.Count(); + Vector& utf32Characters = useHiddenText ? displayCharacters : srcCharacters; + const Length numberOfCharacters = utf32Characters.Count(); // Index to the first character of the first paragraph to be updated. CharacterIndex startIndex = 0u; // Number of characters of the paragraphs to be removed. Length paragraphCharacters = 0u; - CalculateTextUpdateIndices( paragraphCharacters ); + CalculateTextUpdateIndices(paragraphCharacters); // Check whether the indices for updating the text is valid - if ( numberOfCharacters > 0u && - ( mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters || - mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters ) ) + if(numberOfCharacters > 0u && + (mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters || + mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters)) { std::string currentText; - Utf32ToUtf8( mModel->mLogicalModel->mText.Begin(), numberOfCharacters, 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() ); + 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 ); + 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 || - ( 0u != paragraphCharacters ) ) + if(mTextUpdateInfo.mClearAll || + (0u != paragraphCharacters)) { - ClearModelData( startIndex, startIndex + ( ( paragraphCharacters > 0u ) ? paragraphCharacters - 1u : 0u ), operations ); + ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations); } mTextUpdateInfo.mClearAll = false; @@ -902,133 +656,169 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) // Whether the model is updated. bool updated = false; - Vector& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo; - const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters; + Vector& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo; + const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters; - if( NO_OPERATION != ( GET_LINE_BREAKS & operations ) ) + if(NO_OPERATION != (GET_LINE_BREAKS & operations)) { // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to // calculate the bidirectional info for each 'paragraph'. // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines // is not shaped together). - lineBreakInfo.Resize( numberOfCharacters, TextAbstraction::LINE_NO_BREAK ); + lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK); + + SetLineBreakInfo(utf32Characters, + startIndex, + requestedNumberOfCharacters, + lineBreakInfo); + + if(mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) || + mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED)) + { + CharacterIndex end = startIndex + requestedNumberOfCharacters; + LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin(); + + for(CharacterIndex index = startIndex; index < end; index++) + { + CharacterIndex wordEnd = index; + while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK)) + { + wordEnd++; + } + + if((wordEnd + 1) == end) // add last char + { + wordEnd++; + } + + Vector hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr); + + for(CharacterIndex i = 0; i < (wordEnd - index); i++) + { + if(hyphens[i]) + { + *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK; + } + } - SetLineBreakInfo( utf32Characters, - startIndex, - requestedNumberOfCharacters, - lineBreakInfo ); + index = wordEnd; + } + } // Create the paragraph info. - mModel->mLogicalModel->CreateParagraphInfo( startIndex, - requestedNumberOfCharacters ); + mModel->mLogicalModel->CreateParagraphInfo(startIndex, + requestedNumberOfCharacters); updated = true; } - const bool getScripts = NO_OPERATION != ( GET_SCRIPTS & operations ); - const bool validateFonts = NO_OPERATION != ( VALIDATE_FONTS & operations ); + const bool getScripts = NO_OPERATION != (GET_SCRIPTS & operations); + const bool validateFonts = NO_OPERATION != (VALIDATE_FONTS & operations); - Vector& scripts = mModel->mLogicalModel->mScriptRuns; - Vector& validFonts = mModel->mLogicalModel->mFontRuns; + Vector& scripts = mModel->mLogicalModel->mScriptRuns; + Vector& validFonts = mModel->mLogicalModel->mFontRuns; - if( getScripts || validateFonts ) + if(getScripts || validateFonts) { // Validates the fonts assigned by the application or assigns default ones. // It makes sure all the characters are going to be rendered by the correct font. MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get(); - if( getScripts ) + if(getScripts) { // Retrieves the scripts used in the text. - multilanguageSupport.SetScripts( utf32Characters, - startIndex, - requestedNumberOfCharacters, - scripts ); + multilanguageSupport.SetScripts(utf32Characters, + startIndex, + requestedNumberOfCharacters, + scripts); } - if( validateFonts ) + if(validateFonts) { // Validate the fonts set through the mark-up string. Vector& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns; // Get the default font's description. TextAbstraction::FontDescription defaultFontDescription; - TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE; + TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale; - if( IsShowingPlaceholderText() && mEventData && ( NULL != mEventData->mPlaceholderFont ) ) + //Get the number of points per one unit of point-size + uint32_t numberOfPointsPerOneUnitOfPointSize = mFontClient.GetNumberOfPointsPerOneUnitOfPointSize(); + + if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont)) { // If the placeholder font is set specifically, only placeholder font is changed. defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription; - if( mEventData->mPlaceholderFont->sizeDefined ) + if(mEventData->mPlaceholderFont->sizeDefined) { - defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * 64u; + defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize; } } - else if( NULL != mFontDefaults ) + else if(nullptr != mFontDefaults) { // Set the normal font and the placeholder font. defaultFontDescription = mFontDefaults->mFontDescription; - if( mTextFitEnabled ) + if(mTextFitEnabled) { - defaultPointSize = mFontDefaults->mFitPointSize * 64u; + defaultPointSize = mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize; } else { - defaultPointSize = mFontDefaults->mDefaultPointSize * 64u; + defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize; } } // Validates the fonts. If there is a character with no assigned font it sets a default one. // After this call, fonts are validated. - multilanguageSupport.ValidateFonts( utf32Characters, - scripts, - fontDescriptionRuns, - defaultFontDescription, - defaultPointSize, - startIndex, - requestedNumberOfCharacters, - validFonts ); + multilanguageSupport.ValidateFonts(utf32Characters, + scripts, + fontDescriptionRuns, + defaultFontDescription, + defaultPointSize, + startIndex, + requestedNumberOfCharacters, + validFonts); } updated = true; } Vector mirroredUtf32Characters; - bool textMirrored = false; - const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count(); - if( NO_OPERATION != ( BIDI_INFO & operations ) ) + bool textMirrored = false; + const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count(); + if(NO_OPERATION != (BIDI_INFO & operations)) { Vector& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo; - bidirectionalInfo.Reserve( numberOfParagraphs ); + bidirectionalInfo.Reserve(numberOfParagraphs); // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts. - SetBidirectionalInfo( utf32Characters, - scripts, - lineBreakInfo, - startIndex, - requestedNumberOfCharacters, - bidirectionalInfo, - mModel->mMatchSystemLanguageDirection, - mLayoutDirection ); - - if( 0u != bidirectionalInfo.Count() ) + SetBidirectionalInfo(utf32Characters, + scripts, + lineBreakInfo, + startIndex, + requestedNumberOfCharacters, + bidirectionalInfo, + (mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS), + mLayoutDirection); + + if(0u != bidirectionalInfo.Count()) { // Only set the character directions if there is right to left characters. Vector& directions = mModel->mLogicalModel->mCharacterDirections; - GetCharactersDirection( bidirectionalInfo, - numberOfCharacters, - startIndex, - requestedNumberOfCharacters, - directions ); + GetCharactersDirection(bidirectionalInfo, + numberOfCharacters, + startIndex, + requestedNumberOfCharacters, + directions); // This paragraph has right to left text. Some characters may need to be mirrored. // TODO: consider if the mirrored string can be stored as well. - textMirrored = GetMirroredText( utf32Characters, - directions, - bidirectionalInfo, - startIndex, - requestedNumberOfCharacters, - mirroredUtf32Characters ); + textMirrored = GetMirroredText(utf32Characters, + directions, + bidirectionalInfo, + startIndex, + requestedNumberOfCharacters, + mirroredUtf32Characters); } else { @@ -1038,172 +828,230 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) updated = true; } - Vector& glyphs = mModel->mVisualModel->mGlyphs; + Vector& glyphs = mModel->mVisualModel->mGlyphs; Vector& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters; - Vector& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph; - Vector newParagraphGlyphs; - newParagraphGlyphs.Reserve( numberOfParagraphs ); + Vector& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph; + Vector newParagraphGlyphs; + newParagraphGlyphs.Reserve(numberOfParagraphs); const Length currentNumberOfGlyphs = glyphs.Count(); - if( NO_OPERATION != ( SHAPE_TEXT & operations ) ) + if(NO_OPERATION != (SHAPE_TEXT & operations)) { const Vector& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters; // Shapes the text. - ShapeText( textToShape, - lineBreakInfo, - scripts, - validFonts, - startIndex, - mTextUpdateInfo.mStartGlyphIndex, - requestedNumberOfCharacters, - glyphs, - glyphsToCharactersMap, - charactersPerGlyph, - newParagraphGlyphs ); + ShapeText(textToShape, + lineBreakInfo, + scripts, + validFonts, + startIndex, + mTextUpdateInfo.mStartGlyphIndex, + requestedNumberOfCharacters, + glyphs, + glyphsToCharactersMap, + charactersPerGlyph, + newParagraphGlyphs); // Create the 'number of glyphs' per character and the glyph to character conversion tables. - mModel->mVisualModel->CreateGlyphsPerCharacterTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters ); - mModel->mVisualModel->CreateCharacterToGlyphTable( startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters ); + mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters); + mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters); + updated = true; } const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs; - if( NO_OPERATION != ( GET_GLYPH_METRICS & operations ) ) + if(NO_OPERATION != (GET_GLYPH_METRICS & operations)) { GlyphInfo* glyphsBuffer = glyphs.Begin(); - mMetrics->GetGlyphMetrics( glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs ); + mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs); // Update the width and advance of all new paragraph characters. - for( Vector::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it ) + for(Vector::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it) { const GlyphIndex index = *it; - GlyphInfo& glyph = *( glyphsBuffer + index ); + GlyphInfo& glyph = *(glyphsBuffer + index); glyph.xBearing = 0.f; - glyph.width = 0.f; - glyph.advance = 0.f; + glyph.width = 0.f; + glyph.advance = 0.f; } updated = true; } - if( ( NULL != mEventData ) && - mEventData->mPreEditFlag && - ( 0u != mModel->mVisualModel->mCharactersToGlyph.Count() ) ) + if((nullptr != mEventData) && + mEventData->mPreEditFlag && + (0u != mModel->mVisualModel->mCharactersToGlyph.Count())) { Dali::InputMethodContext::PreEditAttributeDataContainer attrs; - mEventData->mInputMethodContext.GetPreeditStyle( 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++ ) + 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 ); + 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; + unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength; + Length numberOfIndices = attrData.endIndex - attrData.startIndex; - switch( type ) + switch(type) { case Dali::InputMethodContext::PreeditStyle::UNDERLINE: { // Add the underline for the pre-edit text. GlyphRun underlineRun; - underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; underlineRun.numberOfGlyphs = numberOfIndices; - mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun); + + //Mark-up processor case + if(mModel->mVisualModel->IsMarkupProcessorEnabled()) + { + CopyUnderlinedFromLogicalToVisualModels(false); + } break; } case Dali::InputMethodContext::PreeditStyle::REVERSE: { - Vector4 textColor = mModel->mVisualModel->GetTextColor(); + Vector4 textColor = mModel->mVisualModel->GetTextColor(); ColorRun backgroundColorRun; - backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; + backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; - backgroundColorRun.color = textColor; - mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + 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; + if(backgroundColor.a == 0) // There is no text background color. + { + // Try use the control's background color. + if(nullptr != mEditableControlInterface) + { + mEditableControlInterface->GetControlBackgroundColor(backgroundColor); + if(backgroundColor.a == 0) // There is no control background color. + { + // Determines black or white color according to text color. + // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/) + float L = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b; + backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE; + } + } + } + + 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); - mModel->mLogicalModel->mColorRuns.PushBack( colorRun ); + //Mark-up processor case + if(mModel->mVisualModel->IsMarkupProcessorEnabled()) + { + CopyUnderlinedFromLogicalToVisualModels(false); + } break; } case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT: { ColorRun backgroundColorRun; - backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; + backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit; backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; - backgroundColorRun.color = LIGHT_BLUE; - mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + backgroundColorRun.color = LIGHT_BLUE; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun); + + //Mark-up processor case + if(mModel->mVisualModel->IsMarkupProcessorEnabled()) + { + CopyUnderlinedFromLogicalToVisualModels(false); + } 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.characterIndex = attrData.startIndex + numberOfCommit; backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; - backgroundColorRun.color = BACKGROUND_SUB4; - mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + backgroundColorRun.color = BACKGROUND_SUB4; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun); GlyphRun underlineRun; - underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; underlineRun.numberOfGlyphs = numberOfIndices; - mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun); + + //Mark-up processor case + if(mModel->mVisualModel->IsMarkupProcessorEnabled()) + { + CopyUnderlinedFromLogicalToVisualModels(false); + } 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.characterIndex = attrData.startIndex + numberOfCommit; backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; - backgroundColorRun.color = BACKGROUND_SUB5; - mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + backgroundColorRun.color = BACKGROUND_SUB5; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun); GlyphRun underlineRun; - underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; underlineRun.numberOfGlyphs = numberOfIndices; - mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun); + + //Mark-up processor case + if(mModel->mVisualModel->IsMarkupProcessorEnabled()) + { + CopyUnderlinedFromLogicalToVisualModels(false); + } 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.characterIndex = attrData.startIndex + numberOfCommit; backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; - backgroundColorRun.color = BACKGROUND_SUB6; - mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + backgroundColorRun.color = BACKGROUND_SUB6; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun); GlyphRun underlineRun; - underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; underlineRun.numberOfGlyphs = numberOfIndices; - mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun); + + //Mark-up processor case + if(mModel->mVisualModel->IsMarkupProcessorEnabled()) + { + CopyUnderlinedFromLogicalToVisualModels(false); + } 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.characterIndex = attrData.startIndex + numberOfCommit; backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices; - backgroundColorRun.color = BACKGROUND_SUB7; - mModel->mLogicalModel->mBackgroundColorRuns.PushBack( backgroundColorRun ); + backgroundColorRun.color = BACKGROUND_SUB7; + mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun); GlyphRun underlineRun; - underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; + underlineRun.glyphIndex = attrData.startIndex + numberOfCommit; underlineRun.numberOfGlyphs = numberOfIndices; - mModel->mVisualModel->mUnderlineRuns.PushBack( underlineRun ); + mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun); + + //Mark-up processor case + if(mModel->mVisualModel->IsMarkupProcessorEnabled()) + { + CopyUnderlinedFromLogicalToVisualModels(false); + } break; } case Dali::InputMethodContext::PreeditStyle::NONE: @@ -1217,34 +1065,47 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) updated = true; } - if( NO_OPERATION != ( COLOR & operations ) ) + if(NO_OPERATION != (COLOR & operations)) { // Set the color runs in glyphs. - SetColorSegmentationInfo( mModel->mLogicalModel->mColorRuns, - mModel->mVisualModel->mCharactersToGlyph, - mModel->mVisualModel->mGlyphsPerCharacter, - startIndex, - mTextUpdateInfo.mStartGlyphIndex, - requestedNumberOfCharacters, - mModel->mVisualModel->mColors, - mModel->mVisualModel->mColorIndices ); + 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, - mModel->mVisualModel->mBackgroundColors, - mModel->mVisualModel->mBackgroundColorIndices ); + SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns, + mModel->mVisualModel->mCharactersToGlyph, + mModel->mVisualModel->mGlyphsPerCharacter, + startIndex, + mTextUpdateInfo.mStartGlyphIndex, + requestedNumberOfCharacters, + mModel->mVisualModel->mBackgroundColors, + mModel->mVisualModel->mBackgroundColorIndices); updated = true; } + if((NO_OPERATION != (SHAPE_TEXT & operations)) && + !((nullptr != mEventData) && + mEventData->mPreEditFlag && + (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))) + { + //Mark-up processor case + if(mModel->mVisualModel->IsMarkupProcessorEnabled()) + { + CopyUnderlinedFromLogicalToVisualModels(true); + } + + updated = true; + } // The estimated number of lines. Used to avoid reallocations when layouting. - mTextUpdateInfo.mEstimatedNumberOfLines = std::max( mModel->mVisualModel->mLines.Count(), mModel->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; @@ -1252,17 +1113,17 @@ bool Controller::Impl::UpdateModel( OperationsMask operationsRequired ) return updated; } -void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle ) +void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle) { // Sets the default text's color. - inputStyle.textColor = mTextColor; + inputStyle.textColor = mTextColor; inputStyle.isDefaultColor = true; inputStyle.familyName.clear(); inputStyle.weight = TextAbstraction::FontWeight::NORMAL; - inputStyle.width = TextAbstraction::FontWidth::NORMAL; - inputStyle.slant = TextAbstraction::FontSlant::NORMAL; - inputStyle.size = 0.f; + inputStyle.width = TextAbstraction::FontWidth::NORMAL; + inputStyle.slant = TextAbstraction::FontSlant::NORMAL; + inputStyle.size = 0.f; inputStyle.lineSpacing = 0.f; @@ -1273,47 +1134,47 @@ void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle ) inputStyle.isFamilyDefined = false; inputStyle.isWeightDefined = false; - inputStyle.isWidthDefined = false; - inputStyle.isSlantDefined = false; - inputStyle.isSizeDefined = false; + inputStyle.isWidthDefined = false; + inputStyle.isSlantDefined = false; + inputStyle.isSizeDefined = false; inputStyle.isLineSpacingDefined = false; inputStyle.isUnderlineDefined = false; - inputStyle.isShadowDefined = false; - inputStyle.isEmbossDefined = false; - inputStyle.isOutlineDefined = false; + inputStyle.isShadowDefined = false; + inputStyle.isEmbossDefined = false; + inputStyle.isOutlineDefined = false; // Sets the default font's family name, weight, width, slant and size. - if( mFontDefaults ) + if(mFontDefaults) { - if( mFontDefaults->familyDefined ) + if(mFontDefaults->familyDefined) { - inputStyle.familyName = mFontDefaults->mFontDescription.family; + inputStyle.familyName = mFontDefaults->mFontDescription.family; inputStyle.isFamilyDefined = true; } - if( mFontDefaults->weightDefined ) + if(mFontDefaults->weightDefined) { - inputStyle.weight = mFontDefaults->mFontDescription.weight; + inputStyle.weight = mFontDefaults->mFontDescription.weight; inputStyle.isWeightDefined = true; } - if( mFontDefaults->widthDefined ) + if(mFontDefaults->widthDefined) { - inputStyle.width = mFontDefaults->mFontDescription.width; + inputStyle.width = mFontDefaults->mFontDescription.width; inputStyle.isWidthDefined = true; } - if( mFontDefaults->slantDefined ) + if(mFontDefaults->slantDefined) { - inputStyle.slant = mFontDefaults->mFontDescription.slant; + inputStyle.slant = mFontDefaults->mFontDescription.slant; inputStyle.isSlantDefined = true; } - if( mFontDefaults->sizeDefined ) + if(mFontDefaults->sizeDefined) { - inputStyle.size = mFontDefaults->mDefaultPointSize; + inputStyle.size = mFontDefaults->mDefaultPointSize; inputStyle.isSizeDefined = true; } } @@ -1322,788 +1183,207 @@ void Controller::Impl::RetrieveDefaultInputStyle( InputStyle& inputStyle ) float Controller::Impl::GetDefaultFontLineHeight() { FontId defaultFontId = 0u; - if( NULL == mFontDefaults ) + if(nullptr == mFontDefaults) { TextAbstraction::FontDescription fontDescription; - defaultFontId = mFontClient.GetFontId( fontDescription ); + defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale); } else { - defaultFontId = mFontDefaults->GetFontId( mFontClient ); + defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale); } Text::FontMetrics fontMetrics; - mMetrics->GetFontMetrics( defaultFontId, fontMetrics ); + mMetrics->GetFontMetrics(defaultFontId, fontMetrics); - return( fontMetrics.ascender - fontMetrics.descender ); + return (fontMetrics.ascender - fontMetrics.descender); } -void Controller::Impl::OnCursorKeyEvent( const Event& event ) +void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd) { - if( NULL == mEventData || !IsShowingRealText() ) + if(nullptr == mEventData) { - // Nothing to do if there is no text input. + // Nothing to do if there is no text. return; } - int keyCode = event.p1.mInt; - bool isShiftModifier = event.p2.mBool; - - CharacterIndex previousPrimaryCursorPosition = mEventData->mPrimaryCursorPosition; - - if( Dali::DALI_KEY_CURSOR_LEFT == keyCode ) + if(mEventData->mSelectionEnabled && (pStart || pEnd)) { - if( mEventData->mPrimaryCursorPosition > 0u ) + uint32_t length = static_cast(mModel->mLogicalModel->mText.Count()); + uint32_t oldStart = mEventData->mLeftSelectionPosition; + uint32_t oldEnd = mEventData->mRightSelectionPosition; + + if(pStart) { - if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() ) - { - mEventData->mPrimaryCursorPosition = std::min(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); - } - else - { - mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition - 1u ); - } + mEventData->mLeftSelectionPosition = std::min(*pStart, length); } - } - else if( Dali::DALI_KEY_CURSOR_RIGHT == keyCode ) - { - if( mModel->mLogicalModel->mText.Count() > mEventData->mPrimaryCursorPosition ) + if(pEnd) { - if ( !isShiftModifier && mEventData->mDecorator->IsHighlightVisible() ) - { - mEventData->mPrimaryCursorPosition = std::max(mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); - } - else - { - mEventData->mPrimaryCursorPosition = CalculateNewCursorIndex( mEventData->mPrimaryCursorPosition ); - } + mEventData->mRightSelectionPosition = std::min(*pEnd, length); } - } - 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; - - if( mEventData->mPrimaryCursorPosition > 0u ) + if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition) { - characterIndex = mEventData->mPrimaryCursorPosition - 1u; + ChangeState(EventData::EDITING); + mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition; + mEventData->mUpdateCursorPosition = true; } - - const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex ); - const LineIndex previousLineIndex = ( lineIndex > 0 ? lineIndex - 1u : lineIndex ); - - // Retrieve the cursor position info. - CursorInfo cursorInfo; - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); - - // 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 ); - - // 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 && !isShiftModifier ) - { - // Ignore Shift-Down for text selection for now. - - // Get first the line index of the current cursor position index. - CharacterIndex characterIndex = 0u; - - if( mEventData->mPrimaryCursorPosition > 0u ) + else { - characterIndex = mEventData->mPrimaryCursorPosition - 1u; + ChangeState(EventData::SELECTING); + mEventData->mUpdateHighlightBox = true; + mEventData->mUpdateLeftSelectionPosition = true; + mEventData->mUpdateRightSelectionPosition = true; } - const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter( characterIndex ); - - if( lineIndex + 1u < mModel->mVisualModel->mLines.Count() ) + if(mSelectableControlInterface != nullptr) { - // Retrieve the cursor position info. - CursorInfo cursorInfo; - GetCursorPosition( mEventData->mPrimaryCursorPosition, - cursorInfo ); - - // Get the line below. - 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. - bool matchedCharacter = false; - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - mEventData->mCursorHookPositionX, - hitPointY, - CharacterHitTest::TAP, - matchedCharacter ); + mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); } } +} + +CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const +{ + if(nullptr == mEventData) + { + return 0; + } + return mEventData->mPrimaryCursorPosition; +} - if ( !isShiftModifier && mEventData->mState != EventData::SELECTING ) +bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused) +{ + if(nullptr == mEventData) { - // Update selection position after moving the cursor - mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition; - mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; + // Nothing to do if there is no text. + return false; } - if ( isShiftModifier && IsShowingRealText() && mEventData->mShiftSelectionFlag ) + if(mEventData->mPrimaryCursorPosition == index && mEventData->mState != EventData::SELECTING) { - // Handle text selection - bool selecting = false; + // Nothing for same cursor position. + return 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; - } + uint32_t length = static_cast(mModel->mLogicalModel->mText.Count()); + uint32_t oldCursorPos = mEventData->mPrimaryCursorPosition; + mEventData->mPrimaryCursorPosition = std::min(index, length); + // If there is no focus, only the value is updated. + if(focused) + { + bool wasInSelectingState = mEventData->mState == EventData::SELECTING; + uint32_t oldStart = mEventData->mLeftSelectionPosition; + uint32_t oldEnd = mEventData->mRightSelectionPosition; + ChangeState(EventData::EDITING); + mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; + mEventData->mUpdateCursorPosition = true; - if ( selecting ) + if(mSelectableControlInterface != nullptr && wasInSelectingState) { - // 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 ); - } + mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); } + + ScrollTextToMatchCursor(); } - else + + if(nullptr != mEditableControlInterface) { - // Handle normal cursor move - ChangeState( EventData::EDITING ); - mEventData->mUpdateCursorPosition = true; + mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition); } - mEventData->mUpdateInputStyle = true; - mEventData->mScrollAfterUpdatePosition = true; + return true; } -void Controller::Impl::OnTapEvent( const Event& event ) +Uint32Pair Controller::Impl::GetTextSelectionRange() const { - if( NULL != mEventData ) - { - const unsigned int tapCount = event.p1.mUint; + Uint32Pair range; - if( 1u == tapCount ) - { - if( IsShowingRealText() ) - { - // 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; - - // Keep the tap 'x' position. Used to move the cursor. - mEventData->mCursorHookPositionX = xPosition; - - // Whether to touch point hits on a glyph. - bool matchedCharacter = false; - mEventData->mPrimaryCursorPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - xPosition, - yPosition, - CharacterHitTest::TAP, - matchedCharacter ); - - // When the cursor position is changing, delay cursor blinking - mEventData->mDecorator->DelayCursorBlink(); - } - else - { - mEventData->mPrimaryCursorPosition = 0u; - } + if(mEventData) + { + range.first = mEventData->mLeftSelectionPosition; + range.second = mEventData->mRightSelectionPosition; + } - // Update selection position after tapping - mEventData->mLeftSelectionPosition = mEventData->mPrimaryCursorPosition; - mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition; + return range; +} - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mUpdateInputStyle = true; +bool Controller::Impl::IsEditable() const +{ + return mEventData && mEventData->mEditingEnabled; +} - // 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 ) - { - // 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 ); - } - } +void Controller::Impl::SetEditable(bool editable) +{ + if(mEventData) + { + mEventData->mEditingEnabled = editable; } } -void Controller::Impl::OnPanEvent( const Event& event ) +void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval) { - if( NULL == mEventData ) + if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition) { - // Nothing to do if there is no text input. + // Nothing to select if handles are in the same place. + selectedText.clear(); return; } - const bool isHorizontalScrollEnabled = mEventData->mDecorator->IsHorizontalScrollEnabled(); - const bool isVerticalScrollEnabled = mEventData->mDecorator->IsVerticalScrollEnabled(); + const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition; - if( !isHorizontalScrollEnabled && !isVerticalScrollEnabled ) - { - // Nothing to do if scrolling is not enabled. - return; - } + //Get start and end position of selection + const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition; + const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText; - const int state = event.p1.mInt; + Vector& utf32Characters = mModel->mLogicalModel->mText; + const Length numberOfCharacters = utf32Characters.Count(); - switch( state ) + // Validate the start and end selection points + if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters) { - case Gesture::Started: - { - // Will remove the cursor, handles or text's popup, ... - ChangeState( EventData::TEXT_PANNING ); - break; - } - case Gesture::Continuing: + //Get text as a UTF8 string + Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText); + + if(deleteAfterRetrieval) // Only delete text if copied successfully { - const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); - const Vector2 currentScroll = mModel->mScrollPosition; - - if( isHorizontalScrollEnabled ) - { - const float displacementX = event.p2.mFloat; - mModel->mScrollPosition.x += displacementX; - - ClampHorizontalScroll( layoutSize ); - } - - if( isVerticalScrollEnabled ) - { - const float displacementY = event.p3.mFloat; - mModel->mScrollPosition.y += displacementY; - - ClampVerticalScroll( layoutSize ); - } - - mEventData->mDecorator->UpdatePositions( mModel->mScrollPosition - currentScroll ); - break; - } - case Gesture::Finished: - case Gesture::Cancelled: // FALLTHROUGH - { - // Will go back to the previous state to show the cursor, handles, the text's popup, ... - ChangeState( mEventData->mPreviousState ); - break; - } - default: - break; - } -} - -void Controller::Impl::OnLongPressEvent( const Event& event ) -{ - DALI_LOG_INFO( gLogFilter, Debug::General, "Controller::OnLongPressEvent\n" ); - - if( !IsShowingRealText() && ( EventData::EDITING == mEventData->mState ) ) - { - 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 ); - } - } -} - -void Controller::Impl::OnHandleEvent( const Event& event ) -{ - if( NULL == mEventData ) - { - // Nothing to do if there is no text input. - return; - } - - const unsigned int state = event.p1.mUint; - const bool handleStopScrolling = ( HANDLE_STOP_SCROLLING == state ); - const bool isSmoothHandlePanEnabled = mEventData->mDecorator->IsSmoothHandlePanEnabled(); - - if( HANDLE_PRESSED == state ) - { - // Convert from decorator's coords to text's coords. - 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. - bool matchedCharacter = false; - const CharacterIndex handleNewPosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - xPosition, - yPosition, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( Event::GRAB_HANDLE_EVENT == event.type ) - { - ChangeState ( EventData::GRAB_HANDLE_PANNING ); - - if( handleNewPosition != mEventData->mPrimaryCursorPosition ) - { - // Updates the cursor position if the handle's new position is different than the current one. - mEventData->mUpdateCursorPosition = true; - // Does not update the grab handle position if the smooth panning is enabled. (The decorator does it smooth). - mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; - mEventData->mPrimaryCursorPosition = handleNewPosition; - } - - // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - } - else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState ( EventData::SELECTION_HANDLE_PANNING ); - - if( ( handleNewPosition != mEventData->mLeftSelectionPosition ) && - ( handleNewPosition != mEventData->mRightSelectionPosition ) ) - { - // Updates the highlight box if the handle's new position is different than the current one. - mEventData->mUpdateHighlightBox = true; - // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth). - mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mLeftSelectionPosition = handleNewPosition; - } - - // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - - // 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 ) - { - ChangeState ( EventData::SELECTION_HANDLE_PANNING ); - - if( ( handleNewPosition != mEventData->mRightSelectionPosition ) && - ( handleNewPosition != mEventData->mLeftSelectionPosition ) ) - { - // Updates the highlight box if the handle's new position is different than the current one. - mEventData->mUpdateHighlightBox = true; - // Does not update the selection handle position if the smooth panning is enabled. (The decorator does it smooth). - mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mRightSelectionPosition = handleNewPosition; - } - - // Updates the decorator if the soft handle panning is enabled. It triggers a relayout in the decorator and the new position of the handle is set. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - - // 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 ) || - handleStopScrolling ) - { - CharacterIndex handlePosition = 0u; - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - // Convert from decorator's coords to text's coords. - const float xPosition = event.p2.mFloat - mModel->mScrollPosition.x; - const float yPosition = event.p3.mFloat - mModel->mScrollPosition.y; - - bool matchedCharacter = false; - handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - xPosition, - yPosition, - CharacterHitTest::SCROLL, - matchedCharacter ); - } - - if( Event::GRAB_HANDLE_EVENT == event.type ) - { - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = true; - mEventData->mUpdateInputStyle = true; - - if( !IsClipboardEmpty() ) - { - ChangeState( EventData::EDITING_WITH_PASTE_POPUP ); // Moving grabhandle will show Paste Popup - } - - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mPrimaryCursorPosition = handlePosition; - } - } - else if( Event::LEFT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState( EventData::SELECTING ); - - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateLeftSelectionPosition = true; - mEventData->mUpdateRightSelectionPosition = true; - - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - mEventData->mScrollAfterUpdatePosition = true; - - if( ( handlePosition != mEventData->mRightSelectionPosition ) && - ( handlePosition != mEventData->mLeftSelectionPosition ) ) - { - mEventData->mLeftSelectionPosition = handlePosition; - } - } - } - else if( Event::RIGHT_SELECTION_HANDLE_EVENT == event.type ) - { - ChangeState( EventData::SELECTING ); - - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateRightSelectionPosition = true; - mEventData->mUpdateLeftSelectionPosition = true; - - if( handleStopScrolling || isSmoothHandlePanEnabled ) - { - mEventData->mScrollAfterUpdatePosition = true; - if( ( handlePosition != mEventData->mRightSelectionPosition ) && - ( handlePosition != mEventData->mLeftSelectionPosition ) ) - { - mEventData->mRightSelectionPosition = handlePosition; - } - } - } - - mEventData->mDecoratorUpdated = true; - } // end ( ( HANDLE_RELEASED == state ) || ( HANDLE_STOP_SCROLLING == state ) ) - else if( HANDLE_SCROLLING == state ) - { - const float xSpeed = event.p2.mFloat; - const float ySpeed = event.p3.mFloat; - const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); - const Vector2 currentScrollPosition = mModel->mScrollPosition; - - mModel->mScrollPosition.x += xSpeed; - mModel->mScrollPosition.y += ySpeed; - - ClampHorizontalScroll( layoutSize ); - ClampVerticalScroll( layoutSize ); - - bool endOfScroll = false; - if( Vector2::ZERO == ( currentScrollPosition - mModel->mScrollPosition ) ) - { - // Notify the decorator there is no more text to scroll. - // The decorator won't send more scroll events. - mEventData->mDecorator->NotifyEndOfScroll(); - // Still need to set the position of the handle. - endOfScroll = true; - } - - // Set the position of the handle. - const bool scrollRightDirection = xSpeed > 0.f; - const bool scrollBottomDirection = ySpeed > 0.f; - const bool leftSelectionHandleEvent = Event::LEFT_SELECTION_HANDLE_EVENT == event.type; - const bool rightSelectionHandleEvent = Event::RIGHT_SELECTION_HANDLE_EVENT == event.type; - - if( Event::GRAB_HANDLE_EVENT == event.type ) - { - ChangeState( EventData::GRAB_HANDLE_PANNING ); - - // Get the grab handle position in decorator coords. - Vector2 position = mEventData->mDecorator->GetPosition( GRAB_HANDLE ); - - if( mEventData->mDecorator->IsHorizontalScrollEnabled() ) - { - // Position the grag handle close to either the left or right edge. - position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width; - } - - if( mEventData->mDecorator->IsVerticalScrollEnabled() ) - { - position.x = mEventData->mCursorHookPositionX; - - // Position the grag handle close to either the top or bottom edge. - 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. - bool matchedCharacter = false; - const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - position.x - mModel->mScrollPosition.x, - position.y - mModel->mScrollPosition.y, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( mEventData->mPrimaryCursorPosition != handlePosition ) - { - mEventData->mUpdateCursorPosition = true; - mEventData->mUpdateGrabHandlePosition = !isSmoothHandlePanEnabled; - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mPrimaryCursorPosition = handlePosition; - } - mEventData->mUpdateInputStyle = mEventData->mUpdateCursorPosition; - - // Updates the decorator if the soft handle panning is enabled. - mEventData->mDecoratorUpdated = isSmoothHandlePanEnabled; - } - else if( leftSelectionHandleEvent || rightSelectionHandleEvent ) - { - ChangeState( EventData::SELECTION_HANDLE_PANNING ); - - // Get the selection handle position in decorator coords. - Vector2 position = mEventData->mDecorator->GetPosition( leftSelectionHandleEvent ? Text::LEFT_SELECTION_HANDLE : Text::RIGHT_SELECTION_HANDLE ); - - if( mEventData->mDecorator->IsHorizontalScrollEnabled() ) - { - // Position the selection handle close to either the left or right edge. - position.x = scrollRightDirection ? 0.f : mModel->mVisualModel->mControlSize.width; - } - - if( mEventData->mDecorator->IsVerticalScrollEnabled() ) - { - position.x = mEventData->mCursorHookPositionX; - - // Position the grag handle close to either the top or bottom edge. - 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. - bool matchedCharacter = false; - const CharacterIndex handlePosition = Text::GetClosestCursorIndex( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - position.x - mModel->mScrollPosition.x, - position.y - mModel->mScrollPosition.y, - CharacterHitTest::SCROLL, - matchedCharacter ); - - if( leftSelectionHandleEvent ) - { - const bool differentHandles = ( mEventData->mLeftSelectionPosition != handlePosition ) && ( mEventData->mRightSelectionPosition != handlePosition ); - - if( differentHandles || endOfScroll ) - { - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateLeftSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mUpdateRightSelectionPosition = isSmoothHandlePanEnabled; - mEventData->mLeftSelectionPosition = handlePosition; - } - } - else - { - const bool differentHandles = ( mEventData->mRightSelectionPosition != handlePosition ) && ( mEventData->mLeftSelectionPosition != handlePosition ); - if( differentHandles || endOfScroll ) - { - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateRightSelectionPosition = !isSmoothHandlePanEnabled; - mEventData->mUpdateLeftSelectionPosition = isSmoothHandlePanEnabled; - mEventData->mRightSelectionPosition = handlePosition; - } - } - - if( mEventData->mUpdateLeftSelectionPosition || mEventData->mUpdateRightSelectionPosition ) - { - RepositionSelectionHandles(); - - mEventData->mScrollAfterUpdatePosition = !isSmoothHandlePanEnabled; - } - } - mEventData->mDecoratorUpdated = true; - } // end ( HANDLE_SCROLLING == state ) -} - -void Controller::Impl::OnSelectEvent( const Event& event ) -{ - if( NULL == mEventData ) - { - // Nothing to do if there is no text. - return; - } - - 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, - Controller::NoTextTap::HIGHLIGHT ); - } -} - -void Controller::Impl::OnSelectAllEvent() -{ - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "OnSelectAllEvent mEventData->mSelectionEnabled%s \n", mEventData->mSelectionEnabled?"true":"false"); - - if( NULL == mEventData ) - { - // Nothing to do if there is no text. - return; - } - - if( mEventData->mSelectionEnabled ) - { - // 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 = 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; - } -} - -void Controller::Impl::RetrieveSelection( std::string& selectedText, bool deleteAfterRetrieval ) -{ - if( mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition ) - { - // Nothing to select if handles are in the same place. - selectedText.clear(); - return; - } - - const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition; - - //Get start and end position of selection - const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition; - const Length lengthOfSelectedText = ( handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition ) - startOfSelectedText; - - Vector& utf32Characters = mModel->mLogicalModel->mText; - const Length numberOfCharacters = utf32Characters.Count(); - - // Validate the start and end selection points - if( ( startOfSelectedText + lengthOfSelectedText ) <= numberOfCharacters ) - { - //Get text as a UTF8 string - Utf32ToUtf8( &utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText ); - - if( deleteAfterRetrieval ) // Only delete text if copied successfully - { - // Keep a copy of the current input style. - InputStyle currentInputStyle; - currentInputStyle.Copy( mEventData->mInputStyle ); + // Keep a copy of the current input style. + InputStyle currentInputStyle; + currentInputStyle.Copy(mEventData->mInputStyle); // Set as input style the style of the first deleted character. - mModel->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 ); + const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle); - if( hasInputStyleChanged ) + if(hasInputStyleChanged) { - const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask( mEventData->mInputStyle ); + const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle); // Queue the input style changed signal. - mEventData->mInputStyleChangedQueue.PushBack( styleChangedMask ); + mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask); } - mModel->mLogicalModel->UpdateTextStyleRuns( startOfSelectedText, -static_cast( lengthOfSelectedText ) ); + mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast(lengthOfSelectedText)); // Mark the paragraphs to be updated. - if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() ) + if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout()) { - mTextUpdateInfo.mCharacterIndex = 0; + mTextUpdateInfo.mCharacterIndex = 0; mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters; - mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText; - mTextUpdateInfo.mClearAll = true; + mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText; + mTextUpdateInfo.mClearAll = true; } else { - mTextUpdateInfo.mCharacterIndex = startOfSelectedText; + mTextUpdateInfo.mCharacterIndex = startOfSelectedText; mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; } // Delete text between handles Vector::Iterator first = utf32Characters.Begin() + startOfSelectedText; Vector::Iterator last = first + lengthOfSelectedText; - utf32Characters.Erase( first, last ); + utf32Characters.Erase(first, last); // Will show the cursor at the first character of the selection. mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition; @@ -2118,9 +1398,29 @@ void Controller::Impl::RetrieveSelection( std::string& selectedText, bool delete } } +void Controller::Impl::SetSelection(int start, int end) +{ + uint32_t oldStart = mEventData->mLeftSelectionPosition; + uint32_t oldEnd = mEventData->mRightSelectionPosition; + + mEventData->mLeftSelectionPosition = start; + mEventData->mRightSelectionPosition = end; + mEventData->mUpdateCursorPosition = true; + + if(mSelectableControlInterface != nullptr) + { + mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end); + } +} + +std::pair Controller::Impl::GetSelectionIndexes() const +{ + return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition}; +} + void Controller::Impl::ShowClipboard() { - if( mClipboard ) + if(mClipboard) { mClipboard.ShowClipboard(); } @@ -2128,7 +1428,7 @@ void Controller::Impl::ShowClipboard() void Controller::Impl::HideClipboard() { - if( mClipboard && mClipboardHideEnabled ) + if(mClipboard && mClipboardHideEnabled) { mClipboard.HideClipboard(); } @@ -2139,23 +1439,23 @@ void Controller::Impl::SetClipboardHideEnable(bool enable) mClipboardHideEnabled = enable; } -bool Controller::Impl::CopyStringToClipboard( std::string& source ) +bool Controller::Impl::CopyStringToClipboard(const std::string& source) { //Send string to clipboard - return ( mClipboard && mClipboard.SetItem( source ) ); + return (mClipboard && mClipboard.SetItem(source)); } -void Controller::Impl::SendSelectionToClipboard( bool deleteAfterSending ) +void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending) { std::string selectedText; - RetrieveSelection( selectedText, deleteAfterSending ); - CopyStringToClipboard( selectedText ); - ChangeState( EventData::EDITING ); + RetrieveSelection(selectedText, deleteAfterSending); + CopyStringToClipboard(selectedText); + ChangeState(EventData::EDITING); } void Controller::Impl::RequestGetTextFromClipboard() { - if ( mClipboard ) + if(mClipboard) { mClipboard.RequestItem(); } @@ -2163,477 +1463,11 @@ void Controller::Impl::RequestGetTextFromClipboard() void Controller::Impl::RepositionSelectionHandles() { - CharacterIndex selectionStart = mEventData->mLeftSelectionPosition; - CharacterIndex selectionEnd = mEventData->mRightSelectionPosition; - - 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 = 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 >= mModel->mLogicalModel->mText.Count(); - const CharacterDirection startDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + selectionStart ) ); - const CharacterDirection endDirection = ( ( NULL == modelCharacterDirectionsBuffer ) ? false : *( modelCharacterDirectionsBuffer + ( selectionEnd - ( isLastCharacter ? 1u : 0u ) ) ) ); - - // Swap the indices if the start is greater than the end. - const bool indicesSwapped = selectionStart > selectionEnd; - - // Tell the decorator to flip the selection handles if needed. - mEventData->mDecorator->SetSelectionHandleFlipState( indicesSwapped, startDirection, endDirection ); - - if( indicesSwapped ) - { - std::swap( selectionStart, selectionEnd ); - } - - // Get the indices to the first and last selected glyphs. - const CharacterIndex selectionEndMinusOne = selectionEnd - 1u; - const GlyphIndex glyphStart = *( charactersToGlyphBuffer + selectionStart ); - const Length numberOfGlyphs = *( glyphsPerCharacterBuffer + selectionEndMinusOne ); - const GlyphIndex glyphEnd = *( charactersToGlyphBuffer + selectionEndMinusOne ) + ( ( numberOfGlyphs > 0 ) ? numberOfGlyphs - 1u : 0u ); - - // Get the lines where the glyphs are laid-out. - const LineRun* lineRun = mModel->mVisualModel->mLines.Begin(); - - LineIndex lineIndex = 0u; - Length numberOfLines = 0u; - mModel->mVisualModel->GetNumberOfLines( glyphStart, - 1u + glyphEnd - glyphStart, - lineIndex, - numberOfLines ); - const LineIndex firstLineIndex = lineIndex; - - // Create the structure to store some selection box info. - Vector selectionBoxLinesInfo; - selectionBoxLinesInfo.Resize( numberOfLines ); - - SelectionBoxInfo* selectionBoxInfo = selectionBoxLinesInfo.Begin(); - selectionBoxInfo->minX = MAX_FLOAT; - selectionBoxInfo->maxX = MIN_FLOAT; - - // Keep the min and max 'x' position to calculate the size and position of the highlighed text. - float minHighlightX = std::numeric_limits::max(); - float maxHighlightX = std::numeric_limits::min(); - Size highLightSize; - Vector2 highLightPosition; // The highlight position in decorator's coords. - - // 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( mModel->mVisualModel->mLines, - firstLineIndex ); - - // Transform to decorator's (control) coords. - selectionBoxInfo->lineOffset += mModel->mScrollPosition.y; - - lineRun += firstLineIndex; - - // The line height is the addition of the line ascender and the line descender. - // However, the line descender has a negative value, hence the subtraction. - selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender; - - GlyphIndex lastGlyphOfLine = lineRun->glyphRun.glyphIndex + lineRun->glyphRun.numberOfGlyphs - 1u; - - // 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( 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( mModel->mLogicalModel->GetScript( selectionEndMinusOne ) ); - - // The number of quads of the selection box. - const unsigned int numberOfQuads = 1u + ( glyphEnd - glyphStart ) + ( ( numberOfLines > 1u ) ? 2u * numberOfLines : 0u ); - mEventData->mDecorator->ResizeHighlightQuads( numberOfQuads ); - - // Count the actual number of quads. - unsigned int actualNumberOfQuads = 0u; - Vector4 quad; - - // Traverse the glyphs. - for( GlyphIndex index = glyphStart; index <= glyphEnd; ++index ) - { - const GlyphInfo& glyph = *( glyphsBuffer + index ); - const Vector2& position = *( positionsBuffer + index ); - - if( splitStartGlyph ) - { - // If the first glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box. - - const float glyphAdvance = glyph.advance / static_cast( numberOfCharactersStart ); - const CharacterIndex interGlyphIndex = selectionStart - *( glyphToCharacterBuffer + glyphStart ); - // Get the direction of the character. - CharacterDirection isCurrentRightToLeft = false; - if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right. - { - isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionStart ); - } - - // The end point could be in the middle of the ligature. - // Calculate the number of characters selected. - const Length numberOfCharacters = ( glyphStart == glyphEnd ) ? ( selectionEnd - selectionStart ) : ( numberOfCharactersStart - 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; - - // Store the min and max 'x' for each line. - selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x ); - selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z ); - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, quad ); - ++actualNumberOfQuads; - - splitStartGlyph = false; - continue; - } - - if( splitEndGlyph && ( index == glyphEnd ) ) - { - // Equally, if the last glyph is a ligature that must be broken it may be needed to add only part of the glyph to the highlight box. - - const float glyphAdvance = glyph.advance / static_cast( numberOfCharactersEnd ); - const CharacterIndex interGlyphIndex = selectionEnd - *( glyphToCharacterBuffer + glyphEnd ); - // Get the direction of the character. - CharacterDirection isCurrentRightToLeft = false; - if( NULL != modelCharacterDirectionsBuffer ) // If modelCharacterDirectionsBuffer is NULL, it means the whole text is left to right. - { - isCurrentRightToLeft = *( modelCharacterDirectionsBuffer + selectionEnd ); - } - - const Length numberOfCharacters = numberOfCharactersEnd - interGlyphIndex; - - 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; - - // Store the min and max 'x' for each line. - selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x ); - selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z ); - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - splitEndGlyph = false; - continue; - } - - 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; - - // Store the min and max 'x' for each line. - selectionBoxInfo->minX = std::min( selectionBoxInfo->minX, quad.x ); - selectionBoxInfo->maxX = std::max( selectionBoxInfo->maxX, quad.z ); - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Whether to retrieve the next line. - if( index == lastGlyphOfLine ) - { - ++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; - - // Get the selection box info for the next line. - ++selectionBoxInfo; - - selectionBoxInfo->minX = MAX_FLOAT; - selectionBoxInfo->maxX = MIN_FLOAT; - - // Update the line's vertical offset. - selectionBoxInfo->lineOffset = currentLineOffset + currentLineHeight; - - // The line height is the addition of the line ascender and the line descender. - // However, the line descender has a negative value, hence the subtraction. - selectionBoxInfo->lineHeight = lineRun->ascender - lineRun->descender; - } - } - } - - // Traverses all the lines and updates the min and max 'x' positions and the total height. - // The final width is calculated after 'boxifying' the selection. - for( Vector::ConstIterator it = selectionBoxLinesInfo.Begin(), - endIt = selectionBoxLinesInfo.End(); - it != endIt; - ++it ) - { - const SelectionBoxInfo& info = *it; - - // Update the size of the highlighted text. - highLightSize.height += info.lineHeight; - minHighlightX = std::min( minHighlightX, info.minX ); - maxHighlightX = std::max( maxHighlightX, info.maxX ); - } - - // Add extra geometry to 'boxify' the selection. - - if( 1u < numberOfLines ) - { - // Boxify the first line. - lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex; - const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() ); - - bool boxifyBegin = ( LTR != lineRun->direction ) && ( LTR != startDirection ); - bool boxifyEnd = ( LTR == lineRun->direction ) && ( LTR == startDirection ); - - if( boxifyBegin ) - { - quad.x = 0.f; - quad.y = firstSelectionBoxLineInfo.lineOffset; - quad.z = firstSelectionBoxLineInfo.minX; - quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight; - - // Boxify at the beginning of the line. - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Update the size of the highlighted text. - minHighlightX = 0.f; - } - - if( boxifyEnd ) - { - quad.x = firstSelectionBoxLineInfo.maxX; - quad.y = firstSelectionBoxLineInfo.lineOffset; - quad.z = mModel->mVisualModel->mControlSize.width; - quad.w = firstSelectionBoxLineInfo.lineOffset + firstSelectionBoxLineInfo.lineHeight; - - // Boxify at the end of the line. - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Update the size of the highlighted text. - maxHighlightX = mModel->mVisualModel->mControlSize.width; - } - - // Boxify the central lines. - if( 2u < numberOfLines ) - { - for( Vector::ConstIterator it = selectionBoxLinesInfo.Begin() + 1u, - endIt = selectionBoxLinesInfo.End() - 1u; - it != endIt; - ++it ) - { - const SelectionBoxInfo& info = *it; - - quad.x = 0.f; - quad.y = info.lineOffset; - quad.z = info.minX; - quad.w = info.lineOffset + info.lineHeight; - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - quad.x = info.maxX; - quad.y = info.lineOffset; - quad.z = mModel->mVisualModel->mControlSize.width; - quad.w = info.lineOffset + info.lineHeight; - - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - } - - // Update the size of the highlighted text. - minHighlightX = 0.f; - maxHighlightX = mModel->mVisualModel->mControlSize.width; - } - - // Boxify the last line. - lineRun = mModel->mVisualModel->mLines.Begin() + firstLineIndex + numberOfLines - 1u; - const SelectionBoxInfo& lastSelectionBoxLineInfo = *( selectionBoxLinesInfo.End() - 1u ); - - boxifyBegin = ( LTR == lineRun->direction ) && ( LTR == endDirection ); - boxifyEnd = ( LTR != lineRun->direction ) && ( LTR != endDirection ); - - if( boxifyBegin ) - { - quad.x = 0.f; - quad.y = lastSelectionBoxLineInfo.lineOffset; - quad.z = lastSelectionBoxLineInfo.minX; - quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight; - - // Boxify at the beginning of the line. - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Update the size of the highlighted text. - minHighlightX = 0.f; - } - - if( boxifyEnd ) - { - quad.x = lastSelectionBoxLineInfo.maxX; - quad.y = lastSelectionBoxLineInfo.lineOffset; - quad.z = mModel->mVisualModel->mControlSize.width; - quad.w = lastSelectionBoxLineInfo.lineOffset + lastSelectionBoxLineInfo.lineHeight; - - // Boxify at the end of the line. - mEventData->mDecorator->AddHighlight( actualNumberOfQuads, - quad ); - ++actualNumberOfQuads; - - // Update the size of the highlighted text. - maxHighlightX = mModel->mVisualModel->mControlSize.width; - } - } - - // Set the actual number of quads. - mEventData->mDecorator->ResizeHighlightQuads( actualNumberOfQuads ); - - // Sets the highlight's size and position. In decorator's coords. - // The highlight's height has been calculated above (before 'boxifying' the highlight). - highLightSize.width = maxHighlightX - minHighlightX; - - highLightPosition.x = minHighlightX; - const SelectionBoxInfo& firstSelectionBoxLineInfo = *( selectionBoxLinesInfo.Begin() ); - highLightPosition.y = firstSelectionBoxLineInfo.lineOffset; - - mEventData->mDecorator->SetHighLightBox( highLightPosition, highLightSize, static_cast( mModel->GetOutlineWidth() ) ); - - if( !mEventData->mDecorator->IsSmoothHandlePanEnabled() ) - { - CursorInfo primaryCursorInfo; - GetCursorPosition( mEventData->mLeftSelectionPosition, - primaryCursorInfo ); - - const Vector2 primaryPosition = primaryCursorInfo.primaryPosition + mModel->mScrollPosition; - - mEventData->mDecorator->SetPosition( LEFT_SELECTION_HANDLE, - primaryPosition.x, - primaryCursorInfo.lineOffset + mModel->mScrollPosition.y, - primaryCursorInfo.lineHeight ); - - CursorInfo secondaryCursorInfo; - GetCursorPosition( mEventData->mRightSelectionPosition, - secondaryCursorInfo ); - - const Vector2 secondaryPosition = secondaryCursorInfo.primaryPosition + mModel->mScrollPosition; - - mEventData->mDecorator->SetPosition( RIGHT_SELECTION_HANDLE, - secondaryPosition.x, - secondaryCursorInfo.lineOffset + mModel->mScrollPosition.y, - secondaryCursorInfo.lineHeight ); - } - - // Set the flag to update the decorator. - mEventData->mDecoratorUpdated = true; + SelectionHandleController::Reposition(*this); } - -void Controller::Impl::RepositionSelectionHandles( float visualX, float visualY, Controller::NoTextTap::Action action ) +void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action) { - if( NULL == mEventData ) - { - // Nothing to do if there is no text input. - return; - } - - if( IsShowingPlaceholderText() ) - { - // Nothing to do if there is the place-holder text. - return; - } - - const Length numberOfGlyphs = mModel->mVisualModel->mGlyphs.Count(); - const Length numberOfLines = mModel->mVisualModel->mLines.Count(); - if( ( 0 == numberOfGlyphs ) || - ( 0 == numberOfLines ) ) - { - // Nothing to do if there is no text. - return; - } - - // Find which word was selected - CharacterIndex selectionStart( 0 ); - CharacterIndex selectionEnd( 0 ); - CharacterIndex noTextHitIndex( 0 ); - const bool characterHit = FindSelectionIndices( mModel->mVisualModel, - mModel->mLogicalModel, - mMetrics, - visualX, - visualY, - selectionStart, - selectionEnd, - noTextHitIndex ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "%p selectionStart %d selectionEnd %d\n", this, selectionStart, selectionEnd ); - - if( characterHit || ( Controller::NoTextTap::HIGHLIGHT == action ) ) - { - ChangeState( EventData::SELECTING ); - - mEventData->mLeftSelectionPosition = selectionStart; - mEventData->mRightSelectionPosition = selectionEnd; - - mEventData->mUpdateLeftSelectionPosition = true; - 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 if( Controller::NoTextTap::SHOW_SELECTION_POPUP == action ) - { - // Nothing to select. i.e. a white space, out of bounds - ChangeState( EventData::EDITING_WITH_POPUP ); - - mEventData->mPrimaryCursorPosition = noTextHitIndex; - - 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; - mEventData->mScrollAfterUpdatePosition = true; - mEventData->mUpdateInputStyle = true; - } + SelectionHandleController::Reposition(*this, visualX, visualY, action); } void Controller::Impl::SetPopupButtons() @@ -2646,259 +1480,273 @@ void Controller::Impl::SetPopupButtons() * If EDITING_WITH_POPUP : SELECT & SELECT_ALL */ + bool isEditable = IsEditable(); TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE; - if( EventData::SELECTING == mEventData->mState ) + if(EventData::SELECTING == mEventData->mState) { - buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::CUT | TextSelectionPopup::COPY ); + buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY); + if(isEditable) + { + buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT); + } - if( !IsClipboardEmpty() ) + if(!IsClipboardEmpty()) { - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) ); - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) ); + if(isEditable) + { + buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE)); + } + buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD)); } - if( !mEventData->mAllTextSelected ) + if(!mEventData->mAllTextSelected) { - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::SELECT_ALL ) ); + buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL)); } } - else if( EventData::EDITING_WITH_POPUP == mEventData->mState ) + else if(EventData::EDITING_WITH_POPUP == mEventData->mState) { - if( mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText() ) + if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText()) { - buttonsToShow = TextSelectionPopup::Buttons( TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL ); + buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL); } - if( !IsClipboardEmpty() ) + if(!IsClipboardEmpty()) { - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) ); - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) ); + if(isEditable) + { + buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE)); + } + buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD)); } } - else if( EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState ) + else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState) { - if ( !IsClipboardEmpty() ) + if(!IsClipboardEmpty()) { - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::PASTE ) ); - buttonsToShow = TextSelectionPopup::Buttons ( ( buttonsToShow | TextSelectionPopup::CLIPBOARD ) ); + if(isEditable) + { + buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE)); + } + buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD)); } } - mEventData->mDecorator->SetEnabledPopupButtons( buttonsToShow ); + mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow); } -void Controller::Impl::ChangeState( EventData::State newState ) +void Controller::Impl::ChangeState(EventData::State newState) { - if( NULL == mEventData ) + if(nullptr == mEventData) { // Nothing to do if there is no text input. return; } - DALI_LOG_INFO( gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState ); + DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState); - if( mEventData->mState != newState ) + if(mEventData->mState != newState) { mEventData->mPreviousState = mEventData->mState; - mEventData->mState = newState; + mEventData->mState = newState; - switch( mEventData->mState ) + switch(mEventData->mState) { case EventData::INACTIVE: { - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE ); + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); mEventData->mDecorator->StopCursorBlink(); - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHighlightActive( false ); - mEventData->mDecorator->SetPopupActive( false ); + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHighlightActive(false); + mEventData->mDecorator->SetPopupActive(false); mEventData->mDecoratorUpdated = true; break; } case EventData::INTERRUPTED: { - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHighlightActive( false ); - mEventData->mDecorator->SetPopupActive( false ); + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHighlightActive(false); + mEventData->mDecorator->SetPopupActive(false); mEventData->mDecoratorUpdated = true; break; } case EventData::SELECTING: { - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE ); + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); mEventData->mDecorator->StopCursorBlink(); - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false ); - if ( mEventData->mGrabHandleEnabled ) + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); + if(mEventData->mGrabHandleEnabled) { - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true ); + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true); } - mEventData->mDecorator->SetHighlightActive( true ); - if( mEventData->mGrabHandlePopupEnabled ) + mEventData->mDecorator->SetHighlightActive(true); + if(mEventData->mGrabHandlePopupEnabled) { SetPopupButtons(); - mEventData->mDecorator->SetPopupActive( true ); + mEventData->mDecorator->SetPopupActive(true); } mEventData->mDecoratorUpdated = true; break; } case EventData::EDITING: { - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY ); - if( mEventData->mCursorBlinkEnabled ) + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(mEventData->mCursorBlinkEnabled) { mEventData->mDecorator->StartCursorBlink(); } // Grab handle is not shown until a tap is received whilst EDITING - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHighlightActive( false ); - if( mEventData->mGrabHandlePopupEnabled ) + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHighlightActive(false); + if(mEventData->mGrabHandlePopupEnabled) { - mEventData->mDecorator->SetPopupActive( false ); + mEventData->mDecorator->SetPopupActive(false); } mEventData->mDecoratorUpdated = true; break; } case EventData::EDITING_WITH_POPUP: { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState); - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY ); - if( mEventData->mCursorBlinkEnabled ) + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(mEventData->mCursorBlinkEnabled) { mEventData->mDecorator->StartCursorBlink(); } - if( mEventData->mSelectionEnabled ) + if(mEventData->mSelectionEnabled) { - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHighlightActive( false ); + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHighlightActive(false); } - else if ( mEventData->mGrabHandleEnabled ) + else if(mEventData->mGrabHandleEnabled) { - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); } - if( mEventData->mGrabHandlePopupEnabled ) + if(mEventData->mGrabHandlePopupEnabled) { SetPopupButtons(); - mEventData->mDecorator->SetPopupActive( true ); + mEventData->mDecorator->SetPopupActive(true); } mEventData->mDecoratorUpdated = true; break; } case EventData::EDITING_WITH_GRAB_HANDLE: { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState); - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY ); - if( mEventData->mCursorBlinkEnabled ) + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(mEventData->mCursorBlinkEnabled) { mEventData->mDecorator->StartCursorBlink(); } // Grab handle is not shown until a tap is received whilst EDITING - if ( mEventData->mGrabHandleEnabled ) + if(mEventData->mGrabHandleEnabled) { - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); } - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHighlightActive( false ); - if( mEventData->mGrabHandlePopupEnabled ) + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHighlightActive(false); + if(mEventData->mGrabHandlePopupEnabled) { - mEventData->mDecorator->SetPopupActive( false ); + mEventData->mDecorator->SetPopupActive(false); } mEventData->mDecoratorUpdated = true; break; } case EventData::SELECTION_HANDLE_PANNING: { - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE ); + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); mEventData->mDecorator->StopCursorBlink(); - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false ); - if ( mEventData->mGrabHandleEnabled ) + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); + if(mEventData->mGrabHandleEnabled) { - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, true ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, true ); + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true); } - mEventData->mDecorator->SetHighlightActive( true ); - if( mEventData->mGrabHandlePopupEnabled ) + mEventData->mDecorator->SetHighlightActive(true); + if(mEventData->mGrabHandlePopupEnabled) { - mEventData->mDecorator->SetPopupActive( false ); + mEventData->mDecorator->SetPopupActive(false); } mEventData->mDecoratorUpdated = true; break; } case EventData::GRAB_HANDLE_PANNING: { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState); - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY ); - if( mEventData->mCursorBlinkEnabled ) + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(mEventData->mCursorBlinkEnabled) { mEventData->mDecorator->StartCursorBlink(); } - if ( mEventData->mGrabHandleEnabled ) + if(mEventData->mGrabHandleEnabled) { - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); } - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHighlightActive( false ); - if( mEventData->mGrabHandlePopupEnabled ) + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHighlightActive(false); + if(mEventData->mGrabHandlePopupEnabled) { - mEventData->mDecorator->SetPopupActive( false ); + mEventData->mDecorator->SetPopupActive(false); } mEventData->mDecoratorUpdated = true; break; } case EventData::EDITING_WITH_PASTE_POPUP: { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState); - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY ); - if( mEventData->mCursorBlinkEnabled ) + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); + if(mEventData->mCursorBlinkEnabled) { mEventData->mDecorator->StartCursorBlink(); } - if ( mEventData->mGrabHandleEnabled ) + if(mEventData->mGrabHandleEnabled) { - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, true ); + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); } - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHighlightActive( false ); + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHighlightActive(false); - if( mEventData->mGrabHandlePopupEnabled ) + if(mEventData->mGrabHandlePopupEnabled) { SetPopupButtons(); - mEventData->mDecorator->SetPopupActive( true ); + mEventData->mDecorator->SetPopupActive(true); } mEventData->mDecoratorUpdated = true; break; } case EventData::TEXT_PANNING: { - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE ); + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); mEventData->mDecorator->StopCursorBlink(); - mEventData->mDecorator->SetHandleActive( GRAB_HANDLE, false ); - if( mEventData->mDecorator->IsHandleActive( LEFT_SELECTION_HANDLE ) || - mEventData->mDecorator->IsHandleActive( RIGHT_SELECTION_HANDLE ) ) + mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false); + if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) || + mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE)) { - mEventData->mDecorator->SetHandleActive( LEFT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHandleActive( RIGHT_SELECTION_HANDLE, false ); - mEventData->mDecorator->SetHighlightActive( true ); + mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); + mEventData->mDecorator->SetHighlightActive(true); } - if( mEventData->mGrabHandlePopupEnabled ) + if(mEventData->mGrabHandlePopupEnabled) { - mEventData->mDecorator->SetPopupActive( false ); + mEventData->mDecorator->SetPopupActive(false); } mEventData->mDecoratorUpdated = true; @@ -2908,10 +1756,10 @@ void Controller::Impl::ChangeState( EventData::State newState ) } } -void Controller::Impl::GetCursorPosition( CharacterIndex logical, - CursorInfo& cursorInfo ) +void Controller::Impl::GetCursorPosition(CharacterIndex logical, + CursorInfo& cursorInfo) { - if( !IsShowingRealText() ) + if(!IsShowingRealText()) { // Do not want to use the place-holder text to set the cursor position. @@ -2919,21 +1767,21 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical, // If there is no font's family set, use the default font. // Use the current alignment to place the cursor at the beginning, center or end of the box. - cursorInfo.lineOffset = 0.f; - cursorInfo.lineHeight = GetDefaultFontLineHeight(); + cursorInfo.lineOffset = 0.f; + cursorInfo.lineHeight = GetDefaultFontLineHeight(); cursorInfo.primaryCursorHeight = cursorInfo.lineHeight; bool isRTL = false; - if( mModel->mMatchSystemLanguageDirection ) + if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS) { isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT; } - switch( mModel->mHorizontalAlignment ) + switch(mModel->mHorizontalAlignment) { - case Text::HorizontalAlignment::BEGIN : + case Text::HorizontalAlignment::BEGIN: { - if( isRTL ) + if(isRTL) { cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth(); } @@ -2945,12 +1793,12 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical, } case Text::HorizontalAlignment::CENTER: { - cursorInfo.primaryPosition.x = floorf( 0.5f * mModel->mVisualModel->mControlSize.width ); + cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width); break; } case Text::HorizontalAlignment::END: { - if( isRTL ) + if(isRTL) { cursorInfo.primaryPosition.x = 0.f; } @@ -2966,47 +1814,47 @@ void Controller::Impl::GetCursorPosition( CharacterIndex logical, return; } - const bool isMultiLine = ( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() ); + const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout()); GetCursorPositionParameters parameters; - parameters.visualModel = mModel->mVisualModel; + parameters.visualModel = mModel->mVisualModel; parameters.logicalModel = mModel->mLogicalModel; - parameters.metrics = mMetrics; - parameters.logical = logical; - parameters.isMultiline = isMultiLine; + parameters.metrics = mMetrics; + parameters.logical = logical; + parameters.isMultiline = isMultiLine; - Text::GetCursorPosition( parameters, - cursorInfo ); + Text::GetCursorPosition(parameters, + cursorInfo); // Adds Outline offset. - const float outlineWidth = static_cast( mModel->GetOutlineWidth() ); + 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(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. // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control. // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line. - if( 0.f > cursorInfo.primaryPosition.x ) + if(0.f > cursorInfo.primaryPosition.x) { cursorInfo.primaryPosition.x = 0.f; } - const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast( mEventData->mDecorator->GetCursorWidth() ); - if( cursorInfo.primaryPosition.x > edgeWidth ) + const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast(mEventData->mDecorator->GetCursorWidth()); + if(cursorInfo.primaryPosition.x > edgeWidth) { cursorInfo.primaryPosition.x = edgeWidth; } } } -CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) const +CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const { - if( NULL == mEventData ) + if(nullptr == mEventData) { // Nothing to do if there is no text input. return 0u; @@ -3014,16 +1862,16 @@ CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition; - const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin(); - const Length* const charactersPerGlyphBuffer = mModel->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 ); + GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index); + Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex); - if( numberOfCharacters > 1u ) + if(numberOfCharacters > 1u) { - const Script script = mModel->mLogicalModel->GetScript( index ); - if( HasLigatureMustBreak( script ) ) + const Script script = mModel->mLogicalModel->GetScript(index); + if(HasLigatureMustBreak(script)) { // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ... numberOfCharacters = 1u; @@ -3031,14 +1879,14 @@ CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) } else { - while( 0u == numberOfCharacters ) + while(0u == numberOfCharacters) { ++glyphIndex; - numberOfCharacters = *( charactersPerGlyphBuffer + glyphIndex ); + numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex); } } - if( index < mEventData->mPrimaryCursorPosition ) + if(index < mEventData->mPrimaryCursorPosition) { cursorIndex -= numberOfCharacters; } @@ -3053,99 +1901,82 @@ CharacterIndex Controller::Impl::CalculateNewCursorIndex( CharacterIndex index ) return cursorIndex; } -void Controller::Impl::UpdateCursorPosition( const CursorInfo& cursorInfo ) +void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo) { - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this ); - if( NULL == mEventData ) + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this); + if(nullptr == mEventData) { // Nothing to do if there is no text input. - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n" ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n"); return; } const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition; - mEventData->mDecorator->SetGlyphOffset( PRIMARY_CURSOR, cursorInfo.glyphOffset ); + mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset); // Sets the cursor position. - mEventData->mDecorator->SetPosition( PRIMARY_CURSOR, - cursorPosition.x, - cursorPosition.y, - cursorInfo.primaryCursorHeight, - cursorInfo.lineHeight ); - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y ); + mEventData->mDecorator->SetPosition(PRIMARY_CURSOR, + cursorPosition.x, + cursorPosition.y, + cursorInfo.primaryCursorHeight, + cursorInfo.lineHeight); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y); - if( mEventData->mUpdateGrabHandlePosition ) + if(mEventData->mUpdateGrabHandlePosition) { // Sets the grab handle position. - mEventData->mDecorator->SetPosition( GRAB_HANDLE, - cursorPosition.x, - cursorInfo.lineOffset + mModel->mScrollPosition.y, - cursorInfo.lineHeight ); + mEventData->mDecorator->SetPosition(GRAB_HANDLE, + cursorPosition.x, + cursorInfo.lineOffset + mModel->mScrollPosition.y, + cursorInfo.lineHeight); } - if( cursorInfo.isSecondaryCursor ) + if(cursorInfo.isSecondaryCursor) { - mEventData->mDecorator->SetPosition( SECONDARY_CURSOR, - 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 + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y ); + mEventData->mDecorator->SetPosition(SECONDARY_CURSOR, + 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 + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y); } // Set which cursors are active according the state. - if( EventData::IsEditingState( mEventData->mState ) || ( EventData::GRAB_HANDLE_PANNING == mEventData->mState ) ) + if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState)) { - if( cursorInfo.isSecondaryCursor ) + if(cursorInfo.isSecondaryCursor) { - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_BOTH ); + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH); } else { - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_PRIMARY ); + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); } } else { - mEventData->mDecorator->SetActiveCursor( ACTIVE_CURSOR_NONE ); + mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); } - DALI_LOG_INFO( gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n" ); + DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n"); } -void Controller::Impl::UpdateSelectionHandle( HandleType handleType, - const CursorInfo& cursorInfo ) +void Controller::Impl::UpdateSelectionHandle(HandleType handleType, + const CursorInfo& cursorInfo) { - if( ( LEFT_SELECTION_HANDLE != handleType ) && - ( RIGHT_SELECTION_HANDLE != handleType ) ) - { - return; - } - - const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition; - - // Sets the handle's position. - mEventData->mDecorator->SetPosition( handleType, - cursorPosition.x, - 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 == mModel->mLogicalModel->mText.Count() ); + SelectionHandleController::Update(*this, handleType, cursorInfo); } -void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize ) +void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize) { // Clamp between -space & -alignment offset. - if( layoutSize.width > mModel->mVisualModel->mControlSize.width ) + if(layoutSize.width > mModel->mVisualModel->mControlSize.width) { - 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; + 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; } @@ -3155,20 +1986,20 @@ void Controller::Impl::ClampHorizontalScroll( const Vector2& layoutSize ) } } -void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize ) +void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize) { - if( Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout() ) + 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 > mModel->mVisualModel->mControlSize.height ) + if(layoutSize.height > mModel->mVisualModel->mControlSize.height) { - 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; + 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; } @@ -3178,9 +2009,9 @@ void Controller::Impl::ClampVerticalScroll( const Vector2& layoutSize ) } } -void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, float lineHeight ) +void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight) { - const float cursorWidth = mEventData->mDecorator ? static_cast( mEventData->mDecorator->GetCursorWidth() ) : 0.f; + const float cursorWidth = mEventData->mDecorator ? static_cast(mEventData->mDecorator->GetCursorWidth()) : 0.f; // position is in actor's coords. const float positionEndX = position.x + cursorWidth; @@ -3188,63 +2019,67 @@ void Controller::Impl::ScrollToMakePositionVisible( const Vector2& position, flo // Transform the position to decorator coords. const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x; - const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x; + const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x; const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y; - const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y; + const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y; - if( decoratorPositionBeginX < 0.f ) + if(decoratorPositionBeginX < 0.f) { mModel->mScrollPosition.x = -position.x; } - else if( decoratorPositionEndX > mModel->mVisualModel->mControlSize.width ) + else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width) { mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX; } - if( Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout() ) + if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout()) { - if( decoratorPositionBeginY < 0.f ) + if(decoratorPositionBeginY < 0.f) { mModel->mScrollPosition.y = -position.y; } - else if( decoratorPositionEndY > mModel->mVisualModel->mControlSize.height ) + else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height) { mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY; } } } -void Controller::Impl::ScrollTextToMatchCursor( const CursorInfo& cursorInfo ) +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 ); - + 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. mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x; //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 ) + 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; + const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR); + mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset; } - - ClampHorizontalScroll( mModel->mVisualModel->GetLayoutSize() ); - ClampVerticalScroll( mModel->mVisualModel->GetLayoutSize() ); + ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize()); + ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize()); // Makes the new cursor position visible if needed. - ScrollToMakePositionVisible( cursorInfo.primaryPosition, cursorInfo.lineHeight ); + ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight); +} + +void Controller::Impl::ScrollTextToMatchCursor() +{ + CursorInfo cursorInfo; + GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo); + ScrollTextToMatchCursor(cursorInfo); } void Controller::Impl::RequestRelayout() { - if( NULL != mControlInterface ) + if(nullptr != mControlInterface) { mControlInterface->RequestTextRelayout(); } @@ -3252,92 +2087,105 @@ void Controller::Impl::RequestRelayout() 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! + // NOTE: Currently we only support background color for left-to-right text. Actor actor; Length numberOfGlyphs = mView.GetNumberOfGlyphs(); - if( numberOfGlyphs > 0u ) + if(numberOfGlyphs > 0u) { Vector glyphs; - glyphs.Resize( numberOfGlyphs ); + glyphs.Resize(numberOfGlyphs); Vector positions; - positions.Resize( numberOfGlyphs ); + 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 ); + 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 ); + glyphs.Resize(numberOfGlyphs); + positions.Resize(numberOfGlyphs); - const GlyphInfo* const glyphsBuffer = glyphs.Begin(); - const Vector2* const positionsBuffer = positions.Begin(); + 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() ); + 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 Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors(); const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices(); - const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT; + const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT; - Vector4 quad; - uint32_t numberOfQuads = 0u; + Vector4 quad; + uint32_t numberOfQuads = 0u; + Length yLineOffset = 0; + Length prevLineIndex = 0; + LineIndex lineIndex; + Length numberOfLines; - for( uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i ) + for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i) { - const GlyphInfo& glyph = *( glyphsBuffer + 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 ); + const bool isMarkupBackground = mView.IsMarkupBackgroundColorSet(); + const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u; + const bool isDefaultBackgroundColor = (0u == backgroundColorIndex); + const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u); + + mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines); + Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing; + + if(lineIndex != prevLineIndex) + { + yLineOffset += lineHeight; + } // Only create quads for glyphs with a background color - if ( backgroundColor != Color::TRANSPARENT ) + if(backgroundColor != Color::TRANSPARENT) { - const Vector2 position = *( positionsBuffer + i ); + const Vector2 position = *(positionsBuffer + i); - if ( i == 0u && glyphSize == 1u ) // Only one glyph in the whole text + 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; + quad.y = yLineOffset; + quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width); + quad.w = lineHeight; } - else if ( i == 0u ) // The first glyph in the whole text + else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line { quad.x = position.x; - quad.y = 0.0f; + quad.y = yLineOffset; quad.z = quad.x - glyph.xBearing + glyph.advance; - quad.w = textSize.height; + quad.w = quad.y + lineHeight; } - else if ( i == glyphSize - 1u ) // The last glyph in the whole text + 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; + quad.y = yLineOffset; + quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width); + quad.w = quad.y + lineHeight; } else // The glyph in the middle of the text { quad.x = position.x - glyph.xBearing; - quad.y = 0.0f; + quad.y = yLineOffset; quad.z = quad.x + glyph.advance; - quad.w = textSize.height; + quad.w = quad.y + lineHeight; } BackgroundVertex vertex; @@ -3345,75 +2193,106 @@ Actor Controller::Impl::CreateBackgroundActor() // Top left vertex.mPosition.x = quad.x - offsetX; vertex.mPosition.y = quad.y - offsetY; - vertex.mColor = backgroundColor; - mesh.mVertices.PushBack( vertex ); + 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 ); + 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 ); + 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 ); + 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 ); + 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++; } + + if(lineIndex != prevLineIndex) + { + prevLineIndex = lineIndex; + } } // Only create the background actor if there are glyphs with background color - if ( mesh.mVertices.Count() > 0u ) + if(mesh.mVertices.Count() > 0u) { Property::Map quadVertexFormat; - quadVertexFormat[ "aPosition" ] = Property::VECTOR2; - quadVertexFormat[ "aColor" ] = Property::VECTOR4; + quadVertexFormat["aPosition"] = Property::VECTOR2; + quadVertexFormat["aColor"] = Property::VECTOR4; - VertexBuffer quadVertices = VertexBuffer::New( quadVertexFormat ); - quadVertices.SetData( &mesh.mVertices[ 0 ], mesh.mVertices.Size() ); + 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() ); + quadGeometry.AddVertexBuffer(quadVertices); + quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size()); - if( !mShaderBackground ) + if(!mShaderBackground) { - mShaderBackground = Shader::New( VERTEX_SHADER_BACKGROUND, FRAGMENT_SHADER_BACKGROUND ); + mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG); } - 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 ); + 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 ); + 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; } +void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns) +{ + //Underlined character runs for markup-processor + const Vector& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns; + const Vector& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph; + const Vector& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter; + + if(shouldClearPreUnderlineRuns) + { + mModel->mVisualModel->mUnderlineRuns.Clear(); + } + + for(Vector::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it) + { + CharacterIndex characterIndex = it->characterRun.characterIndex; + Length numberOfCharacters = it->characterRun.numberOfCharacters; + for(Length index = 0u; index < numberOfCharacters; index++) + { + GlyphRun underlineGlyphRun; + underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index]; + underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index]; + mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun); + } + } +} + } // namespace Text } // namespace Toolkit