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