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-text-updater.cpp;h=4d0ba8f015821bb1e73043500ad024a085de1b7a;hp=6fb472ecc9280e1e4e371fffa92763454e994c14;hb=21b574541aac2281c92020c0f62f7754897808b2;hpb=7f472b6fa56d5382514b9abb9a4c71419189acff diff --git a/dali-toolkit/internal/text/text-controller-text-updater.cpp b/dali-toolkit/internal/text/text-controller-text-updater.cpp index 6fb472e..4d0ba8f 100644 --- a/dali-toolkit/internal/text/text-controller-text-updater.cpp +++ b/dali-toolkit/internal/text/text-controller-text-updater.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Samsung Electronics Co., Ltd. + * Copyright (c) 2022 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. @@ -24,8 +24,11 @@ // INTERNAL INCLUDES #include +#include +#include #include #include +#include #include namespace @@ -55,7 +58,7 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string& ResetText(controller); // Remove the style. - controller.ClearStyleData(); + impl.ClearStyleData(); CharacterIndex lastCursorIndex = 0u; @@ -69,6 +72,11 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string& (EventData::EDITING_WITH_GRAB_HANDLE == eventData->mState) || (EventData::EDITING_WITH_PASTE_POPUP == eventData->mState)) { + if((impl.mSelectableControlInterface != nullptr) && (EventData::SELECTING == eventData->mState)) + { + impl.mSelectableControlInterface->SelectionChanged(eventData->mLeftSelectionPosition, eventData->mRightSelectionPosition, eventData->mPrimaryCursorPosition, eventData->mPrimaryCursorPosition); + } + impl.ChangeState(EventData::EDITING); } } @@ -81,7 +89,13 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string& MarkupProcessData markupProcessData(logicalModel->mColorRuns, logicalModel->mFontDescriptionRuns, - logicalModel->mEmbeddedItems); + logicalModel->mEmbeddedItems, + logicalModel->mAnchors, + logicalModel->mUnderlinedCharacterRuns, + logicalModel->mBackgroundColorRuns, + logicalModel->mStrikethroughCharacterRuns, + logicalModel->mBoundedParagraphRuns, + logicalModel->mCharacterSpacingCharacterRuns); Length textSize = 0u; const uint8_t* utf8 = NULL; @@ -133,14 +147,16 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string& } else { - controller.ShowPlaceholderText(); + PlaceholderHandler::ShowPlaceholderText(impl); } + unsigned int oldCursorPos = (nullptr != eventData ? eventData->mPrimaryCursorPosition : 0); + // Resets the cursor position. controller.ResetCursorPosition(lastCursorIndex); // Scrolls the text to make the cursor visible. - controller.ResetScrollPosition(); + impl.ResetScrollPosition(); impl.RequestRelayout(); @@ -153,6 +169,7 @@ void Controller::TextUpdater::SetText(Controller& controller, const std::string& // Do this last since it provides callbacks into application code. if(NULL != impl.mEditableControlInterface) { + impl.mEditableControlInterface->CursorPositionChanged(oldCursorPos, lastCursorIndex); impl.mEditableControlInterface->TextChanged(true); } } @@ -169,9 +186,10 @@ void Controller::TextUpdater::InsertText(Controller& controller, const std::stri return; } - bool removedPrevious = false; - bool removedSelected = false; - bool maxLengthReached = false; + bool removedPrevious = false; + bool removedSelected = false; + bool maxLengthReached = false; + unsigned int oldCursorPos = eventData->mPrimaryCursorPosition; DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Controller::InsertText %p %s (%s) mPrimaryCursorPosition %d mPreEditFlag %d mPreEditStartPosition %d mPreEditLength %d\n", &controller, text.c_str(), (COMMIT == type ? "COMMIT" : "PRE_EDIT"), eventData->mPrimaryCursorPosition, eventData->mPreEditFlag, eventData->mPreEditStartPosition, eventData->mPreEditLength); @@ -253,7 +271,8 @@ void Controller::TextUpdater::InsertText(Controller& controller, const std::stri const Length numberOfCharactersInModel = logicalModel->mText.Count(); // Restrict new text to fit within Maximum characters setting. - Length maxSizeOfNewText = std::min((impl.mMaximumNumberOfCharacters - numberOfCharactersInModel), characterCount); + Length temp_length = (impl.mMaximumNumberOfCharacters > numberOfCharactersInModel ? impl.mMaximumNumberOfCharacters - numberOfCharactersInModel : 0); + Length maxSizeOfNewText = std::min(temp_length, characterCount); maxLengthReached = (characterCount > maxSizeOfNewText); // The cursor position. @@ -337,7 +356,7 @@ void Controller::TextUpdater::InsertText(Controller& controller, const std::stri if(addFontSizeRun) { - fontDescriptionRun.size = static_cast(inputStyle.size * impl.mFontSizeScale * 64.f); + fontDescriptionRun.size = static_cast(inputStyle.size * impl.GetFontSizeScale() * 64.f); fontDescriptionRun.sizeDefined = true; } @@ -377,6 +396,11 @@ void Controller::TextUpdater::InsertText(Controller& controller, const std::stri textUpdateInfo.mNumberOfCharactersToAdd += maxSizeOfNewText; } + if(impl.mMarkupProcessorEnabled) + { + InsertTextAnchor(controller, maxSizeOfNewText, cursorIndex); + } + // Update the cursor index. cursorIndex += maxSizeOfNewText; @@ -387,7 +411,7 @@ void Controller::TextUpdater::InsertText(Controller& controller, const std::stri impl.IsPlaceholderAvailable()) { // Show place-holder if empty after removing the pre-edit text - controller.ShowPlaceholderText(); + PlaceholderHandler::ShowPlaceholderText(impl); eventData->mUpdateCursorPosition = true; impl.ClearPreEditFlag(); } @@ -409,6 +433,11 @@ void Controller::TextUpdater::InsertText(Controller& controller, const std::stri } } + if(nullptr != impl.mEditableControlInterface) + { + impl.mEditableControlInterface->CursorPositionChanged(oldCursorPos, eventData->mPrimaryCursorPosition); + } + if(maxLengthReached) { DALI_LOG_INFO(gLogFilter, Debug::Verbose, "MaxLengthReached (%d)\n", logicalModel->mText.Count()); @@ -444,6 +473,7 @@ bool Controller::TextUpdater::RemoveText( UpdateInputStyleType type) { bool removed = false; + bool removeAll = false; Controller::Impl& impl = *controller.mImpl; EventData*& eventData = impl.mEventData; @@ -455,14 +485,15 @@ bool Controller::TextUpdater::RemoveText( ModelPtr& model = impl.mModel; LogicalModelPtr& logicalModel = model->mLogicalModel; + VisualModelPtr& visualModel = model->mVisualModel; DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::RemoveText %p mText.Count() %d cursor %d cursorOffset %d numberOfCharacters %d\n", &controller, logicalModel->mText.Count(), eventData->mPrimaryCursorPosition, cursorOffset, numberOfCharacters); if(!impl.IsShowingPlaceholderText()) { // Delete at current cursor position - Vector& currentText = logicalModel->mText; - CharacterIndex& oldCursorIndex = eventData->mPrimaryCursorPosition; + Vector& currentText = logicalModel->mText; + CharacterIndex& previousCursorIndex = eventData->mPrimaryCursorPosition; CharacterIndex cursorIndex = 0; @@ -472,14 +503,38 @@ bool Controller::TextUpdater::RemoveText( cursorIndex = eventData->mPrimaryCursorPosition + cursorOffset; } + //Handle Emoji clustering for cursor handling + // Deletion case: this is handling the deletion cases when the cursor is before or after Emoji + // - Before: when use delete key and cursor is before Emoji (cursorOffset = -1) + // - After: when use backspace key and cursor is after Emoji (cursorOffset = 0) + + const Script script = logicalModel->GetScript(cursorIndex); + if((numberOfCharacters == 1u) && + (IsOneOfEmojiScripts(script))) + { + //TODO: Use this clustering for Emoji cases only. This needs more testing to generalize to all scripts. + CharacterRun emojiClusteredCharacters = RetrieveClusteredCharactersOfCharacterIndex(visualModel, logicalModel, cursorIndex); + Length actualNumberOfCharacters = emojiClusteredCharacters.numberOfCharacters; + + //Set cursorIndex at the first characterIndex of clustred Emoji + cursorIndex = emojiClusteredCharacters.characterIndex; + + numberOfCharacters = actualNumberOfCharacters; + } + if((cursorIndex + numberOfCharacters) > currentText.Count()) { numberOfCharacters = currentText.Count() - cursorIndex; } + if((cursorIndex == 0) && (currentText.Count() - numberOfCharacters == 0)) + { + removeAll = true; + } + TextUpdateInfo& textUpdateInfo = impl.mTextUpdateInfo; - if(eventData->mPreEditFlag || // If the preedit flag is enabled, it means two (or more) of them came together i.e. when two keys have been pressed at the same time. + if(eventData->mPreEditFlag || removeAll || // If the preedit flag is enabled, it means two (or more) of them came together i.e. when two keys have been pressed at the same time. ((cursorIndex + numberOfCharacters) <= textUpdateInfo.mPreviousNumberOfCharacters)) { // Mark the paragraphs to be updated. @@ -525,7 +580,7 @@ bool Controller::TextUpdater::RemoveText( // If the number of current text and the number of characters to be deleted are same, // it means all texts should be removed and all Preedit variables should be initialized. - if((currentText.Count() - numberOfCharacters == 0) && (cursorIndex == 0)) + if(removeAll) { impl.ClearPreEditFlag(); textUpdateInfo.mNumberOfCharactersToAdd = 0; @@ -547,8 +602,18 @@ bool Controller::TextUpdater::RemoveText( currentText.Erase(first, last); + if(impl.mMarkupProcessorEnabled) + { + RemoveTextAnchor(controller, cursorOffset, numberOfCharacters, previousCursorIndex); + } + + if(nullptr != impl.mEditableControlInterface) + { + impl.mEditableControlInterface->CursorPositionChanged(previousCursorIndex, cursorIndex); + } + // Cursor position retreat - oldCursorIndex = cursorIndex; + previousCursorIndex = cursorIndex; eventData->mScrollAfterDelete = true; @@ -558,6 +623,7 @@ bool Controller::TextUpdater::RemoveText( } DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::RemoveText %p removed %d\n", &controller, numberOfCharacters); + removeAll = false; removed = true; } } @@ -574,12 +640,30 @@ bool Controller::TextUpdater::RemoveSelectedText(Controller& controller) if(EventData::SELECTING == impl.mEventData->mState) { std::string removedString; + uint32_t oldSelStart = impl.mEventData->mLeftSelectionPosition; + uint32_t oldSelEnd = impl.mEventData->mRightSelectionPosition; + impl.RetrieveSelection(removedString, true); if(!removedString.empty()) { textRemoved = true; impl.ChangeState(EventData::EDITING); + + if(impl.mMarkupProcessorEnabled) + { + int cursorOffset = -1; + int numberOfCharacters = removedString.length(); + CharacterIndex& cursorIndex = impl.mEventData->mPrimaryCursorPosition; + CharacterIndex previousCursorIndex = cursorIndex + numberOfCharacters; + + RemoveTextAnchor(controller, cursorOffset, numberOfCharacters, previousCursorIndex); + } + + if(impl.mSelectableControlInterface != nullptr) + { + impl.mSelectableControlInterface->SelectionChanged(oldSelStart, oldSelEnd, impl.mEventData->mPrimaryCursorPosition, impl.mEventData->mPrimaryCursorPosition); + } } } @@ -597,6 +681,9 @@ void Controller::TextUpdater::ResetText(Controller& controller) // Reset the embedded images buffer. logicalModel->ClearEmbeddedImages(); + // Reset the anchors buffer. + logicalModel->ClearAnchors(); + // We have cleared everything including the placeholder-text impl.PlaceholderCleared(); @@ -617,6 +704,128 @@ void Controller::TextUpdater::ResetText(Controller& controller) impl.mOperationsPending = ALL_OPERATIONS; } +void Controller::TextUpdater::InsertTextAnchor(Controller& controller, int numberOfCharacters, CharacterIndex previousCursorIndex) +{ + Controller::Impl& impl = *controller.mImpl; + ModelPtr& model = impl.mModel; + LogicalModelPtr& logicalModel = model->mLogicalModel; + + for(auto& anchor : logicalModel->mAnchors) + { + if(anchor.endIndex < previousCursorIndex) // [anchor] CUR + { + continue; + } + if(anchor.startIndex < previousCursorIndex) // [anCURr] + { + anchor.endIndex += numberOfCharacters; + } + else // CUR [anchor] + { + anchor.startIndex += numberOfCharacters; + anchor.endIndex += numberOfCharacters; + } + DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::InsertTextAnchor[%p] Anchor[%s] start[%d] end[%d]\n", &controller, anchor.href, anchor.startIndex, anchor.endIndex); + } +} + +void Controller::TextUpdater::RemoveTextAnchor(Controller& controller, int cursorOffset, int numberOfCharacters, CharacterIndex previousCursorIndex) +{ + Controller::Impl& impl = *controller.mImpl; + ModelPtr& model = impl.mModel; + LogicalModelPtr& logicalModel = model->mLogicalModel; + Vector::Iterator it = logicalModel->mAnchors.Begin(); + + while(it != logicalModel->mAnchors.End()) + { + Anchor& anchor = *it; + + if(anchor.endIndex <= previousCursorIndex && cursorOffset == 0) // [anchor] CUR >> + { + // Nothing happens. + } + else if(anchor.endIndex <= previousCursorIndex && cursorOffset == -1) // [anchor] << CUR + { + int endIndex = anchor.endIndex; + int offset = previousCursorIndex - endIndex; + int index = endIndex - (numberOfCharacters - offset); + + if(index < endIndex) + { + endIndex = index; + } + + if((int)anchor.startIndex >= endIndex) + { + if(anchor.href) + { + delete[] anchor.href; + } + it = logicalModel->mAnchors.Erase(it); + continue; + } + else + { + anchor.endIndex = endIndex; + } + } + else if(anchor.startIndex >= previousCursorIndex && cursorOffset == -1) // << CUR [anchor] + { + anchor.startIndex -= numberOfCharacters; + anchor.endIndex -= numberOfCharacters; + } + else if(anchor.startIndex >= previousCursorIndex && cursorOffset == 0) // CUR >> [anchor] + { + int startIndex = anchor.startIndex; + int endIndex = anchor.endIndex; + int index = previousCursorIndex + numberOfCharacters - 1; + + if(startIndex > index) + { + anchor.startIndex -= numberOfCharacters; + anchor.endIndex -= numberOfCharacters; + } + else if(endIndex > index + 1) + { + anchor.endIndex -= numberOfCharacters; + } + else + { + if(anchor.href) + { + delete[] anchor.href; + } + it = logicalModel->mAnchors.Erase(it); + continue; + } + } + else if(cursorOffset == -1) // [<< CUR] + { + int startIndex = anchor.startIndex; + int index = previousCursorIndex - numberOfCharacters; + + if(startIndex >= index) + { + anchor.startIndex = index; + } + anchor.endIndex -= numberOfCharacters; + } + else if(cursorOffset == 0) // [CUR >>] + { + anchor.endIndex -= numberOfCharacters; + } + else + { + // When this condition is reached, someting is wrong. + DALI_LOG_ERROR("Controller::RemoveTextAnchor[%p] Invaild state cursorOffset[%d]\n", &controller, cursorOffset); + } + + DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::RemoveTextAnchor[%p] Anchor[%s] start[%d] end[%d]\n", &controller, anchor.href, anchor.startIndex, anchor.endIndex); + + it++; + } +} + } // namespace Text } // namespace Toolkit