X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=dali-toolkit%2Finternal%2Ftext%2Ftext-controller-impl.cpp;h=ec00903e4629998a6cb693e63ec79979b03cc116;hb=HEAD;hp=4334f0e6548fbe5c5735bec3ff160eee9bf072a2;hpb=4800a84a19c2fd33aea3174eae8e55297ee3a9af;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-toolkit/internal/text/text-controller-impl.cpp b/dali-toolkit/internal/text/text-controller-impl.cpp deleted file mode 100644 index 4334f0e..0000000 --- a/dali-toolkit/internal/text/text-controller-impl.cpp +++ /dev/null @@ -1,1846 +0,0 @@ -/* - * 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// CLASS HEADER -#include - -// EXTERNAL INCLUDES -#include -#include - -// INTERNAL INCLUDES -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Dali; - -namespace -{ -#if defined(DEBUG_ENABLED) -Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS"); -#endif - -struct BackgroundVertex -{ - Vector2 mPosition; ///< Vertex posiiton - Vector4 mColor; ///< Vertex color -}; - -struct BackgroundMesh -{ - Vector mVertices; ///< container of vertices - Vector mIndices; ///< container of indices -}; - -} // namespace - -namespace Dali::Toolkit::Text -{ - -namespace -{ - -void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const fontDefaults, const Vector4& textColor) -{ - // Sets the default text's color. - inputStyle.textColor = textColor; - 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.lineSpacing = 0.f; - - inputStyle.underlineProperties.clear(); - inputStyle.shadowProperties.clear(); - inputStyle.embossProperties.clear(); - inputStyle.outlineProperties.clear(); - - inputStyle.isFamilyDefined = false; - inputStyle.isWeightDefined = 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; - - // Sets the default font's family name, weight, width, slant and size. - if(fontDefaults) - { - if(fontDefaults->familyDefined) - { - inputStyle.familyName = fontDefaults->mFontDescription.family; - inputStyle.isFamilyDefined = true; - } - - if(fontDefaults->weightDefined) - { - inputStyle.weight = fontDefaults->mFontDescription.weight; - inputStyle.isWeightDefined = true; - } - - if(fontDefaults->widthDefined) - { - inputStyle.width = fontDefaults->mFontDescription.width; - inputStyle.isWidthDefined = true; - } - - if(fontDefaults->slantDefined) - { - inputStyle.slant = fontDefaults->mFontDescription.slant; - inputStyle.isSlantDefined = true; - } - - if(fontDefaults->sizeDefined) - { - inputStyle.size = fontDefaults->mDefaultPointSize; - inputStyle.isSizeDefined = true; - } - } -} -} // unnamed Namespace - -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). - 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), - mEditingEnabled(true) -{ -} - -bool Controller::Impl::ProcessInputEvents() -{ - return ControllerImplEventHandler::ProcessInputEvents(*this); -} - -void Controller::Impl::NotifyInputMethodContext() -{ - if(mEventData && mEventData->mInputMethodContext) - { - CharacterIndex cursorPosition = GetLogicalCursorPosition(); - - const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u); - - // Update the cursor position by removing the initial white spaces. - if(cursorPosition < numberOfWhiteSpaces) - { - cursorPosition = 0u; - } - else - { - cursorPosition -= numberOfWhiteSpaces; - } - - mEventData->mInputMethodContext.SetCursorPosition(cursorPosition); - mEventData->mInputMethodContext.NotifyCursorPosition(); - } -} - -void Controller::Impl::NotifyInputMethodContextMultiLineStatus() -{ - if(mEventData && mEventData->mInputMethodContext) - { - Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout(); - mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX); - } -} - -CharacterIndex Controller::Impl::GetLogicalCursorPosition() const -{ - CharacterIndex cursorPosition = 0u; - - if(mEventData) - { - if((EventData::SELECTING == mEventData->mState) || - (EventData::SELECTION_HANDLE_PANNING == mEventData->mState)) - { - cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition); - } - else - { - cursorPosition = mEventData->mPrimaryCursorPosition; - } - } - - return cursorPosition; -} - -Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const -{ - Length numberOfWhiteSpaces = 0u; - - // Get the buffer to the text. - Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin(); - - const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count(); - for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces) - { - if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index))) - { - break; - } - } - - return numberOfWhiteSpaces; -} - -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) - { - Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text); - } -} - -void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters) -{ - mTextUpdateInfo.mParagraphCharacterIndex = 0u; - mTextUpdateInfo.mStartGlyphIndex = 0u; - mTextUpdateInfo.mStartLineIndex = 0u; - numberOfCharacters = 0u; - - const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count(); - if(0u == numberOfParagraphs) - { - mTextUpdateInfo.mParagraphCharacterIndex = 0u; - numberOfCharacters = 0u; - - mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove; - - // Nothing else to do if there are no paragraphs. - return; - } - - // Find the paragraphs to be updated. - Vector paragraphsToBeUpdated; - if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters) - { - // Text is being added at the end of the current text. - 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.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove; - - mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count(); - mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u; - - // Nothing else to do; - return; - } - - paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u); - } - else - { - Length numberOfCharactersToUpdate = 0u; - if(mTextUpdateInfo.mFullRelayoutNeeded) - { - numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters; - } - else - { - numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u; - } - mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex, - numberOfCharactersToUpdate, - paragraphsToBeUpdated); - } - - if(0u != paragraphsToBeUpdated.Count()) - { - 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); - - 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); - - numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex; - } - else - { - numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex; - } - } - - mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove; - mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex); -} - -void Controller::Impl::ClearFullModelData(OperationsMask operations) -{ - if(NO_OPERATION != (GET_LINE_BREAKS & operations)) - { - mModel->mLogicalModel->mLineBreakInfo.Clear(); - mModel->mLogicalModel->mParagraphInfo.Clear(); - } - - if(NO_OPERATION != (GET_SCRIPTS & operations)) - { - mModel->mLogicalModel->mScriptRuns.Clear(); - } - - if(NO_OPERATION != (VALIDATE_FONTS & operations)) - { - mModel->mLogicalModel->mFontRuns.Clear(); - } - - if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count()) - { - if(NO_OPERATION != (BIDI_INFO & operations)) - { - mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear(); - mModel->mLogicalModel->mCharacterDirections.Clear(); - } - - if(NO_OPERATION != (REORDER & operations)) - { - // Free the allocated memory used to store the conversion table in the bidirectional line info run. - for(Vector::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(), - endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End(); - it != endIt; - ++it) - { - BidirectionalLineInfoRun& bidiLineInfo = *it; - - free(bidiLineInfo.visualToLogicalMap); - bidiLineInfo.visualToLogicalMap = NULL; - } - mModel->mLogicalModel->mBidirectionalLineInfo.Clear(); - } - } - - if(NO_OPERATION != (SHAPE_TEXT & operations)) - { - mModel->mVisualModel->mGlyphs.Clear(); - mModel->mVisualModel->mGlyphsToCharacters.Clear(); - mModel->mVisualModel->mCharactersToGlyph.Clear(); - mModel->mVisualModel->mCharactersPerGlyph.Clear(); - mModel->mVisualModel->mGlyphsPerCharacter.Clear(); - mModel->mVisualModel->mGlyphPositions.Clear(); - } - - if(NO_OPERATION != (LAYOUT & operations)) - { - mModel->mVisualModel->mLines.Clear(); - } - - if(NO_OPERATION != (COLOR & operations)) - { - mModel->mVisualModel->mColorIndices.Clear(); - mModel->mVisualModel->mBackgroundColorIndices.Clear(); - } -} - -void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations) -{ - const CharacterIndex endIndexPlusOne = endIndex + 1u; - - 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); - - // Clear the paragraphs. - ClearCharacterRuns(startIndex, - endIndex, - mModel->mLogicalModel->mParagraphInfo); - } - - if(NO_OPERATION != (GET_SCRIPTS & operations)) - { - // Clear the scripts. - ClearCharacterRuns(startIndex, - endIndex, - mModel->mLogicalModel->mScriptRuns); - } - - if(NO_OPERATION != (VALIDATE_FONTS & operations)) - { - // Clear the fonts. - ClearCharacterRuns(startIndex, - endIndex, - mModel->mLogicalModel->mFontRuns); - } - - if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count()) - { - if(NO_OPERATION != (BIDI_INFO & operations)) - { - // Clear the bidirectional paragraph info. - 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); - } - - if(NO_OPERATION != (REORDER & operations)) - { - uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count(); - 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) - { - BidirectionalLineInfoRun& bidiLineInfo = *it; - - free(bidiLineInfo.visualToLogicalMap); - bidiLineInfo.visualToLogicalMap = NULL; - } - - mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex, - bidirectionalLineInfoBuffer + endRemoveIndex); - } - } -} - -void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations) -{ - 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(); - - const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex); - const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex; - - 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) - { - CharacterIndex& index = *it; - index -= numberOfGlyphsRemoved; - } - - // Clear the character to glyph conversion table. - mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex, - charactersToGlyphBuffer + endIndexPlusOne); - - // Clear the glyphs per character table. - 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); - - 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) - { - CharacterIndex& index = *it; - index -= numberOfCharactersRemoved; - } - - // Clear the glyphs to characters buffer. - 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); - - // Should pass if mGlyphPositions has already been cleared in Controller::Relayouter::Relayout - if(0u != mModel->mVisualModel->mGlyphPositions.Count()) - { - // Clear the positions buffer. - Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin(); - mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex, - positionsBuffer + endGlyphIndexPlusOne); - } - } - - if(NO_OPERATION != (LAYOUT & operations)) - { - // Clear the lines. - uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count(); - 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); - - // 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); - } - - if(NO_OPERATION != (COLOR & operations)) - { - if(0u != mModel->mVisualModel->mColorIndices.Count()) - { - ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin(); - mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, - colorIndexBuffer + endGlyphIndexPlusOne); - } - - if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count()) - { - ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin(); - mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex, - backgroundColorIndexBuffer + endGlyphIndexPlusOne); - } - } -} - -void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations) -{ - if(mTextUpdateInfo.mClearAll || - ((0u == startIndex) && - (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u))) - { - ClearFullModelData(operations); - } - else - { - // Clear the model data related with characters. - ClearCharacterModelData(startIndex, endIndex, operations); - - // Clear the model data related with glyphs. - 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()); - - mModel->mVisualModel->ClearCaches(); -} - -bool Controller::Impl::UpdateModel(OperationsMask operationsRequired) -{ - return ControllerImplModelUpdater::Update(*this, operationsRequired); -} - -void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle) -{ - SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor); -} - -float Controller::Impl::GetDefaultFontLineHeight() -{ - FontId defaultFontId = 0u; - if(nullptr == mFontDefaults) - { - TextAbstraction::FontDescription fontDescription; - defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale); - } - else - { - defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale); - } - - Text::FontMetrics fontMetrics; - mMetrics->GetFontMetrics(defaultFontId, fontMetrics); - - return (fontMetrics.ascender - fontMetrics.descender); -} - -void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd) -{ - if(nullptr == mEventData) - { - // Nothing to do if there is no text. - return; - } - - if(mEventData->mSelectionEnabled && (pStart || pEnd)) - { - uint32_t length = static_cast(mModel->mLogicalModel->mText.Count()); - uint32_t oldStart = mEventData->mLeftSelectionPosition; - uint32_t oldEnd = mEventData->mRightSelectionPosition; - - if(pStart) - { - mEventData->mLeftSelectionPosition = std::min(*pStart, length); - } - if(pEnd) - { - mEventData->mRightSelectionPosition = std::min(*pEnd, length); - } - - if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition) - { - ChangeState(EventData::EDITING); - mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition; - mEventData->mUpdateCursorPosition = true; - } - else - { - ChangeState(EventData::SELECTING); - mEventData->mUpdateHighlightBox = true; - mEventData->mUpdateLeftSelectionPosition = true; - mEventData->mUpdateRightSelectionPosition = true; - } - - if(mSelectableControlInterface != nullptr) - { - mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); - } - } -} - -CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const -{ - if(nullptr == mEventData) - { - return 0; - } - return mEventData->mPrimaryCursorPosition; -} - -bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused) -{ - if(nullptr == mEventData) - { - // Nothing to do if there is no text. - return false; - } - - if(mEventData->mPrimaryCursorPosition == index && mEventData->mState != EventData::SELECTING) - { - // Nothing for same cursor position. - return false; - } - - 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(mSelectableControlInterface != nullptr && wasInSelectingState) - { - mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition); - } - - ScrollTextToMatchCursor(); - } - - if(nullptr != mEditableControlInterface) - { - mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition); - } - - return true; -} - -Uint32Pair Controller::Impl::GetTextSelectionRange() const -{ - Uint32Pair range; - - if(mEventData) - { - range.first = mEventData->mLeftSelectionPosition; - range.second = mEventData->mRightSelectionPosition; - } - - return range; -} - -bool Controller::Impl::IsEditable() const -{ - return mEventData && mEventData->mEditingEnabled; -} - -void Controller::Impl::SetEditable(bool editable) -{ - if(mEventData) - { - mEventData->mEditingEnabled = editable; - - if(mEventData->mDecorator) - { - mEventData->mDecorator->SetEditable(editable); - } - } -} - -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); - - // Set as input style the style of the first deleted character. - mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, 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); - } - - mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast(lengthOfSelectedText)); - - // Mark the paragraphs to be updated. - if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout()) - { - mTextUpdateInfo.mCharacterIndex = 0; - mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters; - mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText; - mTextUpdateInfo.mClearAll = true; - } - else - { - mTextUpdateInfo.mCharacterIndex = startOfSelectedText; - mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText; - } - - // Delete text between handles - Vector::Iterator first = utf32Characters.Begin() + startOfSelectedText; - Vector::Iterator last = first + lengthOfSelectedText; - utf32Characters.Erase(first, last); - - // Will show the cursor at the first character of the selection. - mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition; - } - else - { - // Will show the cursor at the last character of the selection. - mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition; - } - - mEventData->mDecoratorUpdated = true; - } -} - -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) - { - mClipboard.ShowClipboard(); - } -} - -void Controller::Impl::HideClipboard() -{ - if(mClipboard && mClipboardHideEnabled) - { - mClipboard.HideClipboard(); - } -} - -void Controller::Impl::SetClipboardHideEnable(bool enable) -{ - mClipboardHideEnabled = enable; -} - -bool Controller::Impl::CopyStringToClipboard(const std::string& source) -{ - //Send string to clipboard - return (mClipboard && mClipboard.SetItem(source)); -} - -void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending) -{ - std::string selectedText; - RetrieveSelection(selectedText, deleteAfterSending); - CopyStringToClipboard(selectedText); - ChangeState(EventData::EDITING); -} - -void Controller::Impl::RequestGetTextFromClipboard() -{ - if(mClipboard) - { - mClipboard.RequestItem(); - } -} - -void Controller::Impl::RepositionSelectionHandles() -{ - SelectionHandleController::Reposition(*this); -} -void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action) -{ - SelectionHandleController::Reposition(*this, visualX, visualY, action); -} - -void Controller::Impl::SetPopupButtons() -{ - /** - * Sets the Popup buttons to be shown depending on State. - * - * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste ) - * - * If EDITING_WITH_POPUP : SELECT & SELECT_ALL - */ - - bool isEditable = IsEditable(); - TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE; - - if(EventData::SELECTING == mEventData->mState) - { - buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY); - if(isEditable) - { - buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT); - } - - if(!IsClipboardEmpty()) - { - if(isEditable) - { - buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE)); - } - buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD)); - } - - if(!mEventData->mAllTextSelected) - { - buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL)); - } - } - else if(EventData::EDITING_WITH_POPUP == mEventData->mState) - { - if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText()) - { - buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL); - } - - if(!IsClipboardEmpty()) - { - if(isEditable) - { - buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE)); - } - buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD)); - } - } - else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState) - { - if(!IsClipboardEmpty()) - { - if(isEditable) - { - buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE)); - } - buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD)); - } - } - - mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow); -} - -void Controller::Impl::ChangeState(EventData::State newState) -{ - 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); - - if(mEventData->mState != newState) - { - mEventData->mPreviousState = mEventData->mState; - mEventData->mState = newState; - - switch(mEventData->mState) - { - case EventData::INACTIVE: - { - 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->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->mDecoratorUpdated = true; - break; - } - case EventData::SELECTING: - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); - mEventData->mDecorator->StopCursorBlink(); - 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->SetHighlightActive(true); - if(mEventData->mGrabHandlePopupEnabled) - { - SetPopupButtons(); - mEventData->mDecorator->SetPopupActive(true); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::EDITING: - { - 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->SetPopupActive(false); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::EDITING_WITH_POPUP: - { - DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState); - - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); - if(mEventData->mCursorBlinkEnabled) - { - mEventData->mDecorator->StartCursorBlink(); - } - if(mEventData->mSelectionEnabled) - { - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - } - else if(mEventData->mGrabHandleEnabled) - { - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); - } - if(mEventData->mGrabHandlePopupEnabled) - { - SetPopupButtons(); - 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); - - 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) - { - 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->SetPopupActive(false); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::SELECTION_HANDLE_PANNING: - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); - mEventData->mDecorator->StopCursorBlink(); - 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->SetHighlightActive(true); - if(mEventData->mGrabHandlePopupEnabled) - { - mEventData->mDecorator->SetPopupActive(false); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::GRAB_HANDLE_PANNING: - { - DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState); - - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); - if(mEventData->mCursorBlinkEnabled) - { - mEventData->mDecorator->StartCursorBlink(); - } - if(mEventData->mGrabHandleEnabled) - { - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); - } - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - if(mEventData->mGrabHandlePopupEnabled) - { - 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); - - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); - if(mEventData->mCursorBlinkEnabled) - { - mEventData->mDecorator->StartCursorBlink(); - } - - if(mEventData->mGrabHandleEnabled) - { - mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true); - } - mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(false); - - if(mEventData->mGrabHandlePopupEnabled) - { - SetPopupButtons(); - mEventData->mDecorator->SetPopupActive(true); - } - mEventData->mDecoratorUpdated = true; - break; - } - case EventData::TEXT_PANNING: - { - 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(LEFT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false); - mEventData->mDecorator->SetHighlightActive(true); - } - - if(mEventData->mGrabHandlePopupEnabled) - { - mEventData->mDecorator->SetPopupActive(false); - } - - mEventData->mDecoratorUpdated = true; - break; - } - } - } -} - -void Controller::Impl::GetCursorPosition(CharacterIndex logical, - CursorInfo& cursorInfo) -{ - if(!IsShowingRealText()) - { - // Do not want to use the place-holder text to set the cursor position. - - // Use the line's height of the font's family set to set the cursor's size. - // 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.primaryCursorHeight = cursorInfo.lineHeight; - - bool isRTL = false; - if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS) - { - isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT; - } - - switch(mModel->mHorizontalAlignment) - { - case Text::HorizontalAlignment::BEGIN: - { - if(isRTL) - { - cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth(); - } - else - { - cursorInfo.primaryPosition.x = 0.f; - } - break; - } - case Text::HorizontalAlignment::CENTER: - { - cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width); - break; - } - case Text::HorizontalAlignment::END: - { - if(isRTL) - { - cursorInfo.primaryPosition.x = 0.f; - } - else - { - cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth(); - } - break; - } - } - - // Nothing else to do. - return; - } - - const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout()); - GetCursorPositionParameters parameters; - parameters.visualModel = mModel->mVisualModel; - parameters.logicalModel = mModel->mLogicalModel; - parameters.metrics = mMetrics; - parameters.logical = logical; - parameters.isMultiline = isMultiLine; - - Text::GetCursorPosition(parameters, - cursorInfo); - - // Adds Outline offset. - const float outlineWidth = static_cast(mModel->GetOutlineWidth()); - cursorInfo.primaryPosition.x += outlineWidth; - cursorInfo.primaryPosition.y += outlineWidth; - cursorInfo.secondaryPosition.x += outlineWidth; - cursorInfo.secondaryPosition.y += outlineWidth; - - if(isMultiLine) - { - // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control. - - // 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) - { - cursorInfo.primaryPosition.x = 0.f; - } - - 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 -{ - if(nullptr == mEventData) - { - // Nothing to do if there is no text input. - return 0u; - } - - CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition; - - const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin(); - const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin(); - - GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index); - Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex); - - if(numberOfCharacters > 1u) - { - const Script script = mModel->mLogicalModel->GetScript(index); - if(HasLigatureMustBreak(script)) - { - // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ... - numberOfCharacters = 1u; - } - } - else - { - while(0u == numberOfCharacters) - { - ++glyphIndex; - numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex); - } - } - - if(index < mEventData->mPrimaryCursorPosition) - { - cursorIndex -= numberOfCharacters; - } - else - { - cursorIndex += numberOfCharacters; - } - - // Will update the cursor hook position. - mEventData->mUpdateCursorHookPosition = true; - - return cursorIndex; -} - -void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo) -{ - 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"); - return; - } - - const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition; - - 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); - - if(mEventData->mUpdateGrabHandlePosition) - { - // Sets the grab handle position. - mEventData->mDecorator->SetPosition(GRAB_HANDLE, - cursorPosition.x, - cursorInfo.lineOffset + mModel->mScrollPosition.y, - cursorInfo.lineHeight); - } - - 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); - } - - // Set which cursors are active according the state. - if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState)) - { - if(cursorInfo.isSecondaryCursor) - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH); - } - else - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY); - } - } - else - { - mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE); - } - - DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n"); -} - -void Controller::Impl::UpdateSelectionHandle(HandleType handleType, - const CursorInfo& cursorInfo) -{ - SelectionHandleController::Update(*this, handleType, cursorInfo); -} - -void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize) -{ - // Clamp between -space & -alignment offset. - - 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; - - mEventData->mDecoratorUpdated = true; - } - else - { - mModel->mScrollPosition.x = 0.f; - } -} - -void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize) -{ - if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout()) - { - // Nothing to do if the text is single line. - return; - } - - // Clamp between -space & 0. - if(layoutSize.height > 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; - - mEventData->mDecoratorUpdated = true; - } - else - { - mModel->mScrollPosition.y = 0.f; - } -} - -void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight) -{ - const float cursorWidth = mEventData->mDecorator ? static_cast(mEventData->mDecorator->GetCursorWidth()) : 0.f; - - // position is in actor's coords. - const float positionEndX = position.x + cursorWidth; - const float positionEndY = position.y + lineHeight; - - // Transform the position to decorator coords. - const float decoratorPositionBeginX = position.x + 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; - - if(decoratorPositionBeginX < 0.f) - { - mModel->mScrollPosition.x = -position.x; - } - else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width) - { - mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX; - } - - if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout()) - { - if(decoratorPositionBeginY < 0.f) - { - mModel->mScrollPosition.y = -position.y; - } - else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height) - { - mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY; - } - } -} - -void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo) -{ - // Get the current cursor position in decorator coords. - const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR); - - const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition); - - // Calculate the offset to match the cursor position before the character was deleted. - 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) - { - const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR); - mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset; - } - - ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize()); - ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize()); - - // Makes the new cursor position visible if needed. - ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight); -} - -void Controller::Impl::ScrollTextToMatchCursor() -{ - CursorInfo cursorInfo; - GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo); - ScrollTextToMatchCursor(cursorInfo); -} - -void Controller::Impl::RequestRelayout() -{ - if(nullptr != mControlInterface) - { - mControlInterface->RequestTextRelayout(); - } -} - -Actor Controller::Impl::CreateBackgroundActor() -{ - // NOTE: Currently we only support background color for left-to-right text. - - Actor actor; - - Length numberOfGlyphs = mView.GetNumberOfGlyphs(); - if(numberOfGlyphs > 0u) - { - Vector glyphs; - glyphs.Resize(numberOfGlyphs); - - Vector positions; - positions.Resize(numberOfGlyphs); - - // Get the line where the glyphs are laid-out. - const LineRun* lineRun = mModel->mVisualModel->mLines.Begin(); - float alignmentOffset = lineRun->alignmentOffset; - numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(), - positions.Begin(), - alignmentOffset, - 0u, - numberOfGlyphs); - - glyphs.Resize(numberOfGlyphs); - positions.Resize(numberOfGlyphs); - - const GlyphInfo* const glyphsBuffer = glyphs.Begin(); - const Vector2* const positionsBuffer = positions.Begin(); - - BackgroundMesh mesh; - mesh.mVertices.Reserve(4u * glyphs.Size()); - mesh.mIndices.Reserve(6u * glyphs.Size()); - - const Vector2 textSize = mView.GetLayoutSize(); - - const float offsetX = alignmentOffset + textSize.width * 0.5f; - const float offsetY = textSize.height * 0.5f; - - const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors(); - const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices(); - const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT; - - Vector4 quad; - uint32_t numberOfQuads = 0u; - Length yLineOffset = 0; - Length prevLineIndex = 0; - LineIndex lineIndex; - Length numberOfLines; - - for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i) - { - const GlyphInfo& glyph = *(glyphsBuffer + i); - - // Get the background color of the character. - // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT) - const 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) - { - const Vector2 position = *(positionsBuffer + i); - - if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text - { - quad.x = position.x; - quad.y = yLineOffset; - quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width); - quad.w = lineHeight; - } - else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line - { - quad.x = position.x; - quad.y = yLineOffset; - quad.z = quad.x - glyph.xBearing + glyph.advance; - quad.w = quad.y + lineHeight; - } - else if(i == glyphSize - 1u) // The last glyph in the whole text - { - quad.x = position.x - glyph.xBearing; - 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 = yLineOffset; - quad.z = quad.x + glyph.advance; - quad.w = quad.y + lineHeight; - } - - BackgroundVertex vertex; - - // Top left - vertex.mPosition.x = quad.x - offsetX; - vertex.mPosition.y = quad.y - offsetY; - vertex.mColor = backgroundColor; - mesh.mVertices.PushBack(vertex); - - // Top right - vertex.mPosition.x = quad.z - offsetX; - vertex.mPosition.y = quad.y - offsetY; - vertex.mColor = backgroundColor; - mesh.mVertices.PushBack(vertex); - - // Bottom left - vertex.mPosition.x = quad.x - offsetX; - vertex.mPosition.y = quad.w - offsetY; - vertex.mColor = backgroundColor; - mesh.mVertices.PushBack(vertex); - - // Bottom right - vertex.mPosition.x = quad.z - offsetX; - vertex.mPosition.y = quad.w - offsetY; - vertex.mColor = backgroundColor; - mesh.mVertices.PushBack(vertex); - - // Six indices in counter clockwise winding - mesh.mIndices.PushBack(1u + 4 * numberOfQuads); - mesh.mIndices.PushBack(0u + 4 * numberOfQuads); - mesh.mIndices.PushBack(2u + 4 * numberOfQuads); - mesh.mIndices.PushBack(2u + 4 * numberOfQuads); - mesh.mIndices.PushBack(3u + 4 * numberOfQuads); - mesh.mIndices.PushBack(1u + 4 * numberOfQuads); - - numberOfQuads++; - } - - if(lineIndex != prevLineIndex) - { - prevLineIndex = lineIndex; - } - } - - // Only create the background actor if there are glyphs with background color - if(mesh.mVertices.Count() > 0u) - { - Property::Map quadVertexFormat; - quadVertexFormat["aPosition"] = Property::VECTOR2; - quadVertexFormat["aColor"] = Property::VECTOR4; - - VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat); - quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size()); - - Geometry quadGeometry = Geometry::New(); - quadGeometry.AddVertexBuffer(quadVertices); - quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size()); - - if(!mShaderBackground) - { - mShaderBackground = Shader::New(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); - - actor = Actor::New(); - actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor"); - actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT); - actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT); - actor.SetProperty(Actor::Property::SIZE, textSize); - actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR); - actor.AddRenderer(renderer); - } - } - - return actor; -} - -void Controller::Impl::RelayoutForNewLineSize() -{ - // relayout all characters - mTextUpdateInfo.mCharacterIndex = 0; - mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters; - mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count(); - mOperationsPending = static_cast(mOperationsPending | LAYOUT); - - //remove selection - if(mEventData && mEventData->mState == EventData::SELECTING) - { - ChangeState(EventData::EDITING); - } - - RequestRelayout(); -} - -bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty() -{ - return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count()); -} - -void Controller::Impl::ProcessInputStyleChangedSignals() -{ - if(mEventData) - { - if(mEditableControlInterface) - { - // Emit the input style changed signal for each mask - std::for_each(mEventData->mInputStyleChangedQueue.begin(), - mEventData->mInputStyleChangedQueue.end(), - [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); } ); - } - - mEventData->mInputStyleChangedQueue.Clear(); - } -} - -void Controller::Impl::ScrollBy(Vector2 scroll) -{ - if(mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0)) - { - const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize(); - const Vector2 currentScroll = mModel->mScrollPosition; - - scroll.x = -scroll.x; - scroll.y = -scroll.y; - - if(fabs(scroll.x) > Math::MACHINE_EPSILON_0) - { - mModel->mScrollPosition.x += scroll.x; - ClampHorizontalScroll(layoutSize); - } - - if(fabs(scroll.y) > Math::MACHINE_EPSILON_0) - { - mModel->mScrollPosition.y += scroll.y; - ClampVerticalScroll(layoutSize); - } - - if(mModel->mScrollPosition != currentScroll) - { - mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll); - RequestRelayout(); - } - } -} - -float Controller::Impl::GetHorizontalScrollPosition() -{ - // Scroll values are negative internally so we convert them to positive numbers - return mEventData ? -mModel->mScrollPosition.x : 0.0f; -} - -float Controller::Impl::GetVerticalScrollPosition() -{ - // Scroll values are negative internally so we convert them to positive numbers - return mEventData ? -mModel->mScrollPosition.y : 0.0f; -} - -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 Dali::Toolkit::Text