2 * Copyright (c) 2022 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/controller/text-controller-impl-model-updater.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/integration-api/trace.h>
25 #include <dali/public-api/math/math-utils.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/hyphenator.h>
32 #include <dali-toolkit/internal/text/multi-language-support.h>
33 #include <dali-toolkit/internal/text/segmentation.h>
34 #include <dali-toolkit/internal/text/shaper.h>
35 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
37 namespace Dali::Toolkit::Text
41 #if defined(DEBUG_ENABLED)
42 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
45 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
47 #if defined(TRACE_ENABLED)
48 uint32_t GetMilliSeconds()
50 // Get the time of a monotonic clock since its epoch.
51 auto epoch = std::chrono::steady_clock::now().time_since_epoch();
53 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
55 return static_cast<uint32_t>(duration.count());
59 // The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B)
60 // based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
61 constexpr float BRIGHTNESS_THRESHOLD = 0.179f;
62 constexpr float CONSTANT_R = 0.2126f;
63 constexpr float CONSTANT_G = 0.7152f;
64 constexpr float CONSTANT_B = 0.0722f;
65 constexpr Dali::Vector4 BLACK(0.f, 0.f, 0.f, 1.f);
66 constexpr Dali::Vector4 WHITE(1.f, 1.f, 1.f, 1.f);
67 constexpr Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
68 constexpr Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
69 constexpr Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
70 constexpr Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
71 constexpr Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
74 bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask operationsRequired)
76 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
78 // Calculate the operations to be done.
79 const OperationsMask operations = static_cast<OperationsMask>(impl.mOperationsPending & operationsRequired);
81 if(Controller::NO_OPERATION == operations)
83 // Nothing to do if no operations are pending and required.
86 DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_MODEL_UPDATE");
88 Vector<Character>& srcCharacters = impl.mModel->mLogicalModel->mText;
89 Vector<Character> displayCharacters;
90 bool useHiddenText = false;
91 if(impl.mHiddenInput && impl.mEventData != nullptr)
93 if(impl.mEventData->mIsShowingPlaceholderText)
95 impl.mHiddenInput->InitPreviousTextCount();
99 impl.mHiddenInput->Substitute(srcCharacters, displayCharacters, impl.mEventData->mPrimaryCursorPosition);
100 useHiddenText = true;
104 Vector<Character>& utf32Characters = useHiddenText ? displayCharacters : srcCharacters;
105 const Length numberOfCharacters = utf32Characters.Count();
107 // Index to the first character of the first paragraph to be updated.
108 CharacterIndex startIndex = 0u;
109 // Number of characters of the paragraphs to be removed.
110 Length paragraphCharacters = 0u;
112 impl.CalculateTextUpdateIndices(paragraphCharacters);
114 // Check whether the indices for updating the text is valid
115 if(impl.mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
116 impl.mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters)
118 if(numberOfCharacters == 0u)
120 impl.mTextUpdateInfo.Clear();
121 impl.mTextUpdateInfo.mClearAll = true;
123 else // numberOfCharacters > 0u
125 std::string currentText;
126 Utf32ToUtf8(impl.mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
128 DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
129 DALI_LOG_ERROR("Number of characters: %d, current text is: %s paragraphCharacters: %d\n", numberOfCharacters, currentText.c_str(), paragraphCharacters);
131 // Dump mTextUpdateInfo
132 DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
133 DALI_LOG_ERROR(" mTextUpdateInfo.mCharacterIndex = %u\n", impl.mTextUpdateInfo.mCharacterIndex);
134 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToRemove);
135 DALI_LOG_ERROR(" mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToAdd);
136 DALI_LOG_ERROR(" mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mPreviousNumberOfCharacters);
137 DALI_LOG_ERROR(" mTextUpdateInfo.mParagraphCharacterIndex = %u\n", impl.mTextUpdateInfo.mParagraphCharacterIndex);
138 DALI_LOG_ERROR(" mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mRequestedNumberOfCharacters);
139 DALI_LOG_ERROR(" mTextUpdateInfo.mStartGlyphIndex = %u\n", impl.mTextUpdateInfo.mStartGlyphIndex);
140 DALI_LOG_ERROR(" mTextUpdateInfo.mStartLineIndex = %u\n", impl.mTextUpdateInfo.mStartLineIndex);
141 DALI_LOG_ERROR(" mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", impl.mTextUpdateInfo.mEstimatedNumberOfLines);
142 DALI_LOG_ERROR(" mTextUpdateInfo.mClearAll = %d\n", impl.mTextUpdateInfo.mClearAll);
143 DALI_LOG_ERROR(" mTextUpdateInfo.mFullRelayoutNeeded = %d\n", impl.mTextUpdateInfo.mFullRelayoutNeeded);
144 DALI_LOG_ERROR(" mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", impl.mTextUpdateInfo.mIsLastCharacterNewParagraph);
150 startIndex = impl.mTextUpdateInfo.mParagraphCharacterIndex;
152 if(impl.mTextUpdateInfo.mClearAll ||
153 (0u != paragraphCharacters))
155 impl.ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
158 impl.mTextUpdateInfo.mClearAll = false;
160 // Whether the model is updated.
161 bool updated = false;
163 Vector<LineBreakInfo>& lineBreakInfo = impl.mModel->mLogicalModel->mLineBreakInfo;
164 const Length requestedNumberOfCharacters = impl.mTextUpdateInfo.mRequestedNumberOfCharacters;
166 if(Controller::NO_OPERATION != (Controller::GET_LINE_BREAKS & operations))
168 // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
169 // calculate the bidirectional info for each 'paragraph'.
170 // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
171 // is not shaped together).
172 lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
174 SetLineBreakInfo(utf32Characters,
176 requestedNumberOfCharacters,
179 if(impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
180 impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
182 CharacterIndex end = startIndex + requestedNumberOfCharacters;
183 LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
185 for(CharacterIndex index = startIndex; index < end; index++)
187 CharacterIndex wordEnd = index;
188 while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
193 if((wordEnd + 1) == end) // add last char
198 Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
200 for(CharacterIndex i = 0; i < (wordEnd - index); i++)
204 *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
212 // Create the paragraph info.
213 impl.mModel->mLogicalModel->CreateParagraphInfo(startIndex,
214 requestedNumberOfCharacters);
218 const bool getScripts = Controller::NO_OPERATION != (Controller::GET_SCRIPTS & operations);
219 const bool validateFonts = Controller::NO_OPERATION != (Controller::VALIDATE_FONTS & operations);
221 Vector<ScriptRun>& scripts = impl.mModel->mLogicalModel->mScriptRuns;
222 Vector<FontRun>& validFonts = impl.mModel->mLogicalModel->mFontRuns;
224 if(getScripts || validateFonts)
226 // Validates the fonts assigned by the application or assigns default ones.
227 // It makes sure all the characters are going to be rendered by the correct font.
228 MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
232 // Retrieves the scripts used in the text.
233 multilanguageSupport.SetScripts(utf32Characters,
235 requestedNumberOfCharacters,
241 // Validate the fonts set through the mark-up string.
242 Vector<FontDescriptionRun>& fontDescriptionRuns = impl.mModel->mLogicalModel->mFontDescriptionRuns;
244 // Get the default font's description.
245 TextAbstraction::FontDescription defaultFontDescription;
246 TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * impl.GetFontSizeScale();
248 //Get the number of points per one unit of point-size
249 uint32_t numberOfPointsPerOneUnitOfPointSize = impl.mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
251 if(impl.IsShowingPlaceholderText() && impl.mEventData && (nullptr != impl.mEventData->mPlaceholderFont))
253 // If the placeholder font is set specifically, only placeholder font is changed.
254 defaultFontDescription = impl.mEventData->mPlaceholderFont->mFontDescription;
255 if(impl.mEventData->mPlaceholderFont->sizeDefined)
257 defaultPointSize = impl.mEventData->mPlaceholderFont->mDefaultPointSize * impl.GetFontSizeScale() * numberOfPointsPerOneUnitOfPointSize;
260 else if(nullptr != impl.mFontDefaults)
262 // Set the normal font and the placeholder font.
263 defaultFontDescription = impl.mFontDefaults->mFontDescription;
265 if(impl.mTextFitEnabled || impl.mTextFitArrayEnabled)
267 defaultPointSize = impl.mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
271 defaultPointSize = impl.mFontDefaults->mDefaultPointSize * impl.GetFontSizeScale() * numberOfPointsPerOneUnitOfPointSize;
275 // Validates the fonts. If there is a character with no assigned font it sets a default one.
276 // After this call, fonts are validated.
277 multilanguageSupport.ValidateFonts(utf32Characters,
280 defaultFontDescription,
282 impl.GetFontSizeScale(),
284 requestedNumberOfCharacters,
290 Vector<Character> mirroredUtf32Characters;
291 bool textMirrored = false;
292 const Length numberOfParagraphs = impl.mModel->mLogicalModel->mParagraphInfo.Count();
293 if(Controller::NO_OPERATION != (Controller::BIDI_INFO & operations))
295 Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = impl.mModel->mLogicalModel->mBidirectionalParagraphInfo;
296 bidirectionalInfo.Reserve(numberOfParagraphs);
298 // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
299 SetBidirectionalInfo(utf32Characters,
303 requestedNumberOfCharacters,
305 (impl.mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS),
306 impl.mLayoutDirection);
308 if(0u != bidirectionalInfo.Count())
310 // Only set the character directions if there is right to left characters.
311 Vector<CharacterDirection>& directions = impl.mModel->mLogicalModel->mCharacterDirections;
312 GetCharactersDirection(bidirectionalInfo,
315 requestedNumberOfCharacters,
318 // This paragraph has right to left text. Some characters may need to be mirrored.
319 // TODO: consider if the mirrored string can be stored as well.
321 textMirrored = GetMirroredText(utf32Characters,
325 requestedNumberOfCharacters,
326 mirroredUtf32Characters);
330 // There is no right to left characters. Clear the directions vector.
331 impl.mModel->mLogicalModel->mCharacterDirections.Clear();
336 Vector<GlyphInfo>& glyphs = impl.mModel->mVisualModel->mGlyphs;
337 Vector<CharacterIndex>& glyphsToCharactersMap = impl.mModel->mVisualModel->mGlyphsToCharacters;
338 Vector<Length>& charactersPerGlyph = impl.mModel->mVisualModel->mCharactersPerGlyph;
339 Vector<GlyphIndex> newParagraphGlyphs;
340 newParagraphGlyphs.Reserve(numberOfParagraphs);
342 const Length currentNumberOfGlyphs = glyphs.Count();
344 #if defined(TRACE_ENABLED)
345 uint32_t logThreshold = TextAbstraction::FontClient::GetPerformanceLogThresholdTime();
346 bool logEnabled = TextAbstraction::FontClient::IsPerformanceLogEnabled();
348 uint32_t timeStamps[6];
349 uint32_t timeStampIndex = 0;
353 timeStamps[timeStampIndex++] = GetMilliSeconds();
357 if(Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations))
359 const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
361 ShapeText(textToShape,
366 impl.mTextUpdateInfo.mStartGlyphIndex,
367 requestedNumberOfCharacters,
369 glyphsToCharactersMap,
373 // Create the 'number of glyphs' per character and the glyph to character conversion tables.
374 impl.mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, impl.mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
375 impl.mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, impl.mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
380 #if defined(TRACE_ENABLED)
383 timeStamps[timeStampIndex++] = GetMilliSeconds();
387 const Length numberOfGlyphs = static_cast<Length>(glyphs.Count()) - currentNumberOfGlyphs;
389 if(Controller::NO_OPERATION != (Controller::GET_GLYPH_METRICS & operations))
391 GlyphInfo* glyphsBuffer = glyphs.Begin();
392 impl.mMetrics->GetGlyphMetrics(glyphsBuffer + impl.mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
394 // Update the width and advance of all new paragraph characters.
395 for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
397 const GlyphIndex index = *it;
398 GlyphInfo& glyph = *(glyphsBuffer + index);
400 glyph.xBearing = 0.f;
407 #if defined(TRACE_ENABLED)
410 timeStamps[timeStampIndex++] = GetMilliSeconds();
414 if((nullptr != impl.mEventData) &&
415 impl.mEventData->mPreEditFlag &&
416 (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count()))
418 Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
419 impl.mEventData->mInputMethodContext.GetPreeditStyle(attrs);
420 Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
422 // Check the type of preedit and run it.
423 for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
425 Dali::InputMethodContext::PreeditAttributeData attrData = *it;
426 DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
427 type = attrData.preeditType;
429 // Check the number of commit characters for the start position.
430 unsigned int numberOfCommit = impl.mEventData->mPrimaryCursorPosition - impl.mEventData->mPreEditLength;
431 Length numberOfIndices = attrData.endIndex - attrData.startIndex;
435 case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
437 // Add the underline for the pre-edit text.
438 UnderlinedGlyphRun underlineRun;
439 underlineRun.glyphRun.glyphIndex = attrData.startIndex + numberOfCommit;
440 underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
441 impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
443 //Mark-up processor case
444 if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
445 impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
447 impl.CopyUnderlinedFromLogicalToVisualModels(false);
451 case Dali::InputMethodContext::PreeditStyle::REVERSE:
453 Vector4 textColor = impl.mModel->mVisualModel->GetTextColor();
454 ColorRun backgroundColorRun;
455 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
456 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
457 backgroundColorRun.color = textColor;
458 impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
460 Vector4 backgroundColor = impl.mModel->mVisualModel->GetBackgroundColor();
461 if(Dali::EqualsZero(backgroundColor.a)) // There is no text background color.
463 // Try use the control's background color.
464 if(nullptr != impl.mEditableControlInterface)
466 impl.mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
467 if(Dali::EqualsZero(backgroundColor.a)) // There is no control background color.
469 // Determines black or white color according to text color.
470 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
471 float L = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b;
472 backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE;
477 Vector<ColorRun> colorRuns;
478 colorRuns.Resize(1u);
479 ColorRun& colorRun = *(colorRuns.Begin());
480 colorRun.color = backgroundColor;
481 colorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
482 colorRun.characterRun.numberOfCharacters = numberOfIndices;
483 impl.mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
485 //Mark-up processor case
486 if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
487 impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
489 impl.CopyUnderlinedFromLogicalToVisualModels(false);
493 case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
495 ColorRun backgroundColorRun;
496 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
497 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
498 backgroundColorRun.color = LIGHT_BLUE;
499 impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
501 //Mark-up processor case
502 if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
503 impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
505 impl.CopyUnderlinedFromLogicalToVisualModels(false);
509 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
511 // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
512 ColorRun backgroundColorRun;
513 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
514 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
515 backgroundColorRun.color = BACKGROUND_SUB4;
516 impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
518 UnderlinedGlyphRun underlineRun;
519 underlineRun.glyphRun.glyphIndex = attrData.startIndex + numberOfCommit;
520 underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
521 impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
523 //Mark-up processor case
524 if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
525 impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
527 impl.CopyUnderlinedFromLogicalToVisualModels(false);
531 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
533 // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
534 ColorRun backgroundColorRun;
535 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
536 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
537 backgroundColorRun.color = BACKGROUND_SUB5;
538 impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
540 UnderlinedGlyphRun underlineRun;
541 underlineRun.glyphRun.glyphIndex = attrData.startIndex + numberOfCommit;
542 underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
543 impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
545 //Mark-up processor case
546 if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
547 impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
549 impl.CopyUnderlinedFromLogicalToVisualModels(false);
553 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
555 // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
556 ColorRun backgroundColorRun;
557 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
558 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
559 backgroundColorRun.color = BACKGROUND_SUB6;
560 impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
562 UnderlinedGlyphRun underlineRun;
563 underlineRun.glyphRun.glyphIndex = attrData.startIndex + numberOfCommit;
564 underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
565 impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
567 //Mark-up processor case
568 if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
569 impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
571 impl.CopyUnderlinedFromLogicalToVisualModels(false);
575 case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
577 // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
578 ColorRun backgroundColorRun;
579 backgroundColorRun.characterRun.characterIndex = attrData.startIndex + numberOfCommit;
580 backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
581 backgroundColorRun.color = BACKGROUND_SUB7;
582 impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
584 UnderlinedGlyphRun underlineRun;
585 underlineRun.glyphRun.glyphIndex = attrData.startIndex + numberOfCommit;
586 underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
587 impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
589 //Mark-up processor case
590 if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
591 impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
593 impl.CopyUnderlinedFromLogicalToVisualModels(false);
597 case Dali::InputMethodContext::PreeditStyle::NONE:
608 #if defined(TRACE_ENABLED)
611 timeStamps[timeStampIndex++] = GetMilliSeconds();
615 if(Controller::NO_OPERATION != (Controller::COLOR & operations))
617 // Set the color runs in glyphs.
618 SetColorSegmentationInfo(impl.mModel->mLogicalModel->mColorRuns,
619 impl.mModel->mVisualModel->mCharactersToGlyph,
620 impl.mModel->mVisualModel->mGlyphsPerCharacter,
622 impl.mTextUpdateInfo.mStartGlyphIndex,
623 requestedNumberOfCharacters,
624 impl.mModel->mVisualModel->mColors,
625 impl.mModel->mVisualModel->mColorIndices);
627 // Set the background color runs in glyphs.
628 SetColorSegmentationInfo(impl.mModel->mLogicalModel->mBackgroundColorRuns,
629 impl.mModel->mVisualModel->mCharactersToGlyph,
630 impl.mModel->mVisualModel->mGlyphsPerCharacter,
632 impl.mTextUpdateInfo.mStartGlyphIndex,
633 requestedNumberOfCharacters,
634 impl.mModel->mVisualModel->mBackgroundColors,
635 impl.mModel->mVisualModel->mBackgroundColorIndices);
640 #if defined(TRACE_ENABLED)
643 timeStamps[timeStampIndex++] = GetMilliSeconds();
647 if((Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations)) &&
648 !((nullptr != impl.mEventData) &&
649 impl.mEventData->mPreEditFlag &&
650 (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count())))
652 //Mark-up processor case
653 if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
654 impl.mModel->mLogicalModel->mUnderlineRunsUpdated ||
655 impl.mModel->mLogicalModel->mCharacterSpacingRunsUpdated ||
656 impl.mModel->mLogicalModel->mStrikethroughRunsUpdated)
658 impl.CopyUnderlinedFromLogicalToVisualModels(true);
659 impl.CopyStrikethroughFromLogicalToVisualModels();
660 impl.CopyCharacterSpacingFromLogicalToVisualModels();
666 #if defined(TRACE_ENABLED)
669 timeStamps[timeStampIndex++] = GetMilliSeconds();
670 uint32_t timeShape = timeStamps[1] - timeStamps[0];
671 uint32_t timeGlyph = timeStamps[2] - timeStamps[1];
672 uint32_t timePreedit = timeStamps[3] - timeStamps[2];
673 uint32_t timeColor = timeStamps[4] - timeStamps[3];
674 uint32_t timeCopy = timeStamps[5] - timeStamps[4];
676 if(timeStamps[5] - timeStamps[0] > logThreshold)
678 std::string currentText;
679 Utf32ToUtf8(impl.mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
680 DALI_LOG_DEBUG_INFO("DALI_TEXT_MODEL_UPDATE shape:%u ms, glyph:%u ms, preedit:%u ms, color:%u ms, copy:%u ms\n", timeShape, timeGlyph, timePreedit, timeColor, timeCopy);
681 DALI_LOG_DEBUG_INFO("DALI_TEXT_MODEL_UPDATE chars:%d, text:%s\n", numberOfCharacters, currentText.c_str());
686 // The estimated number of lines. Used to avoid reallocations when layouting.
687 impl.mTextUpdateInfo.mEstimatedNumberOfLines = std::max(impl.mModel->mVisualModel->mLines.Count(), impl.mModel->mLogicalModel->mParagraphInfo.Count());
689 // Set the previous number of characters for the next time the text is updated.
690 impl.mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
695 } // namespace Dali::Toolkit::Text