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/bidirectional-support.h>
29 #include <dali-toolkit/internal/text/character-set-conversion.h>
30 #include <dali-toolkit/internal/text/color-segmentation.h>
31 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
32 #include <dali-toolkit/internal/text/hyphenator.h>
33 #include <dali-toolkit/internal/text/multi-language-support.h>
34 #include <dali-toolkit/internal/text/segmentation.h>
35 #include <dali-toolkit/internal/text/shaper.h>
36 #include <dali-toolkit/internal/text/text-control-interface.h>
37 #include <dali-toolkit/internal/text/text-controller-impl-event-handler.h>
38 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
39 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
40 #include <dali-toolkit/internal/text/text-run-container.h>
41 #include <dali-toolkit/internal/text/text-selection-handle-controller.h>
47 #if defined(DEBUG_ENABLED)
48 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
51 struct BackgroundVertex
53 Vector2 mPosition; ///< Vertex posiiton
54 Vector4 mColor; ///< Vertex color
59 Vector<BackgroundVertex> mVertices; ///< container of vertices
60 Vector<unsigned short> mIndices; ///< container of indices
63 // The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B)
64 // based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
65 const float BRIGHTNESS_THRESHOLD = 0.179f;
66 const float CONSTANT_R = 0.2126f;
67 const float CONSTANT_G = 0.7152f;
68 const float CONSTANT_B = 0.0722f;
69 const Dali::Vector4 BLACK(0.f, 0.f, 0.f, 1.f);
70 const Dali::Vector4 WHITE(1.f, 1.f, 1.f, 1.f);
71 const Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
72 const Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
73 const Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
74 const Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
75 const Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
85 EventData::EventData(DecoratorPtr decorator, InputMethodContext& inputMethodContext)
86 : mDecorator(decorator),
87 mInputMethodContext(inputMethodContext),
88 mPlaceholderFont(nullptr),
89 mPlaceholderTextActive(),
90 mPlaceholderTextInactive(),
91 mPlaceholderTextColor(0.8f, 0.8f, 0.8f, 0.8f), // This color has been published in the Public API (placeholder-properties.h).
93 mInputStyleChangedQueue(),
94 mPreviousState(INACTIVE),
96 mPrimaryCursorPosition(0u),
97 mLeftSelectionPosition(0u),
98 mRightSelectionPosition(0u),
99 mPreEditStartPosition(0u),
101 mCursorHookPositionX(0.f),
102 mDoubleTapAction(Controller::NoTextTap::NO_ACTION),
103 mLongPressAction(Controller::NoTextTap::SHOW_SELECTION_POPUP),
104 mIsShowingPlaceholderText(false),
106 mDecoratorUpdated(false),
107 mCursorBlinkEnabled(true),
108 mGrabHandleEnabled(true),
109 mGrabHandlePopupEnabled(true),
110 mSelectionEnabled(true),
111 mUpdateCursorHookPosition(false),
112 mUpdateCursorPosition(false),
113 mUpdateGrabHandlePosition(false),
114 mUpdateLeftSelectionPosition(false),
115 mUpdateRightSelectionPosition(false),
116 mIsLeftHandleSelected(false),
117 mIsRightHandleSelected(false),
118 mUpdateHighlightBox(false),
119 mScrollAfterUpdatePosition(false),
120 mScrollAfterDelete(false),
121 mAllTextSelected(false),
122 mUpdateInputStyle(false),
123 mPasswordInput(false),
124 mCheckScrollAmount(false),
125 mIsPlaceholderPixelSize(false),
126 mIsPlaceholderElideEnabled(false),
127 mPlaceholderEllipsisFlag(false),
128 mShiftSelectionFlag(true),
129 mUpdateAlignment(false),
130 mEditingEnabled(true)
134 bool Controller::Impl::ProcessInputEvents()
136 return ControllerImplEventHandler::ProcessInputEvents(*this);
139 void Controller::Impl::NotifyInputMethodContext()
141 if(mEventData && mEventData->mInputMethodContext)
143 CharacterIndex cursorPosition = GetLogicalCursorPosition();
145 const Length numberOfWhiteSpaces = GetNumberOfWhiteSpaces(0u);
147 // Update the cursor position by removing the initial white spaces.
148 if(cursorPosition < numberOfWhiteSpaces)
154 cursorPosition -= numberOfWhiteSpaces;
157 mEventData->mInputMethodContext.SetCursorPosition(cursorPosition);
158 mEventData->mInputMethodContext.NotifyCursorPosition();
162 void Controller::Impl::NotifyInputMethodContextMultiLineStatus()
164 if(mEventData && mEventData->mInputMethodContext)
166 Text::Layout::Engine::Type layout = mLayoutEngine.GetLayout();
167 mEventData->mInputMethodContext.NotifyTextInputMultiLine(layout == Text::Layout::Engine::MULTI_LINE_BOX);
171 CharacterIndex Controller::Impl::GetLogicalCursorPosition() const
173 CharacterIndex cursorPosition = 0u;
177 if((EventData::SELECTING == mEventData->mState) ||
178 (EventData::SELECTION_HANDLE_PANNING == mEventData->mState))
180 cursorPosition = std::min(mEventData->mRightSelectionPosition, mEventData->mLeftSelectionPosition);
184 cursorPosition = mEventData->mPrimaryCursorPosition;
188 return cursorPosition;
191 Length Controller::Impl::GetNumberOfWhiteSpaces(CharacterIndex index) const
193 Length numberOfWhiteSpaces = 0u;
195 // Get the buffer to the text.
196 Character* utf32CharacterBuffer = mModel->mLogicalModel->mText.Begin();
198 const Length totalNumberOfCharacters = mModel->mLogicalModel->mText.Count();
199 for(; index < totalNumberOfCharacters; ++index, ++numberOfWhiteSpaces)
201 if(!TextAbstraction::IsWhiteSpace(*(utf32CharacterBuffer + index)))
207 return numberOfWhiteSpaces;
210 void Controller::Impl::GetText(CharacterIndex index, std::string& text) const
212 // Get the total number of characters.
213 Length numberOfCharacters = mModel->mLogicalModel->mText.Count();
215 // Retrieve the text.
216 if(0u != numberOfCharacters)
218 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin() + index, numberOfCharacters - index, text);
222 void Controller::Impl::CalculateTextUpdateIndices(Length& numberOfCharacters)
224 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
225 mTextUpdateInfo.mStartGlyphIndex = 0u;
226 mTextUpdateInfo.mStartLineIndex = 0u;
227 numberOfCharacters = 0u;
229 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
230 if(0u == numberOfParagraphs)
232 mTextUpdateInfo.mParagraphCharacterIndex = 0u;
233 numberOfCharacters = 0u;
235 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
237 // Nothing else to do if there are no paragraphs.
241 // Find the paragraphs to be updated.
242 Vector<ParagraphRunIndex> paragraphsToBeUpdated;
243 if(mTextUpdateInfo.mCharacterIndex >= mTextUpdateInfo.mPreviousNumberOfCharacters)
245 // Text is being added at the end of the current text.
246 if(mTextUpdateInfo.mIsLastCharacterNewParagraph)
248 // Text is being added in a new paragraph after the last character of the text.
249 mTextUpdateInfo.mParagraphCharacterIndex = mTextUpdateInfo.mPreviousNumberOfCharacters;
250 numberOfCharacters = 0u;
251 mTextUpdateInfo.mRequestedNumberOfCharacters = mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
253 mTextUpdateInfo.mStartGlyphIndex = mModel->mVisualModel->mGlyphs.Count();
254 mTextUpdateInfo.mStartLineIndex = mModel->mVisualModel->mLines.Count() - 1u;
256 // Nothing else to do;
260 paragraphsToBeUpdated.PushBack(numberOfParagraphs - 1u);
264 Length numberOfCharactersToUpdate = 0u;
265 if(mTextUpdateInfo.mFullRelayoutNeeded)
267 numberOfCharactersToUpdate = mTextUpdateInfo.mPreviousNumberOfCharacters;
271 numberOfCharactersToUpdate = (mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) ? mTextUpdateInfo.mNumberOfCharactersToRemove : 1u;
273 mModel->mLogicalModel->FindParagraphs(mTextUpdateInfo.mCharacterIndex,
274 numberOfCharactersToUpdate,
275 paragraphsToBeUpdated);
278 if(0u != paragraphsToBeUpdated.Count())
280 const ParagraphRunIndex firstParagraphIndex = *(paragraphsToBeUpdated.Begin());
281 const ParagraphRun& firstParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + firstParagraphIndex);
282 mTextUpdateInfo.mParagraphCharacterIndex = firstParagraph.characterRun.characterIndex;
284 ParagraphRunIndex lastParagraphIndex = *(paragraphsToBeUpdated.End() - 1u);
285 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex);
287 if((mTextUpdateInfo.mNumberOfCharactersToRemove > 0u) && // Some character are removed.
288 (lastParagraphIndex < numberOfParagraphs - 1u) && // There is a next paragraph.
289 ((lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters) == // The last removed character is the new paragraph character.
290 (mTextUpdateInfo.mCharacterIndex + mTextUpdateInfo.mNumberOfCharactersToRemove)))
292 // The new paragraph character of the last updated paragraph has been removed so is going to be merged with the next one.
293 const ParagraphRun& lastParagraph = *(mModel->mLogicalModel->mParagraphInfo.Begin() + lastParagraphIndex + 1u);
295 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
299 numberOfCharacters = lastParagraph.characterRun.characterIndex + lastParagraph.characterRun.numberOfCharacters - mTextUpdateInfo.mParagraphCharacterIndex;
303 mTextUpdateInfo.mRequestedNumberOfCharacters = numberOfCharacters + mTextUpdateInfo.mNumberOfCharactersToAdd - mTextUpdateInfo.mNumberOfCharactersToRemove;
304 mTextUpdateInfo.mStartGlyphIndex = *(mModel->mVisualModel->mCharactersToGlyph.Begin() + mTextUpdateInfo.mParagraphCharacterIndex);
307 void Controller::Impl::ClearFullModelData(OperationsMask operations)
309 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
311 mModel->mLogicalModel->mLineBreakInfo.Clear();
312 mModel->mLogicalModel->mParagraphInfo.Clear();
315 if(NO_OPERATION != (GET_SCRIPTS & operations))
317 mModel->mLogicalModel->mScriptRuns.Clear();
320 if(NO_OPERATION != (VALIDATE_FONTS & operations))
322 mModel->mLogicalModel->mFontRuns.Clear();
325 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
327 if(NO_OPERATION != (BIDI_INFO & operations))
329 mModel->mLogicalModel->mBidirectionalParagraphInfo.Clear();
330 mModel->mLogicalModel->mCharacterDirections.Clear();
333 if(NO_OPERATION != (REORDER & operations))
335 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
336 for(Vector<BidirectionalLineInfoRun>::Iterator it = mModel->mLogicalModel->mBidirectionalLineInfo.Begin(),
337 endIt = mModel->mLogicalModel->mBidirectionalLineInfo.End();
341 BidirectionalLineInfoRun& bidiLineInfo = *it;
343 free(bidiLineInfo.visualToLogicalMap);
344 bidiLineInfo.visualToLogicalMap = NULL;
346 mModel->mLogicalModel->mBidirectionalLineInfo.Clear();
350 if(NO_OPERATION != (SHAPE_TEXT & operations))
352 mModel->mVisualModel->mGlyphs.Clear();
353 mModel->mVisualModel->mGlyphsToCharacters.Clear();
354 mModel->mVisualModel->mCharactersToGlyph.Clear();
355 mModel->mVisualModel->mCharactersPerGlyph.Clear();
356 mModel->mVisualModel->mGlyphsPerCharacter.Clear();
357 mModel->mVisualModel->mGlyphPositions.Clear();
360 if(NO_OPERATION != (LAYOUT & operations))
362 mModel->mVisualModel->mLines.Clear();
365 if(NO_OPERATION != (COLOR & operations))
367 mModel->mVisualModel->mColorIndices.Clear();
368 mModel->mVisualModel->mBackgroundColorIndices.Clear();
372 void Controller::Impl::ClearCharacterModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
374 const CharacterIndex endIndexPlusOne = endIndex + 1u;
376 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
378 // Clear the line break info.
379 LineBreakInfo* lineBreakInfoBuffer = mModel->mLogicalModel->mLineBreakInfo.Begin();
381 mModel->mLogicalModel->mLineBreakInfo.Erase(lineBreakInfoBuffer + startIndex,
382 lineBreakInfoBuffer + endIndexPlusOne);
384 // Clear the paragraphs.
385 ClearCharacterRuns(startIndex,
387 mModel->mLogicalModel->mParagraphInfo);
390 if(NO_OPERATION != (GET_SCRIPTS & operations))
392 // Clear the scripts.
393 ClearCharacterRuns(startIndex,
395 mModel->mLogicalModel->mScriptRuns);
398 if(NO_OPERATION != (VALIDATE_FONTS & operations))
401 ClearCharacterRuns(startIndex,
403 mModel->mLogicalModel->mFontRuns);
406 if(0u != mModel->mLogicalModel->mBidirectionalParagraphInfo.Count())
408 if(NO_OPERATION != (BIDI_INFO & operations))
410 // Clear the bidirectional paragraph info.
411 ClearCharacterRuns(startIndex,
413 mModel->mLogicalModel->mBidirectionalParagraphInfo);
415 // Clear the character's directions.
416 CharacterDirection* characterDirectionsBuffer = mModel->mLogicalModel->mCharacterDirections.Begin();
418 mModel->mLogicalModel->mCharacterDirections.Erase(characterDirectionsBuffer + startIndex,
419 characterDirectionsBuffer + endIndexPlusOne);
422 if(NO_OPERATION != (REORDER & operations))
424 uint32_t startRemoveIndex = mModel->mLogicalModel->mBidirectionalLineInfo.Count();
425 uint32_t endRemoveIndex = startRemoveIndex;
426 ClearCharacterRuns(startIndex,
428 mModel->mLogicalModel->mBidirectionalLineInfo,
432 BidirectionalLineInfoRun* bidirectionalLineInfoBuffer = mModel->mLogicalModel->mBidirectionalLineInfo.Begin();
434 // Free the allocated memory used to store the conversion table in the bidirectional line info run.
435 for(Vector<BidirectionalLineInfoRun>::Iterator it = bidirectionalLineInfoBuffer + startRemoveIndex,
436 endIt = bidirectionalLineInfoBuffer + endRemoveIndex;
440 BidirectionalLineInfoRun& bidiLineInfo = *it;
442 free(bidiLineInfo.visualToLogicalMap);
443 bidiLineInfo.visualToLogicalMap = NULL;
446 mModel->mLogicalModel->mBidirectionalLineInfo.Erase(bidirectionalLineInfoBuffer + startRemoveIndex,
447 bidirectionalLineInfoBuffer + endRemoveIndex);
452 void Controller::Impl::ClearGlyphModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
454 const CharacterIndex endIndexPlusOne = endIndex + 1u;
455 const Length numberOfCharactersRemoved = endIndexPlusOne - startIndex;
457 // Convert the character index to glyph index before deleting the character to glyph and the glyphs per character buffers.
458 GlyphIndex* charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
459 Length* glyphsPerCharacterBuffer = mModel->mVisualModel->mGlyphsPerCharacter.Begin();
461 const GlyphIndex endGlyphIndexPlusOne = *(charactersToGlyphBuffer + endIndex) + *(glyphsPerCharacterBuffer + endIndex);
462 const Length numberOfGlyphsRemoved = endGlyphIndexPlusOne - mTextUpdateInfo.mStartGlyphIndex;
464 if(NO_OPERATION != (SHAPE_TEXT & operations))
466 // Update the character to glyph indices.
467 for(Vector<GlyphIndex>::Iterator it = charactersToGlyphBuffer + endIndexPlusOne,
468 endIt = charactersToGlyphBuffer + mModel->mVisualModel->mCharactersToGlyph.Count();
472 CharacterIndex& index = *it;
473 index -= numberOfGlyphsRemoved;
476 // Clear the character to glyph conversion table.
477 mModel->mVisualModel->mCharactersToGlyph.Erase(charactersToGlyphBuffer + startIndex,
478 charactersToGlyphBuffer + endIndexPlusOne);
480 // Clear the glyphs per character table.
481 mModel->mVisualModel->mGlyphsPerCharacter.Erase(glyphsPerCharacterBuffer + startIndex,
482 glyphsPerCharacterBuffer + endIndexPlusOne);
484 // Clear the glyphs buffer.
485 GlyphInfo* glyphsBuffer = mModel->mVisualModel->mGlyphs.Begin();
486 mModel->mVisualModel->mGlyphs.Erase(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex,
487 glyphsBuffer + endGlyphIndexPlusOne);
489 CharacterIndex* glyphsToCharactersBuffer = mModel->mVisualModel->mGlyphsToCharacters.Begin();
491 // Update the glyph to character indices.
492 for(Vector<CharacterIndex>::Iterator it = glyphsToCharactersBuffer + endGlyphIndexPlusOne,
493 endIt = glyphsToCharactersBuffer + mModel->mVisualModel->mGlyphsToCharacters.Count();
497 CharacterIndex& index = *it;
498 index -= numberOfCharactersRemoved;
501 // Clear the glyphs to characters buffer.
502 mModel->mVisualModel->mGlyphsToCharacters.Erase(glyphsToCharactersBuffer + mTextUpdateInfo.mStartGlyphIndex,
503 glyphsToCharactersBuffer + endGlyphIndexPlusOne);
505 // Clear the characters per glyph buffer.
506 Length* charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
507 mModel->mVisualModel->mCharactersPerGlyph.Erase(charactersPerGlyphBuffer + mTextUpdateInfo.mStartGlyphIndex,
508 charactersPerGlyphBuffer + endGlyphIndexPlusOne);
510 // Clear the positions buffer.
511 Vector2* positionsBuffer = mModel->mVisualModel->mGlyphPositions.Begin();
512 mModel->mVisualModel->mGlyphPositions.Erase(positionsBuffer + mTextUpdateInfo.mStartGlyphIndex,
513 positionsBuffer + endGlyphIndexPlusOne);
516 if(NO_OPERATION != (LAYOUT & operations))
519 uint32_t startRemoveIndex = mModel->mVisualModel->mLines.Count();
520 uint32_t endRemoveIndex = startRemoveIndex;
521 ClearCharacterRuns(startIndex,
523 mModel->mVisualModel->mLines,
527 // Will update the glyph runs.
528 startRemoveIndex = mModel->mVisualModel->mLines.Count();
529 endRemoveIndex = startRemoveIndex;
530 ClearGlyphRuns(mTextUpdateInfo.mStartGlyphIndex,
531 endGlyphIndexPlusOne - 1u,
532 mModel->mVisualModel->mLines,
536 // Set the line index from where to insert the new laid-out lines.
537 mTextUpdateInfo.mStartLineIndex = startRemoveIndex;
539 LineRun* linesBuffer = mModel->mVisualModel->mLines.Begin();
540 mModel->mVisualModel->mLines.Erase(linesBuffer + startRemoveIndex,
541 linesBuffer + endRemoveIndex);
544 if(NO_OPERATION != (COLOR & operations))
546 if(0u != mModel->mVisualModel->mColorIndices.Count())
548 ColorIndex* colorIndexBuffer = mModel->mVisualModel->mColorIndices.Begin();
549 mModel->mVisualModel->mColorIndices.Erase(colorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
550 colorIndexBuffer + endGlyphIndexPlusOne);
553 if(0u != mModel->mVisualModel->mBackgroundColorIndices.Count())
555 ColorIndex* backgroundColorIndexBuffer = mModel->mVisualModel->mBackgroundColorIndices.Begin();
556 mModel->mVisualModel->mBackgroundColorIndices.Erase(backgroundColorIndexBuffer + mTextUpdateInfo.mStartGlyphIndex,
557 backgroundColorIndexBuffer + endGlyphIndexPlusOne);
562 void Controller::Impl::ClearModelData(CharacterIndex startIndex, CharacterIndex endIndex, OperationsMask operations)
564 if(mTextUpdateInfo.mClearAll ||
565 ((0u == startIndex) &&
566 (mTextUpdateInfo.mPreviousNumberOfCharacters == endIndex + 1u)))
568 ClearFullModelData(operations);
572 // Clear the model data related with characters.
573 ClearCharacterModelData(startIndex, endIndex, operations);
575 // Clear the model data related with glyphs.
576 ClearGlyphModelData(startIndex, endIndex, operations);
579 // The estimated number of lines. Used to avoid reallocations when layouting.
580 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
582 mModel->mVisualModel->ClearCaches();
585 bool Controller::Impl::UpdateModel(OperationsMask operationsRequired)
587 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
589 // Calculate the operations to be done.
590 const OperationsMask operations = static_cast<OperationsMask>(mOperationsPending & operationsRequired);
592 if(NO_OPERATION == operations)
594 // Nothing to do if no operations are pending and required.
598 Vector<Character>& srcCharacters = mModel->mLogicalModel->mText;
599 Vector<Character> displayCharacters;
600 bool useHiddenText = false;
601 if(mHiddenInput && mEventData != nullptr && !mEventData->mIsShowingPlaceholderText)
603 mHiddenInput->Substitute(srcCharacters, displayCharacters);
604 useHiddenText = true;
607 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
608 const Length numberOfCharacters = utf32Characters.Count();
610 // Index to the first character of the first paragraph to be updated.
611 CharacterIndex startIndex = 0u;
612 // Number of characters of the paragraphs to be removed.
613 Length paragraphCharacters = 0u;
615 CalculateTextUpdateIndices(paragraphCharacters);
617 // Check whether the indices for updating the text is valid
618 if(numberOfCharacters > 0u &&
619 (mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
620 mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
622 std::string currentText;
623 Utf32ToUtf8(mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
625 DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
626 DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
628 // Dump mTextUpdateInfo
629 DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
630 DALI_LOG_ERROR(" mTextUpdateInfo.mCharacterIndex = %u\n", mTextUpdateInfo.mCharacterIndex);
631 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", mTextUpdateInfo.mNumberOfCharactersToRemove);
632 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", mTextUpdateInfo.mNumberOfCharactersToAdd);
633 DALI_LOG_ERROR(" mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", mTextUpdateInfo.mPreviousNumberOfCharacters);
634 DALI_LOG_ERROR(" mTextUpdateInfo.mParagraphCharacterIndex = %u\n", mTextUpdateInfo.mParagraphCharacterIndex);
635 DALI_LOG_ERROR(" mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", mTextUpdateInfo.mRequestedNumberOfCharacters);
636 DALI_LOG_ERROR(" mTextUpdateInfo.mStartGlyphIndex = %u\n", mTextUpdateInfo.mStartGlyphIndex);
637 DALI_LOG_ERROR(" mTextUpdateInfo.mStartLineIndex = %u\n", mTextUpdateInfo.mStartLineIndex);
638 DALI_LOG_ERROR(" mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", mTextUpdateInfo.mEstimatedNumberOfLines);
639 DALI_LOG_ERROR(" mTextUpdateInfo.mClearAll = %d\n", mTextUpdateInfo.mClearAll);
640 DALI_LOG_ERROR(" mTextUpdateInfo.mFullRelayoutNeeded = %d\n", mTextUpdateInfo.mFullRelayoutNeeded);
641 DALI_LOG_ERROR(" mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", mTextUpdateInfo.mIsLastCharacterNewParagraph);
646 startIndex = mTextUpdateInfo.mParagraphCharacterIndex;
648 if(mTextUpdateInfo.mClearAll ||
649 (0u != paragraphCharacters))
651 ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
654 mTextUpdateInfo.mClearAll = false;
656 // Whether the model is updated.
657 bool updated = false;
659 Vector<LineBreakInfo>& lineBreakInfo = mModel->mLogicalModel->mLineBreakInfo;
660 const Length requestedNumberOfCharacters = mTextUpdateInfo.mRequestedNumberOfCharacters;
662 if(NO_OPERATION != (GET_LINE_BREAKS & operations))
664 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
665 // calculate the bidirectional info for each 'paragraph'.
666 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
667 // is not shaped together).
668 lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
670 SetLineBreakInfo(utf32Characters,
672 requestedNumberOfCharacters,
675 if(mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
676 mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
678 CharacterIndex end = startIndex + requestedNumberOfCharacters;
679 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
681 for(CharacterIndex index = startIndex; index < end; index++)
683 CharacterIndex wordEnd = index;
684 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
689 if((wordEnd + 1) == end) // add last char
694 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
696 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
700 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
708 // Create the paragraph info.
709 mModel->mLogicalModel->CreateParagraphInfo(startIndex,
710 requestedNumberOfCharacters);
714 const bool getScripts = NO_OPERATION != (GET_SCRIPTS & operations);
715 const bool validateFonts = NO_OPERATION != (VALIDATE_FONTS & operations);
717 Vector<ScriptRun>& scripts = mModel->mLogicalModel->mScriptRuns;
718 Vector<FontRun>& validFonts = mModel->mLogicalModel->mFontRuns;
720 if(getScripts || validateFonts)
722 // Validates the fonts assigned by the application or assigns default ones.
723 // It makes sure all the characters are going to be rendered by the correct font.
724 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
728 // Retrieves the scripts used in the text.
729 multilanguageSupport.SetScripts(utf32Characters,
731 requestedNumberOfCharacters,
737 // Validate the fonts set through the mark-up string.
738 Vector<FontDescriptionRun>& fontDescriptionRuns = mModel->mLogicalModel->mFontDescriptionRuns;
740 // Get the default font's description.
741 TextAbstraction::FontDescription defaultFontDescription;
742 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale;
744 //Get the number of points per one unit of point-size
745 uint32_t numberOfPointsPerOneUnitOfPointSize = mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
747 if(IsShowingPlaceholderText() && mEventData && (nullptr != mEventData->mPlaceholderFont))
749 // If the placeholder font is set specifically, only placeholder font is changed.
750 defaultFontDescription = mEventData->mPlaceholderFont->mFontDescription;
751 if(mEventData->mPlaceholderFont->sizeDefined)
753 defaultPointSize = mEventData->mPlaceholderFont->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
756 else if(nullptr != mFontDefaults)
758 // Set the normal font and the placeholder font.
759 defaultFontDescription = mFontDefaults->mFontDescription;
763 defaultPointSize = mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
767 defaultPointSize = mFontDefaults->mDefaultPointSize * mFontSizeScale * numberOfPointsPerOneUnitOfPointSize;
771 // Validates the fonts. If there is a character with no assigned font it sets a default one.
772 // After this call, fonts are validated.
773 multilanguageSupport.ValidateFonts(utf32Characters,
776 defaultFontDescription,
779 requestedNumberOfCharacters,
785 Vector<Character> mirroredUtf32Characters;
786 bool textMirrored = false;
787 const Length numberOfParagraphs = mModel->mLogicalModel->mParagraphInfo.Count();
788 if(NO_OPERATION != (BIDI_INFO & operations))
790 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = mModel->mLogicalModel->mBidirectionalParagraphInfo;
791 bidirectionalInfo.Reserve(numberOfParagraphs);
793 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
794 SetBidirectionalInfo(utf32Characters,
798 requestedNumberOfCharacters,
800 (mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS),
803 if(0u != bidirectionalInfo.Count())
805 // Only set the character directions if there is right to left characters.
806 Vector<CharacterDirection>& directions = mModel->mLogicalModel->mCharacterDirections;
807 GetCharactersDirection(bidirectionalInfo,
810 requestedNumberOfCharacters,
813 // This paragraph has right to left text. Some characters may need to be mirrored.
814 // TODO: consider if the mirrored string can be stored as well.
816 textMirrored = GetMirroredText(utf32Characters,
820 requestedNumberOfCharacters,
821 mirroredUtf32Characters);
825 // There is no right to left characters. Clear the directions vector.
826 mModel->mLogicalModel->mCharacterDirections.Clear();
831 Vector<GlyphInfo>& glyphs = mModel->mVisualModel->mGlyphs;
832 Vector<CharacterIndex>& glyphsToCharactersMap = mModel->mVisualModel->mGlyphsToCharacters;
833 Vector<Length>& charactersPerGlyph = mModel->mVisualModel->mCharactersPerGlyph;
834 Vector<GlyphIndex> newParagraphGlyphs;
835 newParagraphGlyphs.Reserve(numberOfParagraphs);
837 const Length currentNumberOfGlyphs = glyphs.Count();
838 if(NO_OPERATION != (SHAPE_TEXT & operations))
840 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
842 ShapeText(textToShape,
847 mTextUpdateInfo.mStartGlyphIndex,
848 requestedNumberOfCharacters,
850 glyphsToCharactersMap,
854 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
855 mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
856 mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
861 const Length numberOfGlyphs = glyphs.Count() - currentNumberOfGlyphs;
863 if(NO_OPERATION != (GET_GLYPH_METRICS & operations))
865 GlyphInfo* glyphsBuffer = glyphs.Begin();
866 mMetrics->GetGlyphMetrics(glyphsBuffer + mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
868 // Update the width and advance of all new paragraph characters.
869 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
871 const GlyphIndex index = *it;
872 GlyphInfo& glyph = *(glyphsBuffer + index);
874 glyph.xBearing = 0.f;
881 if((nullptr != mEventData) &&
882 mEventData->mPreEditFlag &&
883 (0u != mModel->mVisualModel->mCharactersToGlyph.Count()))
885 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
886 mEventData->mInputMethodContext.GetPreeditStyle(attrs);
887 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
889 // Check the type of preedit and run it.
890 for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
892 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
893 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
894 type = attrData.preeditType;
896 // Check the number of commit characters for the start position.
897 unsigned int numberOfCommit = mEventData->mPrimaryCursorPosition - mEventData->mPreEditLength;
898 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
902 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
904 // Add the underline for the pre-edit text.
905 GlyphRun underlineRun;
906 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
907 underlineRun.numberOfGlyphs = numberOfIndices;
908 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
910 //Mark-up processor case
911 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
913 CopyUnderlinedFromLogicalToVisualModels(false);
917 case Dali::InputMethodContext::PreeditStyle::REVERSE:
919 Vector4 textColor = mModel->mVisualModel->GetTextColor();
920 ColorRun backgroundColorRun;
921 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
922 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
923 backgroundColorRun.color = textColor;
924 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
926 Vector4 backgroundColor = mModel->mVisualModel->GetBackgroundColor();
927 if(backgroundColor.a == 0) // There is no text background color.
929 // Try use the control's background color.
930 if(nullptr != mEditableControlInterface)
932 mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
933 if(backgroundColor.a == 0) // There is no control background color.
935 // Determines black or white color according to text color.
936 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
937 float L = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b;
938 backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE;
943 Vector<ColorRun> colorRuns;
944 colorRuns.Resize(1u);
945 ColorRun& colorRun = *(colorRuns.Begin());
946 colorRun.color = backgroundColor;
947 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
948 colorRun.characterRun.numberOfCharacters = numberOfIndices;
949 mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
951 //Mark-up processor case
952 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
954 CopyUnderlinedFromLogicalToVisualModels(false);
958 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
960 ColorRun backgroundColorRun;
961 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
962 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
963 backgroundColorRun.color = LIGHT_BLUE;
964 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
966 //Mark-up processor case
967 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
969 CopyUnderlinedFromLogicalToVisualModels(false);
973 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
975 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
976 ColorRun backgroundColorRun;
977 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
978 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
979 backgroundColorRun.color = BACKGROUND_SUB4;
980 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
982 GlyphRun underlineRun;
983 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
984 underlineRun.numberOfGlyphs = numberOfIndices;
985 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
987 //Mark-up processor case
988 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
990 CopyUnderlinedFromLogicalToVisualModels(false);
994 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
996 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
997 ColorRun backgroundColorRun;
998 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
999 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1000 backgroundColorRun.color = BACKGROUND_SUB5;
1001 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1003 GlyphRun underlineRun;
1004 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1005 underlineRun.numberOfGlyphs = numberOfIndices;
1006 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1008 //Mark-up processor case
1009 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1011 CopyUnderlinedFromLogicalToVisualModels(false);
1015 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
1017 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
1018 ColorRun backgroundColorRun;
1019 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1020 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1021 backgroundColorRun.color = BACKGROUND_SUB6;
1022 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1024 GlyphRun underlineRun;
1025 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1026 underlineRun.numberOfGlyphs = numberOfIndices;
1027 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1029 //Mark-up processor case
1030 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1032 CopyUnderlinedFromLogicalToVisualModels(false);
1036 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
1038 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
1039 ColorRun backgroundColorRun;
1040 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
1041 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
1042 backgroundColorRun.color = BACKGROUND_SUB7;
1043 mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
1045 GlyphRun underlineRun;
1046 underlineRun.glyphIndex = attrData.startIndex + numberOfCommit;
1047 underlineRun.numberOfGlyphs = numberOfIndices;
1048 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
1050 //Mark-up processor case
1051 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1053 CopyUnderlinedFromLogicalToVisualModels(false);
1057 case Dali::InputMethodContext::PreeditStyle::NONE:
1068 if(NO_OPERATION != (COLOR & operations))
1070 // Set the color runs in glyphs.
1071 SetColorSegmentationInfo(mModel->mLogicalModel->mColorRuns,
1072 mModel->mVisualModel->mCharactersToGlyph,
1073 mModel->mVisualModel->mGlyphsPerCharacter,
1075 mTextUpdateInfo.mStartGlyphIndex,
1076 requestedNumberOfCharacters,
1077 mModel->mVisualModel->mColors,
1078 mModel->mVisualModel->mColorIndices);
1080 // Set the background color runs in glyphs.
1081 SetColorSegmentationInfo(mModel->mLogicalModel->mBackgroundColorRuns,
1082 mModel->mVisualModel->mCharactersToGlyph,
1083 mModel->mVisualModel->mGlyphsPerCharacter,
1085 mTextUpdateInfo.mStartGlyphIndex,
1086 requestedNumberOfCharacters,
1087 mModel->mVisualModel->mBackgroundColors,
1088 mModel->mVisualModel->mBackgroundColorIndices);
1093 if((NO_OPERATION != (SHAPE_TEXT & operations)) &&
1094 !((nullptr != mEventData) &&
1095 mEventData->mPreEditFlag &&
1096 (0u != mModel->mVisualModel->mCharactersToGlyph.Count())))
1098 //Mark-up processor case
1099 if(mModel->mVisualModel->IsMarkupProcessorEnabled())
1101 CopyUnderlinedFromLogicalToVisualModels(true);
1107 // The estimated number of lines. Used to avoid reallocations when layouting.
1108 mTextUpdateInfo.mEstimatedNumberOfLines = std::max(mModel->mVisualModel->mLines.Count(), mModel->mLogicalModel->mParagraphInfo.Count());
1110 // Set the previous number of characters for the next time the text is updated.
1111 mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
1116 void Controller::Impl::RetrieveDefaultInputStyle(InputStyle& inputStyle)
1118 // Sets the default text's color.
1119 inputStyle.textColor = mTextColor;
1120 inputStyle.isDefaultColor = true;
1122 inputStyle.familyName.clear();
1123 inputStyle.weight = TextAbstraction::FontWeight::NORMAL;
1124 inputStyle.width = TextAbstraction::FontWidth::NORMAL;
1125 inputStyle.slant = TextAbstraction::FontSlant::NORMAL;
1126 inputStyle.size = 0.f;
1128 inputStyle.lineSpacing = 0.f;
1130 inputStyle.underlineProperties.clear();
1131 inputStyle.shadowProperties.clear();
1132 inputStyle.embossProperties.clear();
1133 inputStyle.outlineProperties.clear();
1135 inputStyle.isFamilyDefined = false;
1136 inputStyle.isWeightDefined = false;
1137 inputStyle.isWidthDefined = false;
1138 inputStyle.isSlantDefined = false;
1139 inputStyle.isSizeDefined = false;
1141 inputStyle.isLineSpacingDefined = false;
1143 inputStyle.isUnderlineDefined = false;
1144 inputStyle.isShadowDefined = false;
1145 inputStyle.isEmbossDefined = false;
1146 inputStyle.isOutlineDefined = false;
1148 // Sets the default font's family name, weight, width, slant and size.
1151 if(mFontDefaults->familyDefined)
1153 inputStyle.familyName = mFontDefaults->mFontDescription.family;
1154 inputStyle.isFamilyDefined = true;
1157 if(mFontDefaults->weightDefined)
1159 inputStyle.weight = mFontDefaults->mFontDescription.weight;
1160 inputStyle.isWeightDefined = true;
1163 if(mFontDefaults->widthDefined)
1165 inputStyle.width = mFontDefaults->mFontDescription.width;
1166 inputStyle.isWidthDefined = true;
1169 if(mFontDefaults->slantDefined)
1171 inputStyle.slant = mFontDefaults->mFontDescription.slant;
1172 inputStyle.isSlantDefined = true;
1175 if(mFontDefaults->sizeDefined)
1177 inputStyle.size = mFontDefaults->mDefaultPointSize;
1178 inputStyle.isSizeDefined = true;
1183 float Controller::Impl::GetDefaultFontLineHeight()
1185 FontId defaultFontId = 0u;
1186 if(nullptr == mFontDefaults)
1188 TextAbstraction::FontDescription fontDescription;
1189 defaultFontId = mFontClient.GetFontId(fontDescription, TextAbstraction::FontClient::DEFAULT_POINT_SIZE * mFontSizeScale);
1193 defaultFontId = mFontDefaults->GetFontId(mFontClient, mFontDefaults->mDefaultPointSize * mFontSizeScale);
1196 Text::FontMetrics fontMetrics;
1197 mMetrics->GetFontMetrics(defaultFontId, fontMetrics);
1199 return (fontMetrics.ascender - fontMetrics.descender);
1202 void Controller::Impl::SetTextSelectionRange(const uint32_t* pStart, const uint32_t* pEnd)
1204 if(nullptr == mEventData)
1206 // Nothing to do if there is no text.
1210 if(mEventData->mSelectionEnabled && (pStart || pEnd))
1212 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1213 uint32_t oldStart = mEventData->mLeftSelectionPosition;
1214 uint32_t oldEnd = mEventData->mRightSelectionPosition;
1218 mEventData->mLeftSelectionPosition = std::min(*pStart, length);
1222 mEventData->mRightSelectionPosition = std::min(*pEnd, length);
1225 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1227 ChangeState(EventData::EDITING);
1228 mEventData->mPrimaryCursorPosition = mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition;
1229 mEventData->mUpdateCursorPosition = true;
1233 ChangeState(EventData::SELECTING);
1234 mEventData->mUpdateHighlightBox = true;
1235 mEventData->mUpdateLeftSelectionPosition = true;
1236 mEventData->mUpdateRightSelectionPosition = true;
1239 if(mSelectableControlInterface != nullptr)
1241 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1246 CharacterIndex Controller::Impl::GetPrimaryCursorPosition() const
1248 if(nullptr == mEventData)
1252 return mEventData->mPrimaryCursorPosition;
1255 bool Controller::Impl::SetPrimaryCursorPosition(CharacterIndex index, bool focused)
1257 if(nullptr == mEventData)
1259 // Nothing to do if there is no text.
1263 if(mEventData->mPrimaryCursorPosition == index)
1265 // Nothing for same cursor position.
1269 uint32_t length = static_cast<uint32_t>(mModel->mLogicalModel->mText.Count());
1270 uint32_t oldCursorPos = mEventData->mPrimaryCursorPosition;
1271 mEventData->mPrimaryCursorPosition = std::min(index, length);
1272 // If there is no focus, only the value is updated.
1275 bool wasInSelectingState = mEventData->mState == EventData::SELECTING;
1276 uint32_t oldStart = mEventData->mLeftSelectionPosition;
1277 uint32_t oldEnd = mEventData->mRightSelectionPosition;
1278 ChangeState(EventData::EDITING);
1279 mEventData->mLeftSelectionPosition = mEventData->mRightSelectionPosition = mEventData->mPrimaryCursorPosition;
1280 mEventData->mUpdateCursorPosition = true;
1282 if(mSelectableControlInterface != nullptr && wasInSelectingState)
1284 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition);
1287 ScrollTextToMatchCursor();
1290 if(nullptr != mEditableControlInterface)
1292 mEditableControlInterface->CursorPositionChanged(oldCursorPos, mEventData->mPrimaryCursorPosition);
1298 Uint32Pair Controller::Impl::GetTextSelectionRange() const
1304 range.first = mEventData->mLeftSelectionPosition;
1305 range.second = mEventData->mRightSelectionPosition;
1311 bool Controller::Impl::IsEditable() const
1313 return mEventData && mEventData->mEditingEnabled;
1316 void Controller::Impl::SetEditable(bool editable)
1320 mEventData->mEditingEnabled = editable;
1324 void Controller::Impl::RetrieveSelection(std::string& selectedText, bool deleteAfterRetrieval)
1326 if(mEventData->mLeftSelectionPosition == mEventData->mRightSelectionPosition)
1328 // Nothing to select if handles are in the same place.
1329 selectedText.clear();
1333 const bool handlesCrossed = mEventData->mLeftSelectionPosition > mEventData->mRightSelectionPosition;
1335 //Get start and end position of selection
1336 const CharacterIndex startOfSelectedText = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1337 const Length lengthOfSelectedText = (handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition) - startOfSelectedText;
1339 Vector<Character>& utf32Characters = mModel->mLogicalModel->mText;
1340 const Length numberOfCharacters = utf32Characters.Count();
1342 // Validate the start and end selection points
1343 if((startOfSelectedText + lengthOfSelectedText) <= numberOfCharacters)
1345 //Get text as a UTF8 string
1346 Utf32ToUtf8(&utf32Characters[startOfSelectedText], lengthOfSelectedText, selectedText);
1348 if(deleteAfterRetrieval) // Only delete text if copied successfully
1350 // Keep a copy of the current input style.
1351 InputStyle currentInputStyle;
1352 currentInputStyle.Copy(mEventData->mInputStyle);
1354 // Set as input style the style of the first deleted character.
1355 mModel->mLogicalModel->RetrieveStyle(startOfSelectedText, mEventData->mInputStyle);
1357 // Compare if the input style has changed.
1358 const bool hasInputStyleChanged = !currentInputStyle.Equal(mEventData->mInputStyle);
1360 if(hasInputStyleChanged)
1362 const InputStyle::Mask styleChangedMask = currentInputStyle.GetInputStyleChangeMask(mEventData->mInputStyle);
1363 // Queue the input style changed signal.
1364 mEventData->mInputStyleChangedQueue.PushBack(styleChangedMask);
1367 mModel->mLogicalModel->UpdateTextStyleRuns(startOfSelectedText, -static_cast<int>(lengthOfSelectedText));
1369 // Mark the paragraphs to be updated.
1370 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1372 mTextUpdateInfo.mCharacterIndex = 0;
1373 mTextUpdateInfo.mNumberOfCharactersToRemove = mTextUpdateInfo.mPreviousNumberOfCharacters;
1374 mTextUpdateInfo.mNumberOfCharactersToAdd = mTextUpdateInfo.mPreviousNumberOfCharacters - lengthOfSelectedText;
1375 mTextUpdateInfo.mClearAll = true;
1379 mTextUpdateInfo.mCharacterIndex = startOfSelectedText;
1380 mTextUpdateInfo.mNumberOfCharactersToRemove = lengthOfSelectedText;
1383 // Delete text between handles
1384 Vector<Character>::Iterator first = utf32Characters.Begin() + startOfSelectedText;
1385 Vector<Character>::Iterator last = first + lengthOfSelectedText;
1386 utf32Characters.Erase(first, last);
1388 // Will show the cursor at the first character of the selection.
1389 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mRightSelectionPosition : mEventData->mLeftSelectionPosition;
1393 // Will show the cursor at the last character of the selection.
1394 mEventData->mPrimaryCursorPosition = handlesCrossed ? mEventData->mLeftSelectionPosition : mEventData->mRightSelectionPosition;
1397 mEventData->mDecoratorUpdated = true;
1401 void Controller::Impl::SetSelection(int start, int end)
1403 uint32_t oldStart = mEventData->mLeftSelectionPosition;
1404 uint32_t oldEnd = mEventData->mRightSelectionPosition;
1406 mEventData->mLeftSelectionPosition = start;
1407 mEventData->mRightSelectionPosition = end;
1408 mEventData->mUpdateCursorPosition = true;
1410 if(mSelectableControlInterface != nullptr)
1412 mSelectableControlInterface->SelectionChanged(oldStart, oldEnd, start, end);
1416 std::pair<int, int> Controller::Impl::GetSelectionIndexes() const
1418 return {mEventData->mLeftSelectionPosition, mEventData->mRightSelectionPosition};
1421 void Controller::Impl::ShowClipboard()
1425 mClipboard.ShowClipboard();
1429 void Controller::Impl::HideClipboard()
1431 if(mClipboard && mClipboardHideEnabled)
1433 mClipboard.HideClipboard();
1437 void Controller::Impl::SetClipboardHideEnable(bool enable)
1439 mClipboardHideEnabled = enable;
1442 bool Controller::Impl::CopyStringToClipboard(const std::string& source)
1444 //Send string to clipboard
1445 return (mClipboard && mClipboard.SetItem(source));
1448 void Controller::Impl::SendSelectionToClipboard(bool deleteAfterSending)
1450 std::string selectedText;
1451 RetrieveSelection(selectedText, deleteAfterSending);
1452 CopyStringToClipboard(selectedText);
1453 ChangeState(EventData::EDITING);
1456 void Controller::Impl::RequestGetTextFromClipboard()
1460 mClipboard.RequestItem();
1464 void Controller::Impl::RepositionSelectionHandles()
1466 SelectionHandleController::Reposition(*this);
1468 void Controller::Impl::RepositionSelectionHandles(float visualX, float visualY, Controller::NoTextTap::Action action)
1470 SelectionHandleController::Reposition(*this, visualX, visualY, action);
1473 void Controller::Impl::SetPopupButtons()
1476 * Sets the Popup buttons to be shown depending on State.
1478 * If SELECTING : CUT & COPY + ( PASTE & CLIPBOARD if content available to paste )
1480 * If EDITING_WITH_POPUP : SELECT & SELECT_ALL
1483 bool isEditable = IsEditable();
1484 TextSelectionPopup::Buttons buttonsToShow = TextSelectionPopup::NONE;
1486 if(EventData::SELECTING == mEventData->mState)
1488 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::COPY);
1491 buttonsToShow = TextSelectionPopup::Buttons(buttonsToShow | TextSelectionPopup::CUT);
1494 if(!IsClipboardEmpty())
1498 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1500 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1503 if(!mEventData->mAllTextSelected)
1505 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::SELECT_ALL));
1508 else if(EventData::EDITING_WITH_POPUP == mEventData->mState)
1510 if(mModel->mLogicalModel->mText.Count() && !IsShowingPlaceholderText())
1512 buttonsToShow = TextSelectionPopup::Buttons(TextSelectionPopup::SELECT | TextSelectionPopup::SELECT_ALL);
1515 if(!IsClipboardEmpty())
1519 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1521 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1524 else if(EventData::EDITING_WITH_PASTE_POPUP == mEventData->mState)
1526 if(!IsClipboardEmpty())
1530 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::PASTE));
1532 buttonsToShow = TextSelectionPopup::Buttons((buttonsToShow | TextSelectionPopup::CLIPBOARD));
1536 mEventData->mDecorator->SetEnabledPopupButtons(buttonsToShow);
1539 void Controller::Impl::ChangeState(EventData::State newState)
1541 if(nullptr == mEventData)
1543 // Nothing to do if there is no text input.
1547 DALI_LOG_INFO(gLogFilter, Debug::General, "ChangeState state:%d newstate:%d\n", mEventData->mState, newState);
1549 if(mEventData->mState != newState)
1551 mEventData->mPreviousState = mEventData->mState;
1552 mEventData->mState = newState;
1554 switch(mEventData->mState)
1556 case EventData::INACTIVE:
1558 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1559 mEventData->mDecorator->StopCursorBlink();
1560 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1561 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1562 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1563 mEventData->mDecorator->SetHighlightActive(false);
1564 mEventData->mDecorator->SetPopupActive(false);
1565 mEventData->mDecoratorUpdated = true;
1568 case EventData::INTERRUPTED:
1570 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1571 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1572 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1573 mEventData->mDecorator->SetHighlightActive(false);
1574 mEventData->mDecorator->SetPopupActive(false);
1575 mEventData->mDecoratorUpdated = true;
1578 case EventData::SELECTING:
1580 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1581 mEventData->mDecorator->StopCursorBlink();
1582 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1583 if(mEventData->mGrabHandleEnabled)
1585 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1586 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1588 mEventData->mDecorator->SetHighlightActive(true);
1589 if(mEventData->mGrabHandlePopupEnabled)
1592 mEventData->mDecorator->SetPopupActive(true);
1594 mEventData->mDecoratorUpdated = true;
1597 case EventData::EDITING:
1599 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1600 if(mEventData->mCursorBlinkEnabled)
1602 mEventData->mDecorator->StartCursorBlink();
1604 // Grab handle is not shown until a tap is received whilst EDITING
1605 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1606 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1607 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1608 mEventData->mDecorator->SetHighlightActive(false);
1609 if(mEventData->mGrabHandlePopupEnabled)
1611 mEventData->mDecorator->SetPopupActive(false);
1613 mEventData->mDecoratorUpdated = true;
1616 case EventData::EDITING_WITH_POPUP:
1618 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_POPUP \n", newState);
1620 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1621 if(mEventData->mCursorBlinkEnabled)
1623 mEventData->mDecorator->StartCursorBlink();
1625 if(mEventData->mSelectionEnabled)
1627 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1628 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1629 mEventData->mDecorator->SetHighlightActive(false);
1631 else if(mEventData->mGrabHandleEnabled)
1633 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1635 if(mEventData->mGrabHandlePopupEnabled)
1638 mEventData->mDecorator->SetPopupActive(true);
1640 mEventData->mDecoratorUpdated = true;
1643 case EventData::EDITING_WITH_GRAB_HANDLE:
1645 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_GRAB_HANDLE \n", newState);
1647 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1648 if(mEventData->mCursorBlinkEnabled)
1650 mEventData->mDecorator->StartCursorBlink();
1652 // Grab handle is not shown until a tap is received whilst EDITING
1653 if(mEventData->mGrabHandleEnabled)
1655 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1657 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1658 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1659 mEventData->mDecorator->SetHighlightActive(false);
1660 if(mEventData->mGrabHandlePopupEnabled)
1662 mEventData->mDecorator->SetPopupActive(false);
1664 mEventData->mDecoratorUpdated = true;
1667 case EventData::SELECTION_HANDLE_PANNING:
1669 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1670 mEventData->mDecorator->StopCursorBlink();
1671 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1672 if(mEventData->mGrabHandleEnabled)
1674 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, true);
1675 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, true);
1677 mEventData->mDecorator->SetHighlightActive(true);
1678 if(mEventData->mGrabHandlePopupEnabled)
1680 mEventData->mDecorator->SetPopupActive(false);
1682 mEventData->mDecoratorUpdated = true;
1685 case EventData::GRAB_HANDLE_PANNING:
1687 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "GRAB_HANDLE_PANNING \n", newState);
1689 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1690 if(mEventData->mCursorBlinkEnabled)
1692 mEventData->mDecorator->StartCursorBlink();
1694 if(mEventData->mGrabHandleEnabled)
1696 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1698 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1699 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1700 mEventData->mDecorator->SetHighlightActive(false);
1701 if(mEventData->mGrabHandlePopupEnabled)
1703 mEventData->mDecorator->SetPopupActive(false);
1705 mEventData->mDecoratorUpdated = true;
1708 case EventData::EDITING_WITH_PASTE_POPUP:
1710 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "EDITING_WITH_PASTE_POPUP \n", newState);
1712 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1713 if(mEventData->mCursorBlinkEnabled)
1715 mEventData->mDecorator->StartCursorBlink();
1718 if(mEventData->mGrabHandleEnabled)
1720 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, true);
1722 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1723 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1724 mEventData->mDecorator->SetHighlightActive(false);
1726 if(mEventData->mGrabHandlePopupEnabled)
1729 mEventData->mDecorator->SetPopupActive(true);
1731 mEventData->mDecoratorUpdated = true;
1734 case EventData::TEXT_PANNING:
1736 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1737 mEventData->mDecorator->StopCursorBlink();
1738 mEventData->mDecorator->SetHandleActive(GRAB_HANDLE, false);
1739 if(mEventData->mDecorator->IsHandleActive(LEFT_SELECTION_HANDLE) ||
1740 mEventData->mDecorator->IsHandleActive(RIGHT_SELECTION_HANDLE))
1742 mEventData->mDecorator->SetHandleActive(LEFT_SELECTION_HANDLE, false);
1743 mEventData->mDecorator->SetHandleActive(RIGHT_SELECTION_HANDLE, false);
1744 mEventData->mDecorator->SetHighlightActive(true);
1747 if(mEventData->mGrabHandlePopupEnabled)
1749 mEventData->mDecorator->SetPopupActive(false);
1752 mEventData->mDecoratorUpdated = true;
1759 void Controller::Impl::GetCursorPosition(CharacterIndex logical,
1760 CursorInfo& cursorInfo)
1762 if(!IsShowingRealText())
1764 // Do not want to use the place-holder text to set the cursor position.
1766 // Use the line's height of the font's family set to set the cursor's size.
1767 // If there is no font's family set, use the default font.
1768 // Use the current alignment to place the cursor at the beginning, center or end of the box.
1770 cursorInfo.lineOffset = 0.f;
1771 cursorInfo.lineHeight = GetDefaultFontLineHeight();
1772 cursorInfo.primaryCursorHeight = cursorInfo.lineHeight;
1775 if(mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS)
1777 isRTL = mLayoutDirection == LayoutDirection::RIGHT_TO_LEFT;
1780 switch(mModel->mHorizontalAlignment)
1782 case Text::HorizontalAlignment::BEGIN:
1786 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1790 cursorInfo.primaryPosition.x = 0.f;
1794 case Text::HorizontalAlignment::CENTER:
1796 cursorInfo.primaryPosition.x = floorf(0.5f * mModel->mVisualModel->mControlSize.width);
1799 case Text::HorizontalAlignment::END:
1803 cursorInfo.primaryPosition.x = 0.f;
1807 cursorInfo.primaryPosition.x = mModel->mVisualModel->mControlSize.width - mEventData->mDecorator->GetCursorWidth();
1813 // Nothing else to do.
1817 const bool isMultiLine = (Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout());
1818 GetCursorPositionParameters parameters;
1819 parameters.visualModel = mModel->mVisualModel;
1820 parameters.logicalModel = mModel->mLogicalModel;
1821 parameters.metrics = mMetrics;
1822 parameters.logical = logical;
1823 parameters.isMultiline = isMultiLine;
1825 Text::GetCursorPosition(parameters,
1828 // Adds Outline offset.
1829 const float outlineWidth = static_cast<float>(mModel->GetOutlineWidth());
1830 cursorInfo.primaryPosition.x += outlineWidth;
1831 cursorInfo.primaryPosition.y += outlineWidth;
1832 cursorInfo.secondaryPosition.x += outlineWidth;
1833 cursorInfo.secondaryPosition.y += outlineWidth;
1837 // If the text is editable and multi-line, the cursor position after a white space shouldn't exceed the boundaries of the text control.
1839 // Note the white spaces laid-out at the end of the line might exceed the boundaries of the control.
1840 // The reason is a wrapped line must not start with a white space so they are laid-out at the end of the line.
1842 if(0.f > cursorInfo.primaryPosition.x)
1844 cursorInfo.primaryPosition.x = 0.f;
1847 const float edgeWidth = mModel->mVisualModel->mControlSize.width - static_cast<float>(mEventData->mDecorator->GetCursorWidth());
1848 if(cursorInfo.primaryPosition.x > edgeWidth)
1850 cursorInfo.primaryPosition.x = edgeWidth;
1855 CharacterIndex Controller::Impl::CalculateNewCursorIndex(CharacterIndex index) const
1857 if(nullptr == mEventData)
1859 // Nothing to do if there is no text input.
1863 CharacterIndex cursorIndex = mEventData->mPrimaryCursorPosition;
1865 const GlyphIndex* const charactersToGlyphBuffer = mModel->mVisualModel->mCharactersToGlyph.Begin();
1866 const Length* const charactersPerGlyphBuffer = mModel->mVisualModel->mCharactersPerGlyph.Begin();
1868 GlyphIndex glyphIndex = *(charactersToGlyphBuffer + index);
1869 Length numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1871 if(numberOfCharacters > 1u)
1873 const Script script = mModel->mLogicalModel->GetScript(index);
1874 if(HasLigatureMustBreak(script))
1876 // Prevents to jump the whole Latin ligatures like fi, ff, or Arabic ï»», ...
1877 numberOfCharacters = 1u;
1882 while(0u == numberOfCharacters)
1885 numberOfCharacters = *(charactersPerGlyphBuffer + glyphIndex);
1889 if(index < mEventData->mPrimaryCursorPosition)
1891 cursorIndex -= numberOfCharacters;
1895 cursorIndex += numberOfCharacters;
1898 // Will update the cursor hook position.
1899 mEventData->mUpdateCursorHookPosition = true;
1904 void Controller::Impl::UpdateCursorPosition(const CursorInfo& cursorInfo)
1906 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "-->Controller::UpdateCursorPosition %p\n", this);
1907 if(nullptr == mEventData)
1909 // Nothing to do if there is no text input.
1910 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition no event data\n");
1914 const Vector2 cursorPosition = cursorInfo.primaryPosition + mModel->mScrollPosition;
1916 mEventData->mDecorator->SetGlyphOffset(PRIMARY_CURSOR, cursorInfo.glyphOffset);
1918 // Sets the cursor position.
1919 mEventData->mDecorator->SetPosition(PRIMARY_CURSOR,
1922 cursorInfo.primaryCursorHeight,
1923 cursorInfo.lineHeight);
1924 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Primary cursor position: %f,%f\n", cursorPosition.x, cursorPosition.y);
1926 if(mEventData->mUpdateGrabHandlePosition)
1928 // Sets the grab handle position.
1929 mEventData->mDecorator->SetPosition(GRAB_HANDLE,
1931 cursorInfo.lineOffset + mModel->mScrollPosition.y,
1932 cursorInfo.lineHeight);
1935 if(cursorInfo.isSecondaryCursor)
1937 mEventData->mDecorator->SetPosition(SECONDARY_CURSOR,
1938 cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x,
1939 cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y,
1940 cursorInfo.secondaryCursorHeight,
1941 cursorInfo.lineHeight);
1942 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Secondary cursor position: %f,%f\n", cursorInfo.secondaryPosition.x + mModel->mScrollPosition.x, cursorInfo.secondaryPosition.y + mModel->mScrollPosition.y);
1945 // Set which cursors are active according the state.
1946 if(EventData::IsEditingState(mEventData->mState) || (EventData::GRAB_HANDLE_PANNING == mEventData->mState))
1948 if(cursorInfo.isSecondaryCursor)
1950 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_BOTH);
1954 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_PRIMARY);
1959 mEventData->mDecorator->SetActiveCursor(ACTIVE_CURSOR_NONE);
1962 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "<--Controller::UpdateCursorPosition\n");
1965 void Controller::Impl::UpdateSelectionHandle(HandleType handleType,
1966 const CursorInfo& cursorInfo)
1968 SelectionHandleController::Update(*this, handleType, cursorInfo);
1971 void Controller::Impl::ClampHorizontalScroll(const Vector2& layoutSize)
1973 // Clamp between -space & -alignment offset.
1975 if(layoutSize.width > mModel->mVisualModel->mControlSize.width)
1977 const float space = (layoutSize.width - mModel->mVisualModel->mControlSize.width) + mModel->mAlignmentOffset;
1978 mModel->mScrollPosition.x = (mModel->mScrollPosition.x < -space) ? -space : mModel->mScrollPosition.x;
1979 mModel->mScrollPosition.x = (mModel->mScrollPosition.x > -mModel->mAlignmentOffset) ? -mModel->mAlignmentOffset : mModel->mScrollPosition.x;
1981 mEventData->mDecoratorUpdated = true;
1985 mModel->mScrollPosition.x = 0.f;
1989 void Controller::Impl::ClampVerticalScroll(const Vector2& layoutSize)
1991 if(Layout::Engine::SINGLE_LINE_BOX == mLayoutEngine.GetLayout())
1993 // Nothing to do if the text is single line.
1997 // Clamp between -space & 0.
1998 if(layoutSize.height > mModel->mVisualModel->mControlSize.height)
2000 const float space = (layoutSize.height - mModel->mVisualModel->mControlSize.height);
2001 mModel->mScrollPosition.y = (mModel->mScrollPosition.y < -space) ? -space : mModel->mScrollPosition.y;
2002 mModel->mScrollPosition.y = (mModel->mScrollPosition.y > 0.f) ? 0.f : mModel->mScrollPosition.y;
2004 mEventData->mDecoratorUpdated = true;
2008 mModel->mScrollPosition.y = 0.f;
2012 void Controller::Impl::ScrollToMakePositionVisible(const Vector2& position, float lineHeight)
2014 const float cursorWidth = mEventData->mDecorator ? static_cast<float>(mEventData->mDecorator->GetCursorWidth()) : 0.f;
2016 // position is in actor's coords.
2017 const float positionEndX = position.x + cursorWidth;
2018 const float positionEndY = position.y + lineHeight;
2020 // Transform the position to decorator coords.
2021 const float decoratorPositionBeginX = position.x + mModel->mScrollPosition.x;
2022 const float decoratorPositionEndX = positionEndX + mModel->mScrollPosition.x;
2024 const float decoratorPositionBeginY = position.y + mModel->mScrollPosition.y;
2025 const float decoratorPositionEndY = positionEndY + mModel->mScrollPosition.y;
2027 if(decoratorPositionBeginX < 0.f)
2029 mModel->mScrollPosition.x = -position.x;
2031 else if(decoratorPositionEndX > mModel->mVisualModel->mControlSize.width)
2033 mModel->mScrollPosition.x = mModel->mVisualModel->mControlSize.width - positionEndX;
2036 if(Layout::Engine::MULTI_LINE_BOX == mLayoutEngine.GetLayout())
2038 if(decoratorPositionBeginY < 0.f)
2040 mModel->mScrollPosition.y = -position.y;
2042 else if(decoratorPositionEndY > mModel->mVisualModel->mControlSize.height)
2044 mModel->mScrollPosition.y = mModel->mVisualModel->mControlSize.height - positionEndY;
2049 void Controller::Impl::ScrollTextToMatchCursor(const CursorInfo& cursorInfo)
2051 // Get the current cursor position in decorator coords.
2052 const Vector2& currentCursorPosition = mEventData->mDecorator->GetPosition(PRIMARY_CURSOR);
2054 const LineIndex lineIndex = mModel->mVisualModel->GetLineOfCharacter(mEventData->mPrimaryCursorPosition);
2056 // Calculate the offset to match the cursor position before the character was deleted.
2057 mModel->mScrollPosition.x = currentCursorPosition.x - cursorInfo.primaryPosition.x;
2059 //If text control has more than two lines and current line index is not last, calculate scrollpositionY
2060 if(mModel->mVisualModel->mLines.Count() > 1u && lineIndex != mModel->mVisualModel->mLines.Count() - 1u)
2062 const float currentCursorGlyphOffset = mEventData->mDecorator->GetGlyphOffset(PRIMARY_CURSOR);
2063 mModel->mScrollPosition.y = currentCursorPosition.y - cursorInfo.lineOffset - currentCursorGlyphOffset;
2066 ClampHorizontalScroll(mModel->mVisualModel->GetLayoutSize());
2067 ClampVerticalScroll(mModel->mVisualModel->GetLayoutSize());
2069 // Makes the new cursor position visible if needed.
2070 ScrollToMakePositionVisible(cursorInfo.primaryPosition, cursorInfo.lineHeight);
2073 void Controller::Impl::ScrollTextToMatchCursor()
2075 CursorInfo cursorInfo;
2076 GetCursorPosition(mEventData->mPrimaryCursorPosition, cursorInfo);
2077 ScrollTextToMatchCursor(cursorInfo);
2080 void Controller::Impl::RequestRelayout()
2082 if(nullptr != mControlInterface)
2084 mControlInterface->RequestTextRelayout();
2088 Actor Controller::Impl::CreateBackgroundActor()
2090 // NOTE: Currently we only support background color for left-to-right text.
2094 Length numberOfGlyphs = mView.GetNumberOfGlyphs();
2095 if(numberOfGlyphs > 0u)
2097 Vector<GlyphInfo> glyphs;
2098 glyphs.Resize(numberOfGlyphs);
2100 Vector<Vector2> positions;
2101 positions.Resize(numberOfGlyphs);
2103 // Get the line where the glyphs are laid-out.
2104 const LineRun* lineRun = mModel->mVisualModel->mLines.Begin();
2105 float alignmentOffset = lineRun->alignmentOffset;
2106 numberOfGlyphs = mView.GetGlyphs(glyphs.Begin(),
2112 glyphs.Resize(numberOfGlyphs);
2113 positions.Resize(numberOfGlyphs);
2115 const GlyphInfo* const glyphsBuffer = glyphs.Begin();
2116 const Vector2* const positionsBuffer = positions.Begin();
2118 BackgroundMesh mesh;
2119 mesh.mVertices.Reserve(4u * glyphs.Size());
2120 mesh.mIndices.Reserve(6u * glyphs.Size());
2122 const Vector2 textSize = mView.GetLayoutSize();
2124 const float offsetX = textSize.width * 0.5f;
2125 const float offsetY = textSize.height * 0.5f;
2127 const Vector4* const backgroundColorsBuffer = mView.GetBackgroundColors();
2128 const ColorIndex* const backgroundColorIndicesBuffer = mView.GetBackgroundColorIndices();
2129 const Vector4& defaultBackgroundColor = mModel->mVisualModel->IsBackgroundEnabled() ? mModel->mVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
2132 uint32_t numberOfQuads = 0u;
2133 Length yLineOffset = 0;
2134 Length prevLineIndex = 0;
2135 LineIndex lineIndex;
2136 Length numberOfLines;
2138 for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
2140 const GlyphInfo& glyph = *(glyphsBuffer + i);
2142 // Get the background color of the character.
2143 // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
2144 const bool isMarkupBackground = mView.IsMarkupBackgroundColorSet();
2145 const ColorIndex backgroundColorIndex = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
2146 const bool isDefaultBackgroundColor = (0u == backgroundColorIndex);
2147 const Vector4& backgroundColor = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
2149 mModel->mVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
2150 Length lineHeight = lineRun[lineIndex].ascender + -(lineRun[lineIndex].descender) + lineRun[lineIndex].lineSpacing;
2152 if(lineIndex != prevLineIndex)
2154 yLineOffset += lineHeight;
2157 // Only create quads for glyphs with a background color
2158 if(backgroundColor != Color::TRANSPARENT)
2160 const Vector2 position = *(positionsBuffer + i);
2162 if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
2164 quad.x = position.x;
2165 quad.y = yLineOffset;
2166 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2167 quad.w = lineHeight;
2169 else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
2171 quad.x = position.x;
2172 quad.y = yLineOffset;
2173 quad.z = quad.x - glyph.xBearing + glyph.advance;
2174 quad.w = quad.y + lineHeight;
2176 else if(i == glyphSize - 1u) // The last glyph in the whole text
2178 quad.x = position.x - glyph.xBearing;
2179 quad.y = yLineOffset;
2180 quad.z = quad.x + std::max(glyph.advance, glyph.xBearing + glyph.width);
2181 quad.w = quad.y + lineHeight;
2183 else // The glyph in the middle of the text
2185 quad.x = position.x - glyph.xBearing;
2186 quad.y = yLineOffset;
2187 quad.z = quad.x + glyph.advance;
2188 quad.w = quad.y + lineHeight;
2191 BackgroundVertex vertex;
2194 vertex.mPosition.x = quad.x - offsetX;
2195 vertex.mPosition.y = quad.y - offsetY;
2196 vertex.mColor = backgroundColor;
2197 mesh.mVertices.PushBack(vertex);
2200 vertex.mPosition.x = quad.z - offsetX;
2201 vertex.mPosition.y = quad.y - offsetY;
2202 vertex.mColor = backgroundColor;
2203 mesh.mVertices.PushBack(vertex);
2206 vertex.mPosition.x = quad.x - offsetX;
2207 vertex.mPosition.y = quad.w - offsetY;
2208 vertex.mColor = backgroundColor;
2209 mesh.mVertices.PushBack(vertex);
2212 vertex.mPosition.x = quad.z - offsetX;
2213 vertex.mPosition.y = quad.w - offsetY;
2214 vertex.mColor = backgroundColor;
2215 mesh.mVertices.PushBack(vertex);
2217 // Six indices in counter clockwise winding
2218 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2219 mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
2220 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2221 mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
2222 mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
2223 mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
2228 if(lineIndex != prevLineIndex)
2230 prevLineIndex = lineIndex;
2234 // Only create the background actor if there are glyphs with background color
2235 if(mesh.mVertices.Count() > 0u)
2237 Property::Map quadVertexFormat;
2238 quadVertexFormat["aPosition"] = Property::VECTOR2;
2239 quadVertexFormat["aColor"] = Property::VECTOR4;
2241 VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
2242 quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
2244 Geometry quadGeometry = Geometry::New();
2245 quadGeometry.AddVertexBuffer(quadVertices);
2246 quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
2248 if(!mShaderBackground)
2250 mShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
2253 Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, mShaderBackground);
2254 renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
2255 renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
2257 actor = Actor::New();
2258 actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
2259 actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
2260 actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
2261 actor.SetProperty(Actor::Property::SIZE, textSize);
2262 actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
2263 actor.AddRenderer(renderer);
2270 void Controller::Impl::CopyUnderlinedFromLogicalToVisualModels(bool shouldClearPreUnderlineRuns)
2272 //Underlined character runs for markup-processor
2273 const Vector<UnderlinedCharacterRun>& underlinedCharacterRuns = mModel->mLogicalModel->mUnderlinedCharacterRuns;
2274 const Vector<GlyphIndex>& charactersToGlyph = mModel->mVisualModel->mCharactersToGlyph;
2275 const Vector<Length>& glyphsPerCharacter = mModel->mVisualModel->mGlyphsPerCharacter;
2277 if(shouldClearPreUnderlineRuns)
2279 mModel->mVisualModel->mUnderlineRuns.Clear();
2282 for(Vector<UnderlinedCharacterRun>::ConstIterator it = underlinedCharacterRuns.Begin(), endIt = underlinedCharacterRuns.End(); it != endIt; ++it)
2284 CharacterIndex characterIndex = it->characterRun.characterIndex;
2285 Length numberOfCharacters = it->characterRun.numberOfCharacters;
2286 for(Length index = 0u; index < numberOfCharacters; index++)
2288 GlyphRun underlineGlyphRun;
2289 underlineGlyphRun.glyphIndex = charactersToGlyph[characterIndex + index];
2290 underlineGlyphRun.numberOfGlyphs = glyphsPerCharacter[characterIndex + index];
2291 mModel->mVisualModel->mUnderlineRuns.PushBack(underlineGlyphRun);
2298 } // namespace Toolkit