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;
789 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
791 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
793 // Nothing to select if handles are in the same place.
794 selectedText.clear();
798 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
800 //Get start and end position of selection
801 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
802 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
804 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
805 const Length numberOfCharacters = utf32Characters.Count();
807 // Validate the start and end selection points
808 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
810 //Get text as a UTF8 string
811 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
813 if(deleteAfterRetrieval) // Only delete text if copied successfully
815 // Keep a copy of the current input style.
816 InputStyle currentInputStyle;
817 currentInputStyle.Copy(mEventData->mInputStyle);
819 // Set as input style the style of the first deleted character.
820 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
822 // Compare if the input style has changed.
823 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
825 if(hasInputStyleChanged)
827 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
828 // Queue the input style changed signal.
829 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
832 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
834 // Mark the paragraphs to be updated.
835 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
837 mTextUpdateInfo.mCharacterIndex = 0;
838 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
839 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
840 mTextUpdateInfo.mClearAll = true;
844 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
845 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
848 // Delete text between handles
849 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
850 Vector<Character>::Iterator last = first + lengthOfSelectedText;
851 utf32Characters.Erase(first, last);
853 // Will show the cursor at the first character of the selection.
854 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
858 // Will show the cursor at the last character of the selection.
859 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
862 mEventData->mDecoratorUpdated = true;
866 void Controller::Impl::SetSelection(int start, int end)
868 uint32_t oldStart = mEventData->mLeftSelectionPosition;
869 uint32_t oldEnd = mEventData->mRightSelectionPosition;
871 mEventData->mLeftSelectionPosition = start;
872 mEventData->mRightSelectionPosition = end;
873 mEventData->mUpdateCursorPosition = true;
875 if(mSelectableControlInterface != nullptr)
877 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
881 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
883 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
886 void Controller::Impl::ShowClipboard()
890 mClipboard.ShowClipboard();
894 void Controller::Impl::HideClipboard()
896 if(mClipboard && mClipboardHideEnabled)
898 mClipboard.HideClipboard();
902 void Controller::Impl::SetClipboardHideEnable(bool enable)
904 mClipboardHideEnabled = enable;
907 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
909 //Send string to clipboard
910 return (mClipboard && mClipboard.SetItem(source));
913 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
915 std::string selectedText;
916 RetrieveSelection(selectedText, deleteAfterSending);
917 CopyStringToClipboard(selectedText);
918 ChangeState(EventData::EDITING);
921 void Controller::Impl::RequestGetTextFromClipboard()
925 mClipboard.RequestItem();
929 void Controller::Impl::RepositionSelectionHandles()
931 SelectionHandleController::Reposition(*this);
933 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
935 SelectionHandleController::Reposition(*this, visualX, visualY, action);
938 void Controller::Impl::SetPopupButtons()
941 * Sets the Popup buttons to be shown depending on State.
943 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
945 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
948 bool isEditable = IsEditable();
949 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
951 if(EventData::SELECTING == mEventData->mState)
953 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
956 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
959 if(!IsClipboardEmpty())
963 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
965 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
968 if(!mEventData->mAllTextSelected)
970 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
973 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
975 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
977 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
980 if(!IsClipboardEmpty())
984 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
986 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
989 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
991 if(!IsClipboardEmpty())
995 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
997 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1001 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1004 void Controller::Impl::ChangeState(EventData::State newState)
1006 if(nullptr == mEventData)
1008 // Nothing to do if there is no text input.
1012 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1014 if(mEventData->mState != newState)
1016 mEventData->mPreviousState = mEventData->mState;
1017 mEventData->mState = newState;
1019 switch(mEventData->mState)
1021 case EventData::INACTIVE:
1023 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1024 mEventData->mDecorator->StopCursorBlink();
1025 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1026 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1027 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1028 mEventData->mDecorator->SetHighlightActive(false);
1029 mEventData->mDecorator->SetPopupActive(false);
1030 mEventData->mDecoratorUpdated = true;
1033 case EventData::INTERRUPTED:
1035 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1036 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1037 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1038 mEventData->mDecorator->SetHighlightActive(false);
1039 mEventData->mDecorator->SetPopupActive(false);
1040 mEventData->mDecoratorUpdated = true;
1043 case EventData::SELECTING:
1045 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1046 mEventData->mDecorator->StopCursorBlink();
1047 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1048 if(mEventData->mGrabHandleEnabled)
1050 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1051 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1053 mEventData->mDecorator->SetHighlightActive(true);
1054 if(mEventData->mGrabHandlePopupEnabled)
1057 mEventData->mDecorator->SetPopupActive(true);
1059 mEventData->mDecoratorUpdated = true;
1062 case EventData::EDITING:
1064 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1065 if(mEventData->mCursorBlinkEnabled)
1067 mEventData->mDecorator->StartCursorBlink();
1069 // Grab handle is not shown until a tap is received whilst EDITING
1070 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1071 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1072 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1073 mEventData->mDecorator->SetHighlightActive(false);
1074 if(mEventData->mGrabHandlePopupEnabled)
1076 mEventData->mDecorator->SetPopupActive(false);
1078 mEventData->mDecoratorUpdated = true;
1081 case EventData::EDITING_WITH_POPUP:
1083 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1085 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1086 if(mEventData->mCursorBlinkEnabled)
1088 mEventData->mDecorator->StartCursorBlink();
1090 if(mEventData->mSelectionEnabled)
1092 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1093 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1094 mEventData->mDecorator->SetHighlightActive(false);
1096 else if(mEventData->mGrabHandleEnabled)
1098 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1100 if(mEventData->mGrabHandlePopupEnabled)
1103 mEventData->mDecorator->SetPopupActive(true);
1105 mEventData->mDecoratorUpdated = true;
1108 case EventData::EDITING_WITH_GRAB_HANDLE:
1110 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1112 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1113 if(mEventData->mCursorBlinkEnabled)
1115 mEventData->mDecorator->StartCursorBlink();
1117 // Grab handle is not shown until a tap is received whilst EDITING
1118 if(mEventData->mGrabHandleEnabled)
1120 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1122 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1123 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1124 mEventData->mDecorator->SetHighlightActive(false);
1125 if(mEventData->mGrabHandlePopupEnabled)
1127 mEventData->mDecorator->SetPopupActive(false);
1129 mEventData->mDecoratorUpdated = true;
1132 case EventData::SELECTION_HANDLE_PANNING:
1134 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1135 mEventData->mDecorator->StopCursorBlink();
1136 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1137 if(mEventData->mGrabHandleEnabled)
1139 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1140 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1142 mEventData->mDecorator->SetHighlightActive(true);
1143 if(mEventData->mGrabHandlePopupEnabled)
1145 mEventData->mDecorator->SetPopupActive(false);
1147 mEventData->mDecoratorUpdated = true;
1150 case EventData::GRAB_HANDLE_PANNING:
1152 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1154 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1155 if(mEventData->mCursorBlinkEnabled)
1157 mEventData->mDecorator->StartCursorBlink();
1159 if(mEventData->mGrabHandleEnabled)
1161 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1163 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1164 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1165 mEventData->mDecorator->SetHighlightActive(false);
1166 if(mEventData->mGrabHandlePopupEnabled)
1168 mEventData->mDecorator->SetPopupActive(false);
1170 mEventData->mDecoratorUpdated = true;
1173 case EventData::EDITING_WITH_PASTE_POPUP:
1175 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1177 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1178 if(mEventData->mCursorBlinkEnabled)
1180 mEventData->mDecorator->StartCursorBlink();
1183 if(mEventData->mGrabHandleEnabled)
1185 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1187 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1188 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1189 mEventData->mDecorator->SetHighlightActive(false);
1191 if(mEventData->mGrabHandlePopupEnabled)
1194 mEventData->mDecorator->SetPopupActive(true);
1196 mEventData->mDecoratorUpdated = true;
1199 case EventData::TEXT_PANNING:
1201 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1202 mEventData->mDecorator->StopCursorBlink();
1203 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1204 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1205 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1207 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1208 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1209 mEventData->mDecorator->SetHighlightActive(true);
1212 if(mEventData->mGrabHandlePopupEnabled)
1214 mEventData->mDecorator->SetPopupActive(false);
1217 mEventData->mDecoratorUpdated = true;
1224 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1225 CursorInfo& cursorInfo)
1227 if(!IsShowingRealText())
1229 // Do not want to use the place-holder text to set the cursor position.
1231 // Use the line's height of the font's family set to set the cursor's size.
1232 // If there is no font's family set, use the default font.
1233 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1235 cursorInfo.lineOffset = 0.f;
1236 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1237 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1240 if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
1242 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1245 switch(mModel->mHorizontalAlignment)
1247 case Text::HorizontalAlignment::BEGIN:
1251 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1255 cursorInfo.primaryPosition.x = 0.f;
1259 case Text::HorizontalAlignment::CENTER:
1261 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1264 case Text::HorizontalAlignment::END:
1268 cursorInfo.primaryPosition.x = 0.f;
1272 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1278 // Nothing else to do.
1282 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1283 GetCursorPositionParameters parameters;
1284 parameters.visualModel = mModel->mVisualModel;
1285 parameters.logicalModel = mModel->mLogicalModel;
1286 parameters.metrics = mMetrics;
1287 parameters.logical = logical;
1288 parameters.isMultiline = isMultiLine;
1290 Text::GetCursorPosition(parameters,
1293 // Adds Outline offset.
1294 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1295 cursorInfo.primaryPosition.x += outlineWidth;
1296 cursorInfo.primaryPosition.y += outlineWidth;
1297 cursorInfo.secondaryPosition.x += outlineWidth;
1298 cursorInfo.secondaryPosition.y += outlineWidth;
1302 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1304 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1305 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1307 if(0.f > cursorInfo.primaryPosition.x)
1309 cursorInfo.primaryPosition.x = 0.f;
1312 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1313 if(cursorInfo.primaryPosition.x > edgeWidth)
1315 cursorInfo.primaryPosition.x = edgeWidth;
1320 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1322 if(nullptr == mEventData)
1324 // Nothing to do if there is no text input.
1328 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1330 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1331 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1333 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1334 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1336 if(numberOfCharacters > 1u)
1338 const Script script = mModel->mLogicalModel->GetScript(index);
1339 if(HasLigatureMustBreak(script))
1341 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1342 numberOfCharacters = 1u;
1347 while(0u == numberOfCharacters)
1350 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1354 if(index < mEventData->mPrimaryCursorPosition)
1356 cursorIndex -= numberOfCharacters;
1360 cursorIndex += numberOfCharacters;
1363 // Will update the cursor hook position.
1364 mEventData->mUpdateCursorHookPosition = true;
1369 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1371 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1372 if(nullptr == mEventData)
1374 // Nothing to do if there is no text input.
1375 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1379 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1381 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1383 // Sets the cursor position.
1384 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1387 cursorInfo.primaryCursorHeight,
1388 cursorInfo.lineHeight);
1389 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1391 if(mEventData->mUpdateGrabHandlePosition)
1393 // Sets the grab handle position.
1394 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1396 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1397 cursorInfo.lineHeight);
1400 if(cursorInfo.isSecondaryCursor)
1402 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1403 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1404 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1405 cursorInfo.secondaryCursorHeight,
1406 cursorInfo.lineHeight);
1407 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1410 // Set which cursors are active according the state.
1411 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1413 if(cursorInfo.isSecondaryCursor)
1415 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1419 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1424 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1427 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1430 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1431 const CursorInfo& cursorInfo)
1433 SelectionHandleController::Update(*this, handleType, cursorInfo);
1436 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1438 // Clamp between -space & -alignment offset.
1440 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1442 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1443 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1444 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1446 mEventData->mDecoratorUpdated = true;
1450 mModel->mScrollPosition.x = 0.f;
1454 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1456 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1458 // Nothing to do if the text is single line.
1462 // Clamp between -space & 0.
1463 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
1465 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
1466 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
1467 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
1469 mEventData->mDecoratorUpdated = true;
1473 mModel->mScrollPosition.y = 0.f;
1477 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
1479 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
1481 // position is in actor's coords.
1482 const float positionEndX = position.x + cursorWidth;
1483 const float positionEndY = position.y + lineHeight;
1485 // Transform the position to decorator coords.
1486 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
1487 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
1489 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
1490 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
1492 if(decoratorPositionBeginX < 0.f)
1494 mModel->mScrollPosition.x = -position.x;
1496 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
1498 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
1501 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
1503 if(decoratorPositionBeginY < 0.f)
1505 mModel->mScrollPosition.y = -position.y;
1507 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
1509 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
1514 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
1516 // Get the current cursor position in decorator coords.
1517 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
1519 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
1521 // Calculate the offset to match the cursor position before the character was deleted.
1522 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
1524 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
1525 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
1527 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
1528 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
1531 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
1532 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
1534 // Makes the new cursor position visible if needed.
1535 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
1538 void Controller::Impl::ScrollTextToMatchCursor()
1540 CursorInfo cursorInfo;
1541 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
1542 ScrollTextToMatchCursor(cursorInfo);
1545 void Controller::Impl::RequestRelayout()
1547 if(nullptr != mControlInterface)
1549 mControlInterface->RequestTextRelayout();
1553 Actor Controller::Impl::CreateBackgroundActor()
1555 // NOTE: Currently we only support background color for left-to-right text.
1559 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
1560 if(numberOfGlyphs > 0u)
1562 Vector<GlyphInfo> glyphs;
1563 glyphs.Resize(numberOfGlyphs);
1565 Vector<Vector2> positions;
1566 positions.Resize(numberOfGlyphs);
1568 // Get the line where the glyphs are laid-out.
1569 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
1570 float alignmentOffset = lineRun->alignmentOffset;
1571 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
1577 glyphs.Resize(numberOfGlyphs);
1578 positions.Resize(numberOfGlyphs);
1580 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
1581 const Vector2* const positionsBuffer = positions.Begin();
1583 BackgroundMesh mesh;
1584 mesh.mVertices.Reserve(4u * glyphs.Size());
1585 mesh.mIndices.Reserve(6u * glyphs.Size());
1587 const Vector2 textSize = mView.GetLayoutSize();
1589 const float offsetX = textSize.width * 0.5f;
1590 const float offsetY = textSize.height * 0.5f;
1592 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
1593 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
1594 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
1597 uint32_t numberOfQuads = 0u;
1598 Length yLineOffset = 0;
1599 Length prevLineIndex = 0;
1600 LineIndex lineIndex;
1601 Length numberOfLines;
1603 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
1605 const GlyphInfo& glyph = *(glyphsBuffer + i);
1607 // Get the background color of the character.
1608 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
1609 const bool isMarkupBackground = mView.IsMarkupBackgroundColorSet();
1610 const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
1611 const bool isDefaultBackgroundColor = (0u == backgroundColorIndex);
1612 const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
1614 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
1615 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
1617 if(lineIndex != prevLineIndex)
1619 yLineOffset += lineHeight;
1622 // Only create quads for glyphs with a background color
1623 if(backgroundColor != Color::TRANSPARENT)
1625 const Vector2 position = *(positionsBuffer + i);
1627 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
1629 quad.x = position.x;
1630 quad.y = yLineOffset;
1631 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
1632 quad.w = lineHeight;
1634 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
1636 quad.x = position.x;
1637 quad.y = yLineOffset;
1638 quad.z = quad.x - glyph.xBearing + glyph.advance;
1639 quad.w = quad.y + lineHeight;
1641 else if(i == glyphSize - 1u) // The last glyph in the whole text
1643 quad.x = position.x - glyph.xBearing;
1644 quad.y = yLineOffset;
1645 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
1646 quad.w = quad.y + lineHeight;
1648 else // The glyph in the middle of the text
1650 quad.x = position.x - glyph.xBearing;
1651 quad.y = yLineOffset;
1652 quad.z = quad.x + glyph.advance;
1653 quad.w = quad.y + lineHeight;
1656 BackgroundVertex vertex;
1659 vertex.mPosition.x = quad.x - offsetX;
1660 vertex.mPosition.y = quad.y - offsetY;
1661 vertex.mColor = backgroundColor;
1662 mesh.mVertices.PushBack(vertex);
1665 vertex.mPosition.x = quad.z - offsetX;
1666 vertex.mPosition.y = quad.y - offsetY;
1667 vertex.mColor = backgroundColor;
1668 mesh.mVertices.PushBack(vertex);
1671 vertex.mPosition.x = quad.x - offsetX;
1672 vertex.mPosition.y = quad.w - offsetY;
1673 vertex.mColor = backgroundColor;
1674 mesh.mVertices.PushBack(vertex);
1677 vertex.mPosition.x = quad.z - offsetX;
1678 vertex.mPosition.y = quad.w - offsetY;
1679 vertex.mColor = backgroundColor;
1680 mesh.mVertices.PushBack(vertex);
1682 // Six indices in counter clockwise winding
1683 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
1684 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
1685 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
1686 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
1687 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
1688 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
1693 if(lineIndex != prevLineIndex)
1695 prevLineIndex = lineIndex;
1699 // Only create the background actor if there are glyphs with background color
1700 if(mesh.mVertices.Count() > 0u)
1702 Property::Map quadVertexFormat;
1703 quadVertexFormat["aPosition"] = Property::VECTOR2;
1704 quadVertexFormat["aColor"] = Property::VECTOR4;
1706 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
1707 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
1709 Geometry quadGeometry = Geometry::New();
1710 quadGeometry.AddVertexBuffer(quadVertices);
1711 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
1713 if(!mShaderBackground)
1715 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
1718 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
1719 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
1720 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
1722 actor = Actor::New();
1723 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
1724 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
1725 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
1726 actor.SetProperty(Actor::Property::SIZE, textSize);
1727 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
1728 actor.AddRenderer(renderer);
1735 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
1737 //Underlined character runs for markup-processor
1738 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
1739 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
1740 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
1742 if(shouldClearPreUnderlineRuns)
1744 mModel->mVisualModel->mUnderlineRuns.Clear();
1747 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
1749 CharacterIndex characterIndex = it->characterRun.characterIndex;
1750 Length numberOfCharacters = it->characterRun.numberOfCharacters;
1751 for(Length index = 0u; index < numberOfCharacters; index++)
1753 GlyphRun underlineGlyphRun;
1754 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
1755 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
1756 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
1761 } // namespace Dali::Toolkit::Text