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