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