DALi Version 2.2.11
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / controller / text-controller-impl-model-updater.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/text/controller/text-controller-impl-model-updater.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/trace.h>
24 #include <dali/public-api/math/math-utils.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/bidirectional-support.h>
28 #include <dali-toolkit/internal/text/character-set-conversion.h>
29 #include <dali-toolkit/internal/text/color-segmentation.h>
30 #include <dali-toolkit/internal/text/hyphenator.h>
31 #include <dali-toolkit/internal/text/multi-language-support.h>
32 #include <dali-toolkit/internal/text/segmentation.h>
33 #include <dali-toolkit/internal/text/shaper.h>
34 #include <dali-toolkit/internal/text/text-editable-control-interface.h>
35
36 namespace Dali::Toolkit::Text
37 {
38 namespace
39 {
40 #if defined(DEBUG_ENABLED)
41 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
42 #endif
43
44 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_TEXT_PERFORMANCE_MARKER, false);
45
46 // The relative luminance of a color is defined as (L = 0.2126 * R + 0.7152 * G + 0.0722 * B)
47 // based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
48 constexpr float         BRIGHTNESS_THRESHOLD = 0.179f;
49 constexpr float         CONSTANT_R           = 0.2126f;
50 constexpr float         CONSTANT_G           = 0.7152f;
51 constexpr float         CONSTANT_B           = 0.0722f;
52 constexpr Dali::Vector4 BLACK(0.f, 0.f, 0.f, 1.f);
53 constexpr Dali::Vector4 WHITE(1.f, 1.f, 1.f, 1.f);
54 constexpr Dali::Vector4 LIGHT_BLUE(0.75f, 0.96f, 1.f, 1.f);
55 constexpr Dali::Vector4 BACKGROUND_SUB4(0.58f, 0.87f, 0.96f, 1.f);
56 constexpr Dali::Vector4 BACKGROUND_SUB5(0.83f, 0.94f, 0.98f, 1.f);
57 constexpr Dali::Vector4 BACKGROUND_SUB6(1.f, 0.5f, 0.5f, 1.f);
58 constexpr Dali::Vector4 BACKGROUND_SUB7(1.f, 0.8f, 0.8f, 1.f);
59 } // namespace
60
61 bool ControllerImplModelUpdater::Update(Controller::Impl& impl, OperationsMask operationsRequired)
62 {
63   DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel\n");
64
65   // Calculate the operations to be done.
66   const OperationsMask operations = static_cast<OperationsMask>(impl.mOperationsPending & operationsRequired);
67
68   if(Controller::NO_OPERATION == operations)
69   {
70     // Nothing to do if no operations are pending and required.
71     return false;
72   }
73   DALI_TRACE_SCOPE(gTraceFilter, "DALI_TEXT_MODEL_UPDATE");
74
75   Vector<Character>& srcCharacters = impl.mModel->mLogicalModel->mText;
76   Vector<Character>  displayCharacters;
77   bool               useHiddenText = false;
78   if(impl.mHiddenInput && impl.mEventData != nullptr)
79   {
80     if(impl.mEventData->mIsShowingPlaceholderText)
81     {
82       impl.mHiddenInput->InitPreviousTextCount();
83     }
84     else
85     {
86       impl.mHiddenInput->Substitute(srcCharacters, displayCharacters, impl.mEventData->mPrimaryCursorPosition);
87       useHiddenText = true;
88     }
89   }
90
91   Vector<Character>& utf32Characters    = useHiddenText ? displayCharacters : srcCharacters;
92   const Length       numberOfCharacters = utf32Characters.Count();
93
94   // Index to the first character of the first paragraph to be updated.
95   CharacterIndex startIndex = 0u;
96   // Number of characters of the paragraphs to be removed.
97   Length paragraphCharacters = 0u;
98
99   impl.CalculateTextUpdateIndices(paragraphCharacters);
100
101   // Check whether the indices for updating the text is valid
102   if(numberOfCharacters > 0u &&
103      (impl.mTextUpdateInfo.mParagraphCharacterIndex > numberOfCharacters ||
104       impl.mTextUpdateInfo.mRequestedNumberOfCharacters > numberOfCharacters))
105   {
106     std::string currentText;
107     Utf32ToUtf8(impl.mModel->mLogicalModel->mText.Begin(), numberOfCharacters, currentText);
108
109     DALI_LOG_ERROR("Controller::Impl::UpdateModel: mTextUpdateInfo has invalid indices\n");
110     DALI_LOG_ERROR("Number of characters: %d, current text is: %s\n", numberOfCharacters, currentText.c_str());
111
112     // Dump mTextUpdateInfo
113     DALI_LOG_ERROR("Dump mTextUpdateInfo:\n");
114     DALI_LOG_ERROR("     mTextUpdateInfo.mCharacterIndex = %u\n", impl.mTextUpdateInfo.mCharacterIndex);
115     DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToRemove = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToRemove);
116     DALI_LOG_ERROR("     mTextUpdateInfo.mNumberOfCharactersToAdd = %u\n", impl.mTextUpdateInfo.mNumberOfCharactersToAdd);
117     DALI_LOG_ERROR("     mTextUpdateInfo.mPreviousNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mPreviousNumberOfCharacters);
118     DALI_LOG_ERROR("     mTextUpdateInfo.mParagraphCharacterIndex = %u\n", impl.mTextUpdateInfo.mParagraphCharacterIndex);
119     DALI_LOG_ERROR("     mTextUpdateInfo.mRequestedNumberOfCharacters = %u\n", impl.mTextUpdateInfo.mRequestedNumberOfCharacters);
120     DALI_LOG_ERROR("     mTextUpdateInfo.mStartGlyphIndex = %u\n", impl.mTextUpdateInfo.mStartGlyphIndex);
121     DALI_LOG_ERROR("     mTextUpdateInfo.mStartLineIndex = %u\n", impl.mTextUpdateInfo.mStartLineIndex);
122     DALI_LOG_ERROR("     mTextUpdateInfo.mEstimatedNumberOfLines = %u\n", impl.mTextUpdateInfo.mEstimatedNumberOfLines);
123     DALI_LOG_ERROR("     mTextUpdateInfo.mClearAll = %d\n", impl.mTextUpdateInfo.mClearAll);
124     DALI_LOG_ERROR("     mTextUpdateInfo.mFullRelayoutNeeded = %d\n", impl.mTextUpdateInfo.mFullRelayoutNeeded);
125     DALI_LOG_ERROR("     mTextUpdateInfo.mIsLastCharacterNewParagraph = %d\n", impl.mTextUpdateInfo.mIsLastCharacterNewParagraph);
126
127     return false;
128   }
129
130   startIndex = impl.mTextUpdateInfo.mParagraphCharacterIndex;
131
132   if(impl.mTextUpdateInfo.mClearAll ||
133      (0u != paragraphCharacters))
134   {
135     impl.ClearModelData(startIndex, startIndex + ((paragraphCharacters > 0u) ? paragraphCharacters - 1u : 0u), operations);
136   }
137
138   impl.mTextUpdateInfo.mClearAll = false;
139
140   // Whether the model is updated.
141   bool updated = false;
142
143   Vector<LineBreakInfo>& lineBreakInfo               = impl.mModel->mLogicalModel->mLineBreakInfo;
144   const Length           requestedNumberOfCharacters = impl.mTextUpdateInfo.mRequestedNumberOfCharacters;
145
146   if(Controller::NO_OPERATION != (Controller::GET_LINE_BREAKS & operations))
147   {
148     // Retrieves the line break info. The line break info is used to split the text in 'paragraphs' to
149     // calculate the bidirectional info for each 'paragraph'.
150     // It's also used to layout the text (where it should be a new line) or to shape the text (text in different lines
151     // is not shaped together).
152     lineBreakInfo.Resize(numberOfCharacters, TextAbstraction::LINE_NO_BREAK);
153
154     SetLineBreakInfo(utf32Characters,
155                      startIndex,
156                      requestedNumberOfCharacters,
157                      lineBreakInfo);
158
159     if(impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::HYPHENATION) ||
160        impl.mModel->mLineWrapMode == ((Text::LineWrap::Mode)DevelText::LineWrap::MIXED))
161     {
162       CharacterIndex end                 = startIndex + requestedNumberOfCharacters;
163       LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
164
165       for(CharacterIndex index = startIndex; index < end; index++)
166       {
167         CharacterIndex wordEnd = index;
168         while((*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_ALLOW_BREAK) && (*(lineBreakInfoBuffer + wordEnd) != TextAbstraction::LINE_MUST_BREAK))
169         {
170           wordEnd++;
171         }
172
173         if((wordEnd + 1) == end) // add last char
174         {
175           wordEnd++;
176         }
177
178         Vector<bool> hyphens = GetWordHyphens(utf32Characters.Begin() + index, wordEnd - index, nullptr);
179
180         for(CharacterIndex i = 0; i < (wordEnd - index); i++)
181         {
182           if(hyphens[i])
183           {
184             *(lineBreakInfoBuffer + index + i) = TextAbstraction::LINE_HYPHENATION_BREAK;
185           }
186         }
187
188         index = wordEnd;
189       }
190     }
191
192     // Create the paragraph info.
193     impl.mModel->mLogicalModel->CreateParagraphInfo(startIndex,
194                                                     requestedNumberOfCharacters);
195     updated = true;
196   }
197
198   const bool getScripts    = Controller::NO_OPERATION != (Controller::GET_SCRIPTS & operations);
199   const bool validateFonts = Controller::NO_OPERATION != (Controller::VALIDATE_FONTS & operations);
200
201   Vector<ScriptRun>& scripts    = impl.mModel->mLogicalModel->mScriptRuns;
202   Vector<FontRun>&   validFonts = impl.mModel->mLogicalModel->mFontRuns;
203
204   if(getScripts || validateFonts)
205   {
206     // Validates the fonts assigned by the application or assigns default ones.
207     // It makes sure all the characters are going to be rendered by the correct font.
208     MultilanguageSupport multilanguageSupport = MultilanguageSupport::Get();
209
210     if(getScripts)
211     {
212       // Retrieves the scripts used in the text.
213       multilanguageSupport.SetScripts(utf32Characters,
214                                       startIndex,
215                                       requestedNumberOfCharacters,
216                                       scripts);
217     }
218
219     if(validateFonts)
220     {
221       // Validate the fonts set through the mark-up string.
222       Vector<FontDescriptionRun>& fontDescriptionRuns = impl.mModel->mLogicalModel->mFontDescriptionRuns;
223
224       // Get the default font's description.
225       TextAbstraction::FontDescription defaultFontDescription;
226       TextAbstraction::PointSize26Dot6 defaultPointSize = TextAbstraction::FontClient::DEFAULT_POINT_SIZE * impl.GetFontSizeScale();
227
228       //Get the number of points per one unit of point-size
229       uint32_t numberOfPointsPerOneUnitOfPointSize = impl.mFontClient.GetNumberOfPointsPerOneUnitOfPointSize();
230
231       if(impl.IsShowingPlaceholderText() && impl.mEventData && (nullptr != impl.mEventData->mPlaceholderFont))
232       {
233         // If the placeholder font is set specifically, only placeholder font is changed.
234         defaultFontDescription = impl.mEventData->mPlaceholderFont->mFontDescription;
235         if(impl.mEventData->mPlaceholderFont->sizeDefined)
236         {
237           defaultPointSize = impl.mEventData->mPlaceholderFont->mDefaultPointSize * impl.GetFontSizeScale() * numberOfPointsPerOneUnitOfPointSize;
238         }
239       }
240       else if(nullptr != impl.mFontDefaults)
241       {
242         // Set the normal font and the placeholder font.
243         defaultFontDescription = impl.mFontDefaults->mFontDescription;
244
245         if(impl.mTextFitEnabled)
246         {
247           defaultPointSize = impl.mFontDefaults->mFitPointSize * numberOfPointsPerOneUnitOfPointSize;
248         }
249         else
250         {
251           defaultPointSize = impl.mFontDefaults->mDefaultPointSize * impl.GetFontSizeScale() * numberOfPointsPerOneUnitOfPointSize;
252         }
253       }
254
255       // Validates the fonts. If there is a character with no assigned font it sets a default one.
256       // After this call, fonts are validated.
257       multilanguageSupport.ValidateFonts(utf32Characters,
258                                          scripts,
259                                          fontDescriptionRuns,
260                                          defaultFontDescription,
261                                          defaultPointSize,
262                                          startIndex,
263                                          requestedNumberOfCharacters,
264                                          validFonts);
265     }
266     updated = true;
267   }
268
269   Vector<Character> mirroredUtf32Characters;
270   bool              textMirrored       = false;
271   const Length      numberOfParagraphs = impl.mModel->mLogicalModel->mParagraphInfo.Count();
272   if(Controller::NO_OPERATION != (Controller::BIDI_INFO & operations))
273   {
274     Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo = impl.mModel->mLogicalModel->mBidirectionalParagraphInfo;
275     bidirectionalInfo.Reserve(numberOfParagraphs);
276
277     // Calculates the bidirectional info for the whole paragraph if it contains right to left scripts.
278     SetBidirectionalInfo(utf32Characters,
279                          scripts,
280                          lineBreakInfo,
281                          startIndex,
282                          requestedNumberOfCharacters,
283                          bidirectionalInfo,
284                          (impl.mModel->mMatchLayoutDirection != DevelText::MatchLayoutDirection::CONTENTS),
285                          impl.mLayoutDirection);
286
287     if(0u != bidirectionalInfo.Count())
288     {
289       // Only set the character directions if there is right to left characters.
290       Vector<CharacterDirection>& directions = impl.mModel->mLogicalModel->mCharacterDirections;
291       GetCharactersDirection(bidirectionalInfo,
292                              numberOfCharacters,
293                              startIndex,
294                              requestedNumberOfCharacters,
295                              directions);
296
297       // This paragraph has right to left text. Some characters may need to be mirrored.
298       // TODO: consider if the mirrored string can be stored as well.
299
300       textMirrored = GetMirroredText(utf32Characters,
301                                      directions,
302                                      bidirectionalInfo,
303                                      startIndex,
304                                      requestedNumberOfCharacters,
305                                      mirroredUtf32Characters);
306     }
307     else
308     {
309       // There is no right to left characters. Clear the directions vector.
310       impl.mModel->mLogicalModel->mCharacterDirections.Clear();
311     }
312     updated = true;
313   }
314
315   Vector<GlyphInfo>&      glyphs                = impl.mModel->mVisualModel->mGlyphs;
316   Vector<CharacterIndex>& glyphsToCharactersMap = impl.mModel->mVisualModel->mGlyphsToCharacters;
317   Vector<Length>&         charactersPerGlyph    = impl.mModel->mVisualModel->mCharactersPerGlyph;
318   Vector<GlyphIndex>      newParagraphGlyphs;
319   newParagraphGlyphs.Reserve(numberOfParagraphs);
320
321   const Length currentNumberOfGlyphs = glyphs.Count();
322   if(Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations))
323   {
324     const Vector<Character>& textToShape = textMirrored ? mirroredUtf32Characters : utf32Characters;
325     // Shapes the text.
326     ShapeText(textToShape,
327               lineBreakInfo,
328               scripts,
329               validFonts,
330               startIndex,
331               impl.mTextUpdateInfo.mStartGlyphIndex,
332               requestedNumberOfCharacters,
333               glyphs,
334               glyphsToCharactersMap,
335               charactersPerGlyph,
336               newParagraphGlyphs);
337
338     // Create the 'number of glyphs' per character and the glyph to character conversion tables.
339     impl.mModel->mVisualModel->CreateGlyphsPerCharacterTable(startIndex, impl.mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
340     impl.mModel->mVisualModel->CreateCharacterToGlyphTable(startIndex, impl.mTextUpdateInfo.mStartGlyphIndex, requestedNumberOfCharacters);
341
342     updated = true;
343   }
344
345   const Length numberOfGlyphs = static_cast<Length>(glyphs.Count()) - currentNumberOfGlyphs;
346
347   if(Controller::NO_OPERATION != (Controller::GET_GLYPH_METRICS & operations))
348   {
349     GlyphInfo* glyphsBuffer = glyphs.Begin();
350     impl.mMetrics->GetGlyphMetrics(glyphsBuffer + impl.mTextUpdateInfo.mStartGlyphIndex, numberOfGlyphs);
351
352     // Update the width and advance of all new paragraph characters.
353     for(Vector<GlyphIndex>::ConstIterator it = newParagraphGlyphs.Begin(), endIt = newParagraphGlyphs.End(); it != endIt; ++it)
354     {
355       const GlyphIndex index = *it;
356       GlyphInfo&       glyph = *(glyphsBuffer + index);
357
358       glyph.xBearing = 0.f;
359       glyph.width    = 0.f;
360       glyph.advance  = 0.f;
361     }
362     updated = true;
363   }
364
365   if((nullptr != impl.mEventData) &&
366      impl.mEventData->mPreEditFlag &&
367      (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count()))
368   {
369     Dali::InputMethodContext::PreEditAttributeDataContainer attrs;
370     impl.mEventData->mInputMethodContext.GetPreeditStyle(attrs);
371     Dali::InputMethodContext::PreeditStyle type = Dali::InputMethodContext::PreeditStyle::NONE;
372
373     // Check the type of preedit and run it.
374     for(Dali::InputMethodContext::PreEditAttributeDataContainer::Iterator it = attrs.Begin(), endIt = attrs.End(); it != endIt; it++)
375     {
376       Dali::InputMethodContext::PreeditAttributeData attrData = *it;
377       DALI_LOG_INFO(gLogFilter, Debug::General, "Controller::UpdateModel PreeditStyle type : %d  start %d end %d \n", attrData.preeditType, attrData.startIndex, attrData.endIndex);
378       type = attrData.preeditType;
379
380       // Check the number of commit characters for the start position.
381       unsigned int numberOfCommit  = impl.mEventData->mPrimaryCursorPosition - impl.mEventData->mPreEditLength;
382       Length       numberOfIndices = attrData.endIndex - attrData.startIndex;
383
384       switch(type)
385       {
386         case Dali::InputMethodContext::PreeditStyle::UNDERLINE:
387         {
388           // Add the underline for the pre-edit text.
389           UnderlinedGlyphRun underlineRun;
390           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
391           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
392           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
393
394           //Mark-up processor case
395           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
396              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
397           {
398             impl.CopyUnderlinedFromLogicalToVisualModels(false);
399           }
400           break;
401         }
402         case Dali::InputMethodContext::PreeditStyle::REVERSE:
403         {
404           Vector4  textColor = impl.mModel->mVisualModel->GetTextColor();
405           ColorRun backgroundColorRun;
406           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
407           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
408           backgroundColorRun.color                           = textColor;
409           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
410
411           Vector4 backgroundColor = impl.mModel->mVisualModel->GetBackgroundColor();
412           if(Dali::EqualsZero(backgroundColor.a)) // There is no text background color.
413           {
414             // Try use the control's background color.
415             if(nullptr != impl.mEditableControlInterface)
416             {
417               impl.mEditableControlInterface->GetControlBackgroundColor(backgroundColor);
418               if(Dali::EqualsZero(backgroundColor.a)) // There is no control background color.
419               {
420                 // Determines black or white color according to text color.
421                 // Based on W3C Recommendations (https://www.w3.org/TR/WCAG20/)
422                 float L         = CONSTANT_R * textColor.r + CONSTANT_G * textColor.g + CONSTANT_B * textColor.b;
423                 backgroundColor = L > BRIGHTNESS_THRESHOLD ? BLACK : WHITE;
424               }
425             }
426           }
427
428           Vector<ColorRun> colorRuns;
429           colorRuns.Resize(1u);
430           ColorRun& colorRun                       = *(colorRuns.Begin());
431           colorRun.color                           = backgroundColor;
432           colorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
433           colorRun.characterRun.numberOfCharacters = numberOfIndices;
434           impl.mModel->mLogicalModel->mColorRuns.PushBack(colorRun);
435
436           //Mark-up processor case
437           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
438              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
439           {
440             impl.CopyUnderlinedFromLogicalToVisualModels(false);
441           }
442           break;
443         }
444         case Dali::InputMethodContext::PreeditStyle::HIGHLIGHT:
445         {
446           ColorRun backgroundColorRun;
447           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
448           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
449           backgroundColorRun.color                           = LIGHT_BLUE;
450           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
451
452           //Mark-up processor case
453           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
454              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
455           {
456             impl.CopyUnderlinedFromLogicalToVisualModels(false);
457           }
458           break;
459         }
460         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_1:
461         {
462           // CUSTOM_PLATFORM_STYLE_1 should be drawn with background and underline together.
463           ColorRun backgroundColorRun;
464           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
465           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
466           backgroundColorRun.color                           = BACKGROUND_SUB4;
467           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
468
469           UnderlinedGlyphRun underlineRun;
470           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
471           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
472           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
473
474           //Mark-up processor case
475           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
476              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
477           {
478             impl.CopyUnderlinedFromLogicalToVisualModels(false);
479           }
480           break;
481         }
482         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_2:
483         {
484           // CUSTOM_PLATFORM_STYLE_2 should be drawn with background and underline together.
485           ColorRun backgroundColorRun;
486           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
487           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
488           backgroundColorRun.color                           = BACKGROUND_SUB5;
489           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
490
491           UnderlinedGlyphRun underlineRun;
492           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
493           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
494           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
495
496           //Mark-up processor case
497           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
498              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
499           {
500             impl.CopyUnderlinedFromLogicalToVisualModels(false);
501           }
502           break;
503         }
504         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_3:
505         {
506           // CUSTOM_PLATFORM_STYLE_3 should be drawn with background and underline together.
507           ColorRun backgroundColorRun;
508           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
509           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
510           backgroundColorRun.color                           = BACKGROUND_SUB6;
511           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
512
513           UnderlinedGlyphRun underlineRun;
514           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
515           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
516           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
517
518           //Mark-up processor case
519           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
520              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
521           {
522             impl.CopyUnderlinedFromLogicalToVisualModels(false);
523           }
524           break;
525         }
526         case Dali::InputMethodContext::PreeditStyle::CUSTOM_PLATFORM_STYLE_4:
527         {
528           // CUSTOM_PLATFORM_STYLE_4 should be drawn with background and underline together.
529           ColorRun backgroundColorRun;
530           backgroundColorRun.characterRun.characterIndex     = attrData.startIndex + numberOfCommit;
531           backgroundColorRun.characterRun.numberOfCharacters = numberOfIndices;
532           backgroundColorRun.color                           = BACKGROUND_SUB7;
533           impl.mModel->mLogicalModel->mBackgroundColorRuns.PushBack(backgroundColorRun);
534
535           UnderlinedGlyphRun underlineRun;
536           underlineRun.glyphRun.glyphIndex     = attrData.startIndex + numberOfCommit;
537           underlineRun.glyphRun.numberOfGlyphs = numberOfIndices;
538           impl.mModel->mVisualModel->mUnderlineRuns.PushBack(underlineRun);
539
540           //Mark-up processor case
541           if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
542              impl.mModel->mLogicalModel->mUnderlineRunsUpdated)
543           {
544             impl.CopyUnderlinedFromLogicalToVisualModels(false);
545           }
546           break;
547         }
548         case Dali::InputMethodContext::PreeditStyle::NONE:
549         default:
550         {
551           break;
552         }
553       }
554     }
555     attrs.Clear();
556     updated = true;
557   }
558
559   if(Controller::NO_OPERATION != (Controller::COLOR & operations))
560   {
561     // Set the color runs in glyphs.
562     SetColorSegmentationInfo(impl.mModel->mLogicalModel->mColorRuns,
563                              impl.mModel->mVisualModel->mCharactersToGlyph,
564                              impl.mModel->mVisualModel->mGlyphsPerCharacter,
565                              startIndex,
566                              impl.mTextUpdateInfo.mStartGlyphIndex,
567                              requestedNumberOfCharacters,
568                              impl.mModel->mVisualModel->mColors,
569                              impl.mModel->mVisualModel->mColorIndices);
570
571     // Set the background color runs in glyphs.
572     SetColorSegmentationInfo(impl.mModel->mLogicalModel->mBackgroundColorRuns,
573                              impl.mModel->mVisualModel->mCharactersToGlyph,
574                              impl.mModel->mVisualModel->mGlyphsPerCharacter,
575                              startIndex,
576                              impl.mTextUpdateInfo.mStartGlyphIndex,
577                              requestedNumberOfCharacters,
578                              impl.mModel->mVisualModel->mBackgroundColors,
579                              impl.mModel->mVisualModel->mBackgroundColorIndices);
580
581     updated = true;
582   }
583
584   if((Controller::NO_OPERATION != (Controller::SHAPE_TEXT & operations)) &&
585      !((nullptr != impl.mEventData) &&
586        impl.mEventData->mPreEditFlag &&
587        (0u != impl.mModel->mVisualModel->mCharactersToGlyph.Count())))
588   {
589     //Mark-up processor case
590     if(impl.mModel->mVisualModel->IsMarkupProcessorEnabled() ||
591        impl.mModel->mLogicalModel->mUnderlineRunsUpdated ||
592        impl.mModel->mLogicalModel->mCharacterSpacingRunsUpdated ||
593        impl.mModel->mLogicalModel->mStrikethroughRunsUpdated)
594     {
595       impl.CopyUnderlinedFromLogicalToVisualModels(true);
596       impl.CopyStrikethroughFromLogicalToVisualModels();
597       impl.CopyCharacterSpacingFromLogicalToVisualModels();
598     }
599
600     updated = true;
601   }
602
603   // The estimated number of lines. Used to avoid reallocations when layouting.
604   impl.mTextUpdateInfo.mEstimatedNumberOfLines = std::max(impl.mModel->mVisualModel->mLines.Count(), impl.mModel->mLogicalModel->mParagraphInfo.Count());
605
606   // Set the previous number of characters for the next time the text is updated.
607   impl.mTextUpdateInfo.mPreviousNumberOfCharacters = numberOfCharacters;
608
609   return updated;
610 }
611
612 } // namespace Dali::Toolkit::Text