2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-toolkit/internal/text/text-controller-impl.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/rendering/renderer.h>
26 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
27 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
30 #include <dali-toolkit/internal/text/text-control-interface.h>
31 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
32 #include <dali-toolkit/internal/text/text-controller-impl-model-updater.h>
33 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
34 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
35 #include <dali-toolkit/internal/text/text-run-container.h>
36 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
42 #if defined(DEBUG_ENABLED)
43 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
46 struct BackgroundVertex
48 Vector2 mPosition; ///< Vertex posiiton
49 Vector4 mColor; ///< Vertex color
54 Vector<BackgroundVertex> mVertices; ///< container of vertices
55 Vector<unsigned short> mIndices; ///< container of indices
60 namespace Dali::Toolkit::Text
66 void SetDefaultInputStyle(InputStyle& inputStyle, const FontDefaults* const fontDefaults, const Vector4& textColor)
68 // Sets the default text's color.
69 inputStyle.textColor = textColor;
70 inputStyle.isDefaultColor = true;
72 inputStyle.familyName.clear();
73 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
74 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
75 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
76 inputStyle.size = 0.f;
78 inputStyle.lineSpacing = 0.f;
80 inputStyle.underlineProperties.clear();
81 inputStyle.shadowProperties.clear();
82 inputStyle.embossProperties.clear();
83 inputStyle.outlineProperties.clear();
85 inputStyle.isFamilyDefined = false;
86 inputStyle.isWeightDefined = false;
87 inputStyle.isWidthDefined = false;
88 inputStyle.isSlantDefined = false;
89 inputStyle.isSizeDefined = false;
91 inputStyle.isLineSpacingDefined = false;
93 inputStyle.isUnderlineDefined = false;
94 inputStyle.isShadowDefined = false;
95 inputStyle.isEmbossDefined = false;
96 inputStyle.isOutlineDefined = false;
98 // Sets the default font's family name, weight, width, slant and size.
101 if(fontDefaults->familyDefined)
103 inputStyle.familyName = fontDefaults->mFontDescription.family;
104 inputStyle.isFamilyDefined = true;
107 if(fontDefaults->weightDefined)
109 inputStyle.weight = fontDefaults->mFontDescription.weight;
110 inputStyle.isWeightDefined = true;
113 if(fontDefaults->widthDefined)
115 inputStyle.width = fontDefaults->mFontDescription.width;
116 inputStyle.isWidthDefined = true;
119 if(fontDefaults->slantDefined)
121 inputStyle.slant = fontDefaults->mFontDescription.slant;
122 inputStyle.isSlantDefined = true;
125 if(fontDefaults->sizeDefined)
127 inputStyle.size = fontDefaults->mDefaultPointSize;
128 inputStyle.isSizeDefined = true;
132 } // unnamed Namespace
134 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
135 : mDecorator(decorator),
136 mInputMethodContext(inputMethodContext),
137 mPlaceholderFont(nullptr),
138 mPlaceholderTextActive(),
139 mPlaceholderTextInactive(),
140 mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
142 mInputStyleChangedQueue(),
143 mPreviousState(INACTIVE),
145 mPrimaryCursorPosition(0u),
146 mLeftSelectionPosition(0u),
147 mRightSelectionPosition(0u),
148 mPreEditStartPosition(0u),
150 mCursorHookPositionX(0.f),
151 mDoubleTapAction(Controller::NoTextTap::NO_ACTION),
152 mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP),
153 mIsShowingPlaceholderText(false),
155 mDecoratorUpdated(false),
156 mCursorBlinkEnabled(true),
157 mGrabHandleEnabled(true),
158 mGrabHandlePopupEnabled(true),
159 mSelectionEnabled(true),
160 mUpdateCursorHookPosition(false),
161 mUpdateCursorPosition(false),
162 mUpdateGrabHandlePosition(false),
163 mUpdateLeftSelectionPosition(false),
164 mUpdateRightSelectionPosition(false),
165 mIsLeftHandleSelected(false),
166 mIsRightHandleSelected(false),
167 mUpdateHighlightBox(false),
168 mScrollAfterUpdatePosition(false),
169 mScrollAfterDelete(false),
170 mAllTextSelected(false),
171 mUpdateInputStyle(false),
172 mPasswordInput(false),
173 mCheckScrollAmount(false),
174 mIsPlaceholderPixelSize(false),
175 mIsPlaceholderElideEnabled(false),
176 mPlaceholderEllipsisFlag(false),
177 mShiftSelectionFlag(true),
178 mUpdateAlignment(false),
179 mEditingEnabled(true)
183 bool Controller::Impl::ProcessInputEvents()
185 return ControllerImplEventHandler::ProcessInputEvents(*this);
188 void Controller::Impl::NotifyInputMethodContext()
190 if(mEventData && mEventData->mInputMethodContext)
192 CharacterIndex cursorPosition = GetLogicalCursorPosition();
194 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
196 // Update the cursor position by removing the initial white spaces.
197 if(cursorPosition < numberOfWhiteSpaces)
203 cursorPosition -= numberOfWhiteSpaces;
206 mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
207 mEventData->mInputMethodContext.NotifyCursorPosition();
211 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
213 if(mEventData && mEventData->mInputMethodContext)
215 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
216 mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
220 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
222 CharacterIndex cursorPosition = 0u;
226 if((EventData::SELECTING == mEventData->mState) ||
227 (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
229 cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
233 cursorPosition = mEventData->mPrimaryCursorPosition;
237 return cursorPosition;
240 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
242 Length numberOfWhiteSpaces = 0u;
244 // Get the buffer to the text.
245 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
247 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
248 for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
250 if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
256 return numberOfWhiteSpaces;
259 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
261 // Get the total number of characters.
262 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
264 // Retrieve the text.
265 if(0u != numberOfCharacters)
267 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
271 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
273 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
274 mTextUpdateInfo.mStartGlyphIndex = 0u;
275 mTextUpdateInfo.mStartLineIndex = 0u;
276 numberOfCharacters = 0u;
278 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
279 if(0u == numberOfParagraphs)
281 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
282 numberOfCharacters = 0u;
284 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
286 // Nothing else to do if there are no paragraphs.
290 // Find the paragraphs to be updated.
291 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
292 if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
294 // Text is being added at the end of the current text.
295 if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
297 // Text is being added in a new paragraph after the last character of the text.
298 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
299 numberOfCharacters = 0u;
300 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
302 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
303 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
305 // Nothing else to do;
309 paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
313 Length numberOfCharactersToUpdate = 0u;
314 if(mTextUpdateInfo.mFullRelayoutNeeded)
316 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
320 numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
322 mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
323 numberOfCharactersToUpdate,
324 paragraphsToBeUpdated);
327 if(0u != paragraphsToBeUpdated.Count())
329 const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
330 const ParagraphRun& firstParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
331 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
333 ParagraphRunIndex lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
334 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
336 if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) && // Some character are removed.
337 (lastParagraphIndex < numberOfParagraphs - 1u) && // There is a next paragraph.
338 ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
339 (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
341 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
342 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
344 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
348 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
352 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
353 mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
356 void Controller::Impl::ClearFullModelData(OperationsMask operations)
358 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
360 mModel->mLogicalModel->mLineBreakInfo.Clear();
361 mModel->mLogicalModel->mParagraphInfo.Clear();
364 if(NO_OPERATION != (GET_SCRIPTS & operations))
366 mModel->mLogicalModel->mScriptRuns.Clear();
369 if(NO_OPERATION != (VALIDATE_FONTS & operations))
371 mModel->mLogicalModel->mFontRuns.Clear();
374 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
376 if(NO_OPERATION != (BIDI_INFO & operations))
378 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
379 mModel->mLogicalModel->mCharacterDirections.Clear();
382 if(NO_OPERATION != (REORDER & operations))
384 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
385 for(Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
386 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
390 BidirectionalLineInfoRun& bidiLineInfo = *it;
392 free(bidiLineInfo.visualToLogicalMap);
393 bidiLineInfo.visualToLogicalMap = NULL;
395 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
399 if(NO_OPERATION != (SHAPE_TEXT & operations))
401 mModel->mVisualModel->mGlyphs.Clear();
402 mModel->mVisualModel->mGlyphsToCharacters.Clear();
403 mModel->mVisualModel->mCharactersToGlyph.Clear();
404 mModel->mVisualModel->mCharactersPerGlyph.Clear();
405 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
406 mModel->mVisualModel->mGlyphPositions.Clear();
409 if(NO_OPERATION != (LAYOUT & operations))
411 mModel->mVisualModel->mLines.Clear();
414 if(NO_OPERATION != (COLOR & operations))
416 mModel->mVisualModel->mColorIndices.Clear();
417 mModel->mVisualModel->mBackgroundColorIndices.Clear();
421 void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
423 const CharacterIndex endIndexPlusOne = endIndex + 1u;
425 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
427 // Clear the line break info.
428 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
430 mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
431 lineBreakInfoBuffer + endIndexPlusOne);
433 // Clear the paragraphs.
434 ClearCharacterRuns(startIndex,
436 mModel->mLogicalModel->mParagraphInfo);
439 if(NO_OPERATION != (GET_SCRIPTS & operations))
441 // Clear the scripts.
442 ClearCharacterRuns(startIndex,
444 mModel->mLogicalModel->mScriptRuns);
447 if(NO_OPERATION != (VALIDATE_FONTS & operations))
450 ClearCharacterRuns(startIndex,
452 mModel->mLogicalModel->mFontRuns);
455 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
457 if(NO_OPERATION != (BIDI_INFO & operations))
459 // Clear the bidirectional paragraph info.
460 ClearCharacterRuns(startIndex,
462 mModel->mLogicalModel->mBidirectionalParagraphInfo);
464 // Clear the character's directions.
465 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
467 mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex,
468 characterDirectionsBuffer + endIndexPlusOne);
471 if(NO_OPERATION != (REORDER & operations))
473 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
474 uint32_t endRemoveIndex = startRemoveIndex;
475 ClearCharacterRuns(startIndex,
477 mModel->mLogicalModel->mBidirectionalLineInfo,
481 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
483 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
484 for(Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
485 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
489 BidirectionalLineInfoRun& bidiLineInfo = *it;
491 free(bidiLineInfo.visualToLogicalMap);
492 bidiLineInfo.visualToLogicalMap = NULL;
495 mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex,
496 bidirectionalLineInfoBuffer + endRemoveIndex);
501 void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
503 const CharacterIndex endIndexPlusOne = endIndex + 1u;
504 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
506 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
507 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
508 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
510 const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex);
511 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
513 if(NO_OPERATION != (SHAPE_TEXT & operations))
515 // Update the character to glyph indices.
516 for(Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
517 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
521 CharacterIndex& index = *it;
522 index -= numberOfGlyphsRemoved;
525 // Clear the character to glyph conversion table.
526 mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex,
527 charactersToGlyphBuffer + endIndexPlusOne);
529 // Clear the glyphs per character table.
530 mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex,
531 glyphsPerCharacterBuffer + endIndexPlusOne);
533 // Clear the glyphs buffer.
534 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
535 mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
536 glyphsBuffer + endGlyphIndexPlusOne);
538 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
540 // Update the glyph to character indices.
541 for(Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
542 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
546 CharacterIndex& index = *it;
547 index -= numberOfCharactersRemoved;
550 // Clear the glyphs to characters buffer.
551 mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
552 glyphsToCharactersBuffer + endGlyphIndexPlusOne);
554 // Clear the characters per glyph buffer.
555 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
556 mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
557 charactersPerGlyphBuffer + endGlyphIndexPlusOne);
559 // Should pass if mGlyphPositions has already been cleared in Controller::Relayouter::Relayout
560 if(0u != mModel->mVisualModel->mGlyphPositions.Count())
562 // Clear the positions buffer.
563 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
564 mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
565 positionsBuffer + endGlyphIndexPlusOne);
569 if(NO_OPERATION != (LAYOUT & operations))
572 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
573 uint32_t endRemoveIndex = startRemoveIndex;
574 ClearCharacterRuns(startIndex,
576 mModel->mVisualModel->mLines,
580 // Will update the glyph runs.
581 startRemoveIndex = mModel->mVisualModel->mLines.Count();
582 endRemoveIndex = startRemoveIndex;
583 ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
584 endGlyphIndexPlusOne - 1u,
585 mModel->mVisualModel->mLines,
589 // Set the line index from where to insert the new laid-out lines.
590 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
592 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
593 mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
594 linesBuffer + endRemoveIndex);
597 if(NO_OPERATION != (COLOR & operations))
599 if(0u != mModel->mVisualModel->mColorIndices.Count())
601 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
602 mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
603 colorIndexBuffer + endGlyphIndexPlusOne);
606 if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
608 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
609 mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
610 backgroundColorIndexBuffer + endGlyphIndexPlusOne);
615 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
617 if(mTextUpdateInfo.mClearAll ||
618 ((0u == startIndex) &&
619 (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
621 ClearFullModelData(operations);
625 // Clear the model data related with characters.
626 ClearCharacterModelData(startIndex, endIndex, operations);
628 // Clear the model data related with glyphs.
629 ClearGlyphModelData(startIndex, endIndex, operations);
632 // The estimated number of lines. Used to avoid reallocations when layouting.
633 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
635 mModel->mVisualModel->ClearCaches();
638 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
640 return ControllerImplModelUpdater::Update(*this, operationsRequired);
643 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
645 SetDefaultInputStyle(inputStyle, mFontDefaults, mTextColor);
648 float Controller::Impl::GetDefaultFontLineHeight()
650 FontId defaultFontId = 0u;
651 if(nullptr == mFontDefaults)
653 TextAbstraction::FontDescription fontDescription;
654 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
658 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
661 Text::FontMetrics fontMetrics;
662 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
664 return (fontMetrics.ascender - fontMetrics.descender);
667 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
669 if(nullptr == mEventData)
671 // Nothing to do if there is no text.
675 if(mEventData->mSelectionEnabled && (pStart || pEnd))
677 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
678 uint32_t oldStart = mEventData->mLeftSelectionPosition;
679 uint32_t oldEnd = mEventData->mRightSelectionPosition;
683 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
687 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
690 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
692 ChangeState(EventData::EDITING);
693 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
694 mEventData->mUpdateCursorPosition = true;
698 ChangeState(EventData::SELECTING);
699 mEventData->mUpdateHighlightBox = true;
700 mEventData->mUpdateLeftSelectionPosition = true;
701 mEventData->mUpdateRightSelectionPosition = true;
704 if(mSelectableControlInterface != nullptr)
706 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
711 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
713 if(nullptr == mEventData)
717 return mEventData->mPrimaryCursorPosition;
720 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused)
722 if(nullptr == mEventData)
724 // Nothing to do if there is no text.
728 if(mEventData->mPrimaryCursorPosition == index && mEventData->mState != EventData::SELECTING)
730 // Nothing for same cursor position.
734 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
735 uint32_t oldCursorPos = mEventData->mPrimaryCursorPosition;
736 mEventData->mPrimaryCursorPosition = std::min(index, length);
737 // If there is no focus, only the value is updated.
740 bool wasInSelectingState = mEventData->mState == EventData::SELECTING;
741 uint32_t oldStart = mEventData->mLeftSelectionPosition;
742 uint32_t oldEnd = mEventData->mRightSelectionPosition;
743 ChangeState(EventData::EDITING);
744 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
745 mEventData->mUpdateCursorPosition = true;
747 if(mSelectableControlInterface != nullptr && wasInSelectingState)
749 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
752 ScrollTextToMatchCursor();
755 if(nullptr != mEditableControlInterface)
757 mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition);
763 Uint32Pair Controller::Impl::GetTextSelectionRange() const
769 range.first = mEventData->mLeftSelectionPosition;
770 range.second = mEventData->mRightSelectionPosition;
776 bool Controller::Impl::IsEditable() const
778 return mEventData && mEventData->mEditingEnabled;
781 void Controller::Impl::SetEditable(bool editable)
785 mEventData->mEditingEnabled = editable;
787 if(mEventData->mDecorator)
789 mEventData->mDecorator->SetEditable(editable);
794 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
796 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
798 // Nothing to select if handles are in the same place.
799 selectedText.clear();
803 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
805 //Get start and end position of selection
806 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
807 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
809 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
810 const Length numberOfCharacters = utf32Characters.Count();
812 // Validate the start and end selection points
813 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
815 //Get text as a UTF8 string
816 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
818 if(deleteAfterRetrieval) // Only delete text if copied successfully
820 // Keep a copy of the current input style.
821 InputStyle currentInputStyle;
822 currentInputStyle.Copy(mEventData->mInputStyle);
824 // Set as input style the style of the first deleted character.
825 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
827 // Compare if the input style has changed.
828 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
830 if(hasInputStyleChanged)
832 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
833 // Queue the input style changed signal.
834 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
837 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
839 // Mark the paragraphs to be updated.
840 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
842 mTextUpdateInfo.mCharacterIndex = 0;
843 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
844 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
845 mTextUpdateInfo.mClearAll = true;
849 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
850 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
853 // Delete text between handles
854 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
855 Vector<Character>::Iterator last = first + lengthOfSelectedText;
856 utf32Characters.Erase(first, last);
858 // Will show the cursor at the first character of the selection.
859 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
863 // Will show the cursor at the last character of the selection.
864 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
867 mEventData->mDecoratorUpdated = true;
871 void Controller::Impl::SetSelection(int start, int end)
873 uint32_t oldStart = mEventData->mLeftSelectionPosition;
874 uint32_t oldEnd = mEventData->mRightSelectionPosition;
876 mEventData->mLeftSelectionPosition = start;
877 mEventData->mRightSelectionPosition = end;
878 mEventData->mUpdateCursorPosition = true;
880 if(mSelectableControlInterface != nullptr)
882 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
886 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
888 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
891 void Controller::Impl::ShowClipboard()
895 mClipboard.ShowClipboard();
899 void Controller::Impl::HideClipboard()
901 if(mClipboard && mClipboardHideEnabled)
903 mClipboard.HideClipboard();
907 void Controller::Impl::SetClipboardHideEnable(bool enable)
909 mClipboardHideEnabled = enable;
912 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
914 //Send string to clipboard
915 return (mClipboard && mClipboard.SetItem(source));
918 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
920 std::string selectedText;
921 RetrieveSelection(selectedText, deleteAfterSending);
922 CopyStringToClipboard(selectedText);
923 ChangeState(EventData::EDITING);
926 void Controller::Impl::RequestGetTextFromClipboard()
930 mClipboard.RequestItem();
934 void Controller::Impl::RepositionSelectionHandles()
936 SelectionHandleController::Reposition(*this);
938 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
940 SelectionHandleController::Reposition(*this, visualX, visualY, action);
943 void Controller::Impl::SetPopupButtons()
946 * Sets the Popup buttons to be shown depending on State.
948 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
950 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
953 bool isEditable = IsEditable();
954 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
956 if(EventData::SELECTING == mEventData->mState)
958 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
961 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
964 if(!IsClipboardEmpty())
968 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
970 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
973 if(!mEventData->mAllTextSelected)
975 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
978 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
980 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
982 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
985 if(!IsClipboardEmpty())
989 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
991 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
994 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
996 if(!IsClipboardEmpty())
1000 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1002 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1006 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1009 void Controller::Impl::ChangeState(EventData::State newState)
1011 if(nullptr == mEventData)
1013 // Nothing to do if there is no text input.
1017 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1019 if(mEventData->mState != newState)
1021 mEventData->mPreviousState = mEventData->mState;
1022 mEventData->mState = newState;
1024 switch(mEventData->mState)
1026 case EventData::INACTIVE:
1028 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1029 mEventData->mDecorator->StopCursorBlink();
1030 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1031 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1032 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1033 mEventData->mDecorator->SetHighlightActive(false);
1034 mEventData->mDecorator->SetPopupActive(false);
1035 mEventData->mDecoratorUpdated = true;
1038 case EventData::INTERRUPTED:
1040 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1041 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1042 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1043 mEventData->mDecorator->SetHighlightActive(false);
1044 mEventData->mDecorator->SetPopupActive(false);
1045 mEventData->mDecoratorUpdated = true;
1048 case EventData::SELECTING:
1050 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1051 mEventData->mDecorator->StopCursorBlink();
1052 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1053 if(mEventData->mGrabHandleEnabled)
1055 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1056 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1058 mEventData->mDecorator->SetHighlightActive(true);
1059 if(mEventData->mGrabHandlePopupEnabled)
1062 mEventData->mDecorator->SetPopupActive(true);
1064 mEventData->mDecoratorUpdated = true;
1067 case EventData::EDITING:
1069 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1070 if(mEventData->mCursorBlinkEnabled)
1072 mEventData->mDecorator->StartCursorBlink();
1074 // Grab handle is not shown until a tap is received whilst EDITING
1075 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1076 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1077 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1078 mEventData->mDecorator->SetHighlightActive(false);
1079 if(mEventData->mGrabHandlePopupEnabled)
1081 mEventData->mDecorator->SetPopupActive(false);
1083 mEventData->mDecoratorUpdated = true;
1086 case EventData::EDITING_WITH_POPUP:
1088 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1090 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1091 if(mEventData->mCursorBlinkEnabled)
1093 mEventData->mDecorator->StartCursorBlink();
1095 if(mEventData->mSelectionEnabled)
1097 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1098 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1099 mEventData->mDecorator->SetHighlightActive(false);
1101 else if(mEventData->mGrabHandleEnabled)
1103 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1105 if(mEventData->mGrabHandlePopupEnabled)
1108 mEventData->mDecorator->SetPopupActive(true);
1110 mEventData->mDecoratorUpdated = true;
1113 case EventData::EDITING_WITH_GRAB_HANDLE:
1115 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1117 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1118 if(mEventData->mCursorBlinkEnabled)
1120 mEventData->mDecorator->StartCursorBlink();
1122 // Grab handle is not shown until a tap is received whilst EDITING
1123 if(mEventData->mGrabHandleEnabled)
1125 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1127 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1128 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1129 mEventData->mDecorator->SetHighlightActive(false);
1130 if(mEventData->mGrabHandlePopupEnabled)
1132 mEventData->mDecorator->SetPopupActive(false);
1134 mEventData->mDecoratorUpdated = true;
1137 case EventData::SELECTION_HANDLE_PANNING:
1139 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1140 mEventData->mDecorator->StopCursorBlink();
1141 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1142 if(mEventData->mGrabHandleEnabled)
1144 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1145 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1147 mEventData->mDecorator->SetHighlightActive(true);
1148 if(mEventData->mGrabHandlePopupEnabled)
1150 mEventData->mDecorator->SetPopupActive(false);
1152 mEventData->mDecoratorUpdated = true;
1155 case EventData::GRAB_HANDLE_PANNING:
1157 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1159 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1160 if(mEventData->mCursorBlinkEnabled)
1162 mEventData->mDecorator->StartCursorBlink();
1164 if(mEventData->mGrabHandleEnabled)
1166 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1168 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1169 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1170 mEventData->mDecorator->SetHighlightActive(false);
1171 if(mEventData->mGrabHandlePopupEnabled)
1173 mEventData->mDecorator->SetPopupActive(false);
1175 mEventData->mDecoratorUpdated = true;
1178 case EventData::EDITING_WITH_PASTE_POPUP:
1180 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1182 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1183 if(mEventData->mCursorBlinkEnabled)
1185 mEventData->mDecorator->StartCursorBlink();
1188 if(mEventData->mGrabHandleEnabled)
1190 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1192 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1193 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1194 mEventData->mDecorator->SetHighlightActive(false);
1196 if(mEventData->mGrabHandlePopupEnabled)
1199 mEventData->mDecorator->SetPopupActive(true);
1201 mEventData->mDecoratorUpdated = true;
1204 case EventData::TEXT_PANNING:
1206 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1207 mEventData->mDecorator->StopCursorBlink();
1208 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1209 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1210 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1212 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1213 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1214 mEventData->mDecorator->SetHighlightActive(true);
1217 if(mEventData->mGrabHandlePopupEnabled)
1219 mEventData->mDecorator->SetPopupActive(false);
1222 mEventData->mDecoratorUpdated = true;
1229 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1230 CursorInfo& cursorInfo)
1232 if(!IsShowingRealText())
1234 // Do not want to use the place-holder text to set the cursor position.
1236 // Use the line's height of the font's family set to set the cursor's size.
1237 // If there is no font's family set, use the default font.
1238 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1240 cursorInfo.lineOffset = 0.f;
1241 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1242 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1245 if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
1247 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1250 switch(mModel->mHorizontalAlignment)
1252 case Text::HorizontalAlignment::BEGIN:
1256 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1260 cursorInfo.primaryPosition.x = 0.f;
1264 case Text::HorizontalAlignment::CENTER:
1266 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1269 case Text::HorizontalAlignment::END:
1273 cursorInfo.primaryPosition.x = 0.f;
1277 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1283 // Nothing else to do.
1287 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1288 GetCursorPositionParameters parameters;
1289 parameters.visualModel = mModel->mVisualModel;
1290 parameters.logicalModel = mModel->mLogicalModel;
1291 parameters.metrics = mMetrics;
1292 parameters.logical = logical;
1293 parameters.isMultiline = isMultiLine;
1295 Text::GetCursorPosition(parameters,
1298 // Adds Outline offset.
1299 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1300 cursorInfo.primaryPosition.x += outlineWidth;
1301 cursorInfo.primaryPosition.y += outlineWidth;
1302 cursorInfo.secondaryPosition.x += outlineWidth;
1303 cursorInfo.secondaryPosition.y += outlineWidth;
1307 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1309 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1310 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1312 if(0.f > cursorInfo.primaryPosition.x)
1314 cursorInfo.primaryPosition.x = 0.f;
1317 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1318 if(cursorInfo.primaryPosition.x > edgeWidth)
1320 cursorInfo.primaryPosition.x = edgeWidth;
1325 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1327 if(nullptr == mEventData)
1329 // Nothing to do if there is no text input.
1333 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1335 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1336 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1338 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1339 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1341 if(numberOfCharacters > 1u)
1343 const Script script = mModel->mLogicalModel->GetScript(index);
1344 if(HasLigatureMustBreak(script))
1346 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1347 numberOfCharacters = 1u;
1352 while(0u == numberOfCharacters)
1355 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1359 if(index < mEventData->mPrimaryCursorPosition)
1361 cursorIndex -= numberOfCharacters;
1365 cursorIndex += numberOfCharacters;
1368 // Will update the cursor hook position.
1369 mEventData->mUpdateCursorHookPosition = true;
1374 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1376 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1377 if(nullptr == mEventData)
1379 // Nothing to do if there is no text input.
1380 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1384 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1386 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1388 // Sets the cursor position.
1389 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1392 cursorInfo.primaryCursorHeight,
1393 cursorInfo.lineHeight);
1394 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1396 if(mEventData->mUpdateGrabHandlePosition)
1398 // Sets the grab handle position.
1399 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1401 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1402 cursorInfo.lineHeight);
1405 if(cursorInfo.isSecondaryCursor)
1407 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1408 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1409 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1410 cursorInfo.secondaryCursorHeight,
1411 cursorInfo.lineHeight);
1412 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1415 // Set which cursors are active according the state.
1416 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1418 if(cursorInfo.isSecondaryCursor)
1420 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1424 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1429 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1432 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1435 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1436 const CursorInfo& cursorInfo)
1438 SelectionHandleController::Update(*this, handleType, cursorInfo);
1441 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1443 // Clamp between -space & -alignment offset.
1445 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1447 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1448 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1449 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1451 mEventData->mDecoratorUpdated = true;
1455 mModel->mScrollPosition.x = 0.f;
1459 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1461 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1463 // Nothing to do if the text is single line.
1467 // Clamp between -space & 0.
1468 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1470 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1471 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1472 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1474 mEventData->mDecoratorUpdated = true;
1478 mModel->mScrollPosition.y = 0.f;
1482 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1484 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1486 // position is in actor's coords.
1487 const float positionEndX = position.x + cursorWidth;
1488 const float positionEndY = position.y + lineHeight;
1490 // Transform the position to decorator coords.
1491 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1492 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1494 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1495 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1497 if(decoratorPositionBeginX < 0.f)
1499 mModel->mScrollPosition.x = -position.x;
1501 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1503 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1506 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1508 if(decoratorPositionBeginY < 0.f)
1510 mModel->mScrollPosition.y = -position.y;
1512 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1514 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1519 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1521 // Get the current cursor position in decorator coords.
1522 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1524 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1526 // Calculate the offset to match the cursor position before the character was deleted.
1527 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1529 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1530 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1532 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1533 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1536 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1537 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1539 // Makes the new cursor position visible if needed.
1540 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1543 void Controller::Impl::ScrollTextToMatchCursor()
1545 CursorInfo cursorInfo;
1546 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1547 ScrollTextToMatchCursor(cursorInfo);
1550 void Controller::Impl::RequestRelayout()
1552 if(nullptr != mControlInterface)
1554 mControlInterface->RequestTextRelayout();
1558 Actor Controller::Impl::CreateBackgroundActor()
1560 // NOTE: Currently we only support background color for left-to-right text.
1564 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
1565 if(numberOfGlyphs > 0u)
1567 Vector<GlyphInfo> glyphs;
1568 glyphs.Resize(numberOfGlyphs);
1570 Vector<Vector2> positions;
1571 positions.Resize(numberOfGlyphs);
1573 // Get the line where the glyphs are laid-out.
1574 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1575 float alignmentOffset = lineRun->alignmentOffset;
1576 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
1582 glyphs.Resize(numberOfGlyphs);
1583 positions.Resize(numberOfGlyphs);
1585 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
1586 const Vector2* const positionsBuffer = positions.Begin();
1588 BackgroundMesh mesh;
1589 mesh.mVertices.Reserve(4u * glyphs.Size());
1590 mesh.mIndices.Reserve(6u * glyphs.Size());
1592 const Vector2 textSize = mView.GetLayoutSize();
1594 const float offsetX = alignmentOffset + textSize.width * 0.5f;
1595 const float offsetY = textSize.height * 0.5f;
1597 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
1598 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
1599 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
1602 uint32_t numberOfQuads = 0u;
1603 Length yLineOffset = 0;
1604 Length prevLineIndex = 0;
1605 LineIndex lineIndex;
1606 Length numberOfLines;
1608 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
1610 const GlyphInfo& glyph = *(glyphsBuffer + i);
1612 // Get the background color of the character.
1613 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
1614 const bool isMarkupBackground = mView.IsMarkupBackgroundColorSet();
1615 const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
1616 const bool isDefaultBackgroundColor = (0u == backgroundColorIndex);
1617 const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
1619 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
1620 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
1622 if(lineIndex != prevLineIndex)
1624 yLineOffset += lineHeight;
1627 // Only create quads for glyphs with a background color
1628 if(backgroundColor != Color::TRANSPARENT)
1630 const Vector2 position = *(positionsBuffer + i);
1632 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
1634 quad.x = position.x;
1635 quad.y = yLineOffset;
1636 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
1637 quad.w = lineHeight;
1639 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
1641 quad.x = position.x;
1642 quad.y = yLineOffset;
1643 quad.z = quad.x - glyph.xBearing + glyph.advance;
1644 quad.w = quad.y + lineHeight;
1646 else if(i == glyphSize - 1u) // The last glyph in the whole text
1648 quad.x = position.x - glyph.xBearing;
1649 quad.y = yLineOffset;
1650 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
1651 quad.w = quad.y + lineHeight;
1653 else // The glyph in the middle of the text
1655 quad.x = position.x - glyph.xBearing;
1656 quad.y = yLineOffset;
1657 quad.z = quad.x + glyph.advance;
1658 quad.w = quad.y + lineHeight;
1661 BackgroundVertex vertex;
1664 vertex.mPosition.x = quad.x - offsetX;
1665 vertex.mPosition.y = quad.y - offsetY;
1666 vertex.mColor = backgroundColor;
1667 mesh.mVertices.PushBack(vertex);
1670 vertex.mPosition.x = quad.z - offsetX;
1671 vertex.mPosition.y = quad.y - offsetY;
1672 vertex.mColor = backgroundColor;
1673 mesh.mVertices.PushBack(vertex);
1676 vertex.mPosition.x = quad.x - offsetX;
1677 vertex.mPosition.y = quad.w - offsetY;
1678 vertex.mColor = backgroundColor;
1679 mesh.mVertices.PushBack(vertex);
1682 vertex.mPosition.x = quad.z - offsetX;
1683 vertex.mPosition.y = quad.w - offsetY;
1684 vertex.mColor = backgroundColor;
1685 mesh.mVertices.PushBack(vertex);
1687 // Six indices in counter clockwise winding
1688 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
1689 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
1690 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
1691 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
1692 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
1693 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
1698 if(lineIndex != prevLineIndex)
1700 prevLineIndex = lineIndex;
1704 // Only create the background actor if there are glyphs with background color
1705 if(mesh.mVertices.Count() > 0u)
1707 Property::Map quadVertexFormat;
1708 quadVertexFormat["aPosition"] = Property::VECTOR2;
1709 quadVertexFormat["aColor"] = Property::VECTOR4;
1711 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
1712 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
1714 Geometry quadGeometry = Geometry::New();
1715 quadGeometry.AddVertexBuffer(quadVertices);
1716 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
1718 if(!mShaderBackground)
1720 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
1723 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
1724 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
1725 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
1727 actor = Actor::New();
1728 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
1729 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1730 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1731 actor.SetProperty(Actor::Property::SIZE, textSize);
1732 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
1733 actor.AddRenderer(renderer);
1740 void Controller::Impl::RelayoutForNewLineSize()
1742 // relayout all characters
1743 mTextUpdateInfo.mCharacterIndex = 0;
1744 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1745 mTextUpdateInfo.mNumberOfCharactersToAdd = mModel->mLogicalModel->mText.Count();
1746 mOperationsPending = static_cast<OperationsMask>(mOperationsPending | LAYOUT);
1749 if(mEventData && mEventData->mState == EventData::SELECTING)
1751 ChangeState(EventData::EDITING);
1757 bool Controller::Impl::IsInputStyleChangedSignalsQueueEmpty()
1759 return (NULL == mEventData) || (0u == mEventData->mInputStyleChangedQueue.Count());
1762 void Controller::Impl::ProcessInputStyleChangedSignals()
1766 if(mEditableControlInterface)
1768 // Emit the input style changed signal for each mask
1769 std::for_each(mEventData->mInputStyleChangedQueue.begin(),
1770 mEventData->mInputStyleChangedQueue.end(),
1771 [&](const auto mask) { mEditableControlInterface->InputStyleChanged(mask); } );
1774 mEventData->mInputStyleChangedQueue.Clear();
1778 void Controller::Impl::ScrollBy(Vector2 scroll)
1780 if(mEventData && (fabs(scroll.x) > Math::MACHINE_EPSILON_0 || fabs(scroll.y) > Math::MACHINE_EPSILON_0))
1782 const Vector2& layoutSize = mModel->mVisualModel->GetLayoutSize();
1783 const Vector2 currentScroll = mModel->mScrollPosition;
1785 scroll.x = -scroll.x;
1786 scroll.y = -scroll.y;
1788 if(fabs(scroll.x) > Math::MACHINE_EPSILON_0)
1790 mModel->mScrollPosition.x += scroll.x;
1791 ClampHorizontalScroll(layoutSize);
1794 if(fabs(scroll.y) > Math::MACHINE_EPSILON_0)
1796 mModel->mScrollPosition.y += scroll.y;
1797 ClampVerticalScroll(layoutSize);
1800 if(mModel->mScrollPosition != currentScroll)
1802 mEventData->mDecorator->UpdatePositions(mModel->mScrollPosition - currentScroll);
1808 float Controller::Impl::GetHorizontalScrollPosition()
1810 // Scroll values are negative internally so we convert them to positive numbers
1811 return mEventData ? -mModel->mScrollPosition.x : 0.0f;
1814 float Controller::Impl::GetVerticalScrollPosition()
1816 // Scroll values are negative internally so we convert them to positive numbers
1817 return mEventData ? -mModel->mScrollPosition.y : 0.0f;
1820 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
1822 //Underlined character runs for markup-processor
1823 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
1824 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
1825 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
1827 if(shouldClearPreUnderlineRuns)
1829 mModel->mVisualModel->mUnderlineRuns.Clear();
1832 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
1834 CharacterIndex characterIndex = it->characterRun.characterIndex;
1835 Length numberOfCharacters = it->characterRun.numberOfCharacters;
1836 for(Length index = 0u; index < numberOfCharacters; index++)
1838 GlyphRun underlineGlyphRun;
1839 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
1840 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
1841 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
1846 } // namespace Dali::Toolkit::Text