Merge "fix issue in negative line spacing with key arrow down" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-view.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-view.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/font-client.h>
23 #include <dali/public-api/math/vector2.h>
24 #include <memory.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/internal/text/glyph-metrics-helper.h>
28 #include <dali-toolkit/internal/text/rendering/styles/character-spacing-helper-functions.h>
29
30 namespace Dali
31 {
32 namespace Toolkit
33 {
34 namespace Text
35 {
36 struct View::Impl
37 {
38   VisualModelPtr              mVisualModel;
39   LogicalModelPtr             mLogicalModel;
40   TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
41 };
42
43 View::View()
44 : mImpl(NULL)
45 {
46   mImpl = new View::Impl();
47
48   mImpl->mFontClient = TextAbstraction::FontClient::Get();
49 }
50
51 View::~View()
52 {
53   delete mImpl;
54 }
55
56 void View::SetVisualModel(VisualModelPtr visualModel)
57 {
58   mImpl->mVisualModel = visualModel;
59 }
60
61 void View::SetLogicalModel(LogicalModelPtr logicalModel)
62 {
63   mImpl->mLogicalModel = logicalModel;
64 }
65
66 const Vector2& View::GetControlSize() const
67 {
68   if(mImpl->mVisualModel)
69   {
70     return mImpl->mVisualModel->mControlSize;
71   }
72
73   return Vector2::ZERO;
74 }
75
76 const Vector2& View::GetLayoutSize() const
77 {
78   if(mImpl->mVisualModel)
79   {
80     return mImpl->mVisualModel->GetLayoutSize();
81   }
82
83   return Vector2::ZERO;
84 }
85
86 Length View::GetNumberOfGlyphs() const
87 {
88   if(mImpl->mVisualModel)
89   {
90     const VisualModel& model = *mImpl->mVisualModel;
91
92     const Length glyphCount    = model.mGlyphs.Count();
93     const Length positionCount = model.mGlyphPositions.Count();
94
95     DALI_ASSERT_DEBUG(positionCount <= glyphCount && "Invalid glyph positions in Model");
96
97     return (positionCount < glyphCount) ? positionCount : glyphCount;
98   }
99
100   return 0;
101 }
102
103 Length View::GetGlyphs(GlyphInfo* glyphs,
104                        Vector2*   glyphPositions,
105                        float&     minLineOffset,
106                        GlyphIndex glyphIndex,
107                        Length     numberOfGlyphs) const
108 {
109   Length                  numberOfLaidOutGlyphs       = 0u;
110   Length                  numberOfActualLaidOutGlyphs = 0u;
111   const float             modelCharacterSpacing       = mImpl->mVisualModel->GetCharacterSpacing();
112   Vector<CharacterIndex>& glyphToCharacterMap         = mImpl->mVisualModel->mGlyphsToCharacters;
113   const CharacterIndex*   glyphToCharacterMapBuffer   = glyphToCharacterMap.Begin();
114   float                   calculatedAdvance           = 0.f;
115   const Character*        textBuffer                  = mImpl->mLogicalModel->mText.Begin();
116
117
118   if(mImpl->mVisualModel)
119   {
120     // Get the character-spacing runs.
121     const Vector<CharacterSpacingGlyphRun>& characterSpacingGlyphRuns = mImpl->mVisualModel->GetCharacterSpacingGlyphRuns();
122
123     bool                              textElided       = false;
124     DevelText::EllipsisPosition::Type ellipsisPosition = GetEllipsisPosition();
125
126     //Reset indices of ElidedGlyphs
127     mImpl->mVisualModel->SetStartIndexOfElidedGlyphs(0u);
128     mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(numberOfGlyphs);
129     mImpl->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(0u);
130     mImpl->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(0u);
131
132     // If ellipsis is enabled, the number of glyphs the layout engine has laid out may be less than 'numberOfGlyphs'.
133     // Check the last laid out line to know if the layout engine elided some text.
134
135     const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
136     if(numberOfLines > 0u)
137     {
138       const LineRun* const lines = mImpl->mVisualModel->mLines.Begin();
139
140       //Get line of ellipsis
141       const LineRun* ellipsisLine     = nullptr;
142       const LineRun* ellipsisNextLine = nullptr;
143       bool           hasEllipsis      = false;
144       for(Length lineIndex = 0; lineIndex < numberOfLines; lineIndex++)
145       {
146         const LineRun* line = (lines + lineIndex);
147         if(line->ellipsis)
148         {
149           ellipsisLine = line;
150           hasEllipsis  = true;
151           if(lineIndex < numberOfLines - 1u)
152           {
153             ellipsisNextLine = (lines + lineIndex + 1u);
154           }
155           break;
156         }
157       }
158
159       // If ellipsis is enabled, calculate the number of laid out glyphs.
160       // Otherwise use the given number of glyphs.
161       if(hasEllipsis)
162       {
163         textElided            = true;
164         numberOfLaidOutGlyphs = numberOfGlyphs;
165
166         if(ellipsisPosition == DevelText::EllipsisPosition::START)
167         {
168           numberOfActualLaidOutGlyphs = numberOfGlyphs - ellipsisLine->glyphRun.glyphIndex;
169         }
170         else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
171         {
172           numberOfActualLaidOutGlyphs = 0u;
173           for(Length lineIndex = 0u; lineIndex < numberOfLines; lineIndex++)
174           {
175             numberOfActualLaidOutGlyphs += lines[lineIndex].glyphRun.numberOfGlyphs + lines[lineIndex].glyphRunSecondHalf.numberOfGlyphs;
176           }
177         }
178         else // DevelText::EllipsisPosition::END
179         {
180           numberOfActualLaidOutGlyphs = ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs;
181         }
182       }
183       else
184       {
185         numberOfActualLaidOutGlyphs = numberOfLaidOutGlyphs = numberOfGlyphs;
186       }
187
188       if(0u < numberOfActualLaidOutGlyphs)
189       {
190         // Retrieve from the visual model the glyphs and positions.
191         mImpl->mVisualModel->GetGlyphs(glyphs,
192                                        glyphIndex,
193                                        numberOfLaidOutGlyphs);
194
195         mImpl->mVisualModel->GetGlyphPositions(glyphPositions,
196                                                glyphIndex,
197                                                numberOfLaidOutGlyphs);
198
199         // Get the lines for the given range of glyphs.
200         // The lines contain the alignment offset which needs to be added to the glyph's position.
201         LineIndex firstLineIndex = 0u;
202         Length    numberOfLines  = 0u;
203         mImpl->mVisualModel->GetNumberOfLines(glyphIndex,
204                                               numberOfLaidOutGlyphs,
205                                               firstLineIndex,
206                                               numberOfLines);
207
208         Vector<LineRun> lines;
209         lines.Resize(numberOfLines);
210         LineRun* lineBuffer = lines.Begin();
211
212         mImpl->mVisualModel->GetLinesOfGlyphRange(lineBuffer,
213                                                   glyphIndex,
214                                                   numberOfLaidOutGlyphs);
215
216         // Get the first line for the given glyph range.
217         LineIndex lineIndex = firstLineIndex;
218         LineRun*  line      = lineBuffer + lineIndex;
219
220         // Index of the last glyph of the line.
221         GlyphIndex lastGlyphIndexOfLine = (line->isSplitToTwoHalves ? line->glyphRunSecondHalf.glyphIndex + line->glyphRunSecondHalf.numberOfGlyphs : line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs) - 1u;
222
223         // Add the alignment offset to the glyph's position.
224
225         minLineOffset = line->alignmentOffset;
226         float penY    = line->ascender;
227         for(Length index = 0u; index < numberOfLaidOutGlyphs; ++index)
228         {
229           Vector2& position = *(glyphPositions + index);
230           position.x += line->alignmentOffset;
231           position.y += penY;
232
233           if(lastGlyphIndexOfLine == index)
234           {
235             penY += -line->descender + line->lineSpacing;
236
237             // Get the next line.
238             ++lineIndex;
239
240             if(lineIndex < numberOfLines)
241             {
242               line          = lineBuffer + lineIndex;
243               minLineOffset = std::min(minLineOffset, line->alignmentOffset);
244
245               lastGlyphIndexOfLine = (line->isSplitToTwoHalves ? line->glyphRunSecondHalf.glyphIndex + line->glyphRunSecondHalf.numberOfGlyphs : line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs) - 1u;
246
247               penY += line->ascender;
248             }
249           }
250         }
251
252         // Set index where to set Ellipsis according to the selected position of Ellipsis.
253         // Start with this index to replace its glyph by Ellipsis, if the width  is not enough, then remove more glyphs.
254         GlyphIndex startIndexOfEllipsis = 0u;
255         if(hasEllipsis)
256         {
257           if(ellipsisPosition == DevelText::EllipsisPosition::START)
258           {
259             // It's the fisrt glyph in line.
260             startIndexOfEllipsis = ellipsisLine->glyphRun.glyphIndex;
261           }
262           else if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
263           {
264             // It's the second middle of the line in case the line split to two halves.
265             // Otherwise it's It's the last glyph in line (line before all removed lines).
266             startIndexOfEllipsis = ellipsisLine->isSplitToTwoHalves ? (ellipsisLine->glyphRunSecondHalf.glyphIndex) : (ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u);
267           }
268           else // DevelText::EllipsisPosition::END
269           {
270             // It's the last glyph in line.
271             startIndexOfEllipsis = ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u;
272           }
273         }
274
275         if(1u == numberOfLaidOutGlyphs)
276         {
277           // not a point try to do ellipsis with only one laid out character.
278
279           return numberOfLaidOutGlyphs;
280         }
281
282         if(textElided)
283         {
284           const LineRun& elidedLine = *ellipsisLine;
285
286           if((1u == numberOfLines) &&
287              (GetLineHeight(elidedLine, true) > mImpl->mVisualModel->mControlSize.height))
288           {
289             // Replace the first glyph with ellipsis glyph
290             auto indexOfFirstGlyph = (ellipsisPosition == DevelText::EllipsisPosition::START) ? startIndexOfEllipsis : 0u;
291
292             // Regardless where the location of ellipsis,in-case the hight of line is greater than control's height
293             // then replace the first glyph with ellipsis glyph.
294
295             // Get the first glyph which is going to be replaced and the ellipsis glyph.
296             GlyphInfo&       glyphInfo     = *(glyphs + indexOfFirstGlyph);
297             const GlyphInfo& ellipsisGlyph = mImpl->mFontClient.GetEllipsisGlyph(mImpl->mFontClient.GetPointSize(glyphInfo.fontId));
298
299             // Change the 'x' and 'y' position of the ellipsis glyph.
300             Vector2& position = *(glyphPositions + indexOfFirstGlyph);
301             position.x        = ellipsisGlyph.xBearing;
302             position.y        = mImpl->mVisualModel->mControlSize.height - ellipsisGlyph.yBearing;
303
304             // Replace the glyph by the ellipsis glyph.
305             glyphInfo = ellipsisGlyph;
306
307             mImpl->mVisualModel->SetStartIndexOfElidedGlyphs(indexOfFirstGlyph);
308             mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(indexOfFirstGlyph);
309             mImpl->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(indexOfFirstGlyph);
310             mImpl->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(indexOfFirstGlyph);
311
312             numberOfLaidOutGlyphs = 1u;
313
314             return numberOfLaidOutGlyphs;
315           }
316
317           // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed.
318           float firstPenX   = 0.f; // Used if rtl text is elided.
319           float penY        = 0.f;
320           bool  firstPenSet = false;
321
322           // Add the ellipsis glyph.
323           bool       inserted              = false;
324           float      removedGlypsWidth     = 0.f;
325           Length     numberOfRemovedGlyphs = 0u;
326           GlyphIndex indexOfEllipsis       = startIndexOfEllipsis;
327
328           // Tail Mode: start by the end of line.
329           const bool isTailMode = ellipsisPosition == DevelText::EllipsisPosition::END ||
330                                   (ellipsisPosition == DevelText::EllipsisPosition::MIDDLE && numberOfLines != 1u);
331
332           // The ellipsis glyph has to fit in the place where the last glyph(s) is(are) removed.
333           while(!inserted)
334           {
335             const GlyphInfo& glyphToRemove = *(glyphs + indexOfEllipsis);
336
337             if(0u != glyphToRemove.fontId)
338             {
339               // i.e. The font id of the glyph shaped from the '\n' character is zero.
340
341               // Need to reshape the glyph as the font may be different in size.
342               const GlyphInfo& ellipsisGlyph = mImpl->mFontClient.GetEllipsisGlyph(mImpl->mFontClient.GetPointSize(glyphToRemove.fontId));
343
344               if(!firstPenSet)
345               {
346                 const Vector2& position = *(glyphPositions + indexOfEllipsis);
347
348                 // Calculates the penY of the current line. It will be used to position the ellipsis glyph.
349                 penY = position.y + glyphToRemove.yBearing;
350
351                 // Calculates the first penX which will be used if rtl text is elided.
352                 firstPenX = position.x - glyphToRemove.xBearing;
353                 if(firstPenX < -ellipsisGlyph.xBearing)
354                 {
355                   // Avoids to exceed the bounding box when rtl text is elided.
356                   firstPenX = -ellipsisGlyph.xBearing;
357                 }
358
359                 removedGlypsWidth = -ellipsisGlyph.xBearing;
360
361                 firstPenSet = true;
362               }
363
364               const float characterSpacing = GetGlyphCharacterSpacing(indexOfEllipsis, characterSpacingGlyphRuns, modelCharacterSpacing);
365               calculatedAdvance            = GetCalculatedAdvance(*(textBuffer + (*(glyphToCharacterMapBuffer + indexOfEllipsis))), characterSpacing, glyphToRemove.advance);
366               removedGlypsWidth += std::min(calculatedAdvance, (glyphToRemove.xBearing + glyphToRemove.width));
367
368               // Calculate the width of the ellipsis glyph and check if it fits.
369               const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing;
370               if((ellipsisGlyphWidth < removedGlypsWidth) || (isTailMode ? (indexOfEllipsis == 0u) : (indexOfEllipsis == numberOfGlyphs - 1u)))
371               {
372                 GlyphInfo& glyphInfo = *(glyphs + indexOfEllipsis);
373                 Vector2&   position  = *(glyphPositions + indexOfEllipsis);
374                 position.x -= (0.f > glyphInfo.xBearing) ? glyphInfo.xBearing : 0.f;
375
376                 // Replace the glyph by the ellipsis glyph.
377                 glyphInfo = ellipsisGlyph;
378
379                 // Change the 'x' and 'y' position of the ellipsis glyph.
380
381                 if(position.x > firstPenX)
382                 {
383                   position.x = firstPenX + removedGlypsWidth - ellipsisGlyphWidth;
384                 }
385
386                 position.x += ellipsisGlyph.xBearing;
387                 position.y = penY - ellipsisGlyph.yBearing;
388
389                 inserted = true;
390               }
391             }
392
393             if(!inserted)
394             {
395               if(isTailMode && indexOfEllipsis > 0u)
396               {
397                 // Tail Mode: remove glyphs from startIndexOfEllipsis then decrement indexOfEllipsis, until arrive to index zero.
398                 --indexOfEllipsis;
399               }
400               else if(!isTailMode && indexOfEllipsis < numberOfLaidOutGlyphs - 1u)
401               {
402                 // Not Tail Mode: remove glyphs from startIndexOfEllipsis then increase indexOfEllipsis, until arrive to last index (numberOfGlyphs - 1u).
403                 ++indexOfEllipsis;
404               }
405               else
406               {
407                 // No space for the ellipsis.
408                 inserted = true;
409               }
410               ++numberOfRemovedGlyphs;
411             }
412           }
413
414           // 'Removes' all the glyphs after the ellipsis glyph.
415           if(ellipsisPosition == DevelText::EllipsisPosition::MIDDLE)
416           {
417             //Reduce size, shift glyphs and start from ellipsis glyph
418             numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
419
420             GlyphIndex firstMiddleIndexOfElidedGlyphs  = 0u;
421             GlyphIndex secondMiddleIndexOfElidedGlyphs = 0u;
422
423             bool isOnlySecondHalf = false;
424             if(isTailMode)
425             {
426               // Multi-lines case with MIDDLE
427               // In case the Ellipsis in the end of line,
428               // then this index will be the firstMiddleIndex.
429               // The secondMiddleIndex will be the fisrt index in next line.
430               // But in case there is no line after Ellipsis's line then secondMiddleIndex and endIndex equal firstMiddle
431               // Example:
432               // A: are laid out glyphs in line has Ellipsis in the end.
433               // N: are laid out glyphs in lines after removed lines.
434               // R: are removed glyphs.
435               // L: are removed glyphs when removed lines.
436               // AAAAAAAAAAAA...RRR    => Here's the firstMiddleIndex (First index after last A)
437               // LLLLLLLLLLLLLLL
438               // LLLLLLLLLLLLLLL
439               // NNNNNNNNNNNNNN        => Here's the secondMiddleIndex (First N)
440               // NNNNNNNNNN
441
442               firstMiddleIndexOfElidedGlyphs = indexOfEllipsis;
443               if(ellipsisNextLine != nullptr)
444               {
445                 secondMiddleIndexOfElidedGlyphs = ellipsisNextLine->glyphRun.glyphIndex;
446               }
447               else
448               {
449                 secondMiddleIndexOfElidedGlyphs = firstMiddleIndexOfElidedGlyphs;
450                 mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(firstMiddleIndexOfElidedGlyphs);
451               }
452             }
453             else
454             {
455               // Single line case with MIDDLE
456               // In case the Ellipsis in the middle of line,
457               // Then the last index in first half will be firstMiddleIndex.
458               // And the indexOfEllipsis will be secondMiddleIndex, which is the first index in second half.
459               // Example:
460               // A: are laid out glyphs in first half of line.
461               // N: are laid out glyphs in second half of line.
462               // R: are removed glyphs.
463               // L: re removed glyphs when layouting text
464               // AAAAAAALLLLLLLLLLLRRR...NNNNN
465               // firstMiddleIndex (index of last A)
466               // secondMiddleIndex (index before first N)
467
468               firstMiddleIndexOfElidedGlyphs  = (ellipsisLine->glyphRun.numberOfGlyphs > 0u) ? (ellipsisLine->glyphRun.glyphIndex + ellipsisLine->glyphRun.numberOfGlyphs - 1u) : (ellipsisLine->glyphRun.glyphIndex);
469               secondMiddleIndexOfElidedGlyphs = indexOfEllipsis;
470               isOnlySecondHalf                = ellipsisLine->glyphRun.numberOfGlyphs == 0u && ellipsisLine->glyphRunSecondHalf.numberOfGlyphs > 0u;
471             }
472
473             mImpl->mVisualModel->SetFirstMiddleIndexOfElidedGlyphs(firstMiddleIndexOfElidedGlyphs);
474             mImpl->mVisualModel->SetSecondMiddleIndexOfElidedGlyphs(secondMiddleIndexOfElidedGlyphs);
475
476             // The number of shifted glyphs and shifting positions will be different according to Single-line or Multi-lines.
477             // isOnlySecondHalf will be true when MIDDLE Ellipsis glyph in single line.
478             if(isOnlySecondHalf)
479             {
480               Length numberOfSecondHalfGlyphs = numberOfLaidOutGlyphs - firstMiddleIndexOfElidedGlyphs;
481
482               //Copy elided glyphs after the ellipsis glyph.
483               memcpy(glyphs + firstMiddleIndexOfElidedGlyphs, glyphs + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(GlyphInfo));
484               memcpy(glyphPositions + firstMiddleIndexOfElidedGlyphs, glyphPositions + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(Vector2));
485             }
486             else
487             {
488               Length numberOfSecondHalfGlyphs = numberOfLaidOutGlyphs - firstMiddleIndexOfElidedGlyphs + 1u;
489
490               //Copy elided glyphs after the ellipsis glyph.
491               memcpy(glyphs + firstMiddleIndexOfElidedGlyphs + 1u, glyphs + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(GlyphInfo));
492               memcpy(glyphPositions + firstMiddleIndexOfElidedGlyphs + 1u, glyphPositions + secondMiddleIndexOfElidedGlyphs, numberOfSecondHalfGlyphs * sizeof(Vector2));
493             }
494           }
495           else if(ellipsisPosition == DevelText::EllipsisPosition::START)
496           {
497             numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
498             //Copy elided glyphs after the ellipsis glyph.
499             memcpy(glyphs, glyphs + startIndexOfEllipsis + numberOfRemovedGlyphs, numberOfLaidOutGlyphs * sizeof(GlyphInfo));
500             memcpy(glyphPositions, glyphPositions + startIndexOfEllipsis + numberOfRemovedGlyphs, numberOfLaidOutGlyphs * sizeof(Vector2));
501             mImpl->mVisualModel->SetStartIndexOfElidedGlyphs(indexOfEllipsis);
502           }
503           else // DevelText::EllipsisPosition::END
504           {
505             numberOfLaidOutGlyphs = numberOfActualLaidOutGlyphs - numberOfRemovedGlyphs;
506             mImpl->mVisualModel->SetEndIndexOfElidedGlyphs(indexOfEllipsis);
507           }
508         }
509       }
510     }
511   }
512
513   return numberOfLaidOutGlyphs;
514 }
515
516 const Vector4* const View::GetColors() const
517 {
518   if(mImpl->mVisualModel)
519   {
520     return mImpl->mVisualModel->mColors.Begin();
521   }
522
523   return NULL;
524 }
525
526 const ColorIndex* const View::GetColorIndices() const
527 {
528   if(mImpl->mVisualModel)
529   {
530     return mImpl->mVisualModel->mColorIndices.Begin();
531   }
532
533   return NULL;
534 }
535
536 const Vector4* const View::GetBackgroundColors() const
537 {
538   if(mImpl->mVisualModel)
539   {
540     return mImpl->mVisualModel->mBackgroundColors.Begin();
541   }
542
543   return nullptr;
544 }
545
546 const ColorIndex* const View::GetBackgroundColorIndices() const
547 {
548   if(mImpl->mVisualModel)
549   {
550     return mImpl->mVisualModel->mBackgroundColorIndices.Begin();
551   }
552
553   return nullptr;
554 }
555
556 bool const View::IsMarkupBackgroundColorSet() const
557 {
558   if(mImpl->mVisualModel)
559   {
560     return (mImpl->mVisualModel->mBackgroundColors.Count() > 0);
561   }
562
563   return false;
564 }
565
566 const Vector4& View::GetTextColor() const
567 {
568   if(mImpl->mVisualModel)
569   {
570     return mImpl->mVisualModel->GetTextColor();
571   }
572   return Vector4::ZERO;
573 }
574
575 const Vector2& View::GetShadowOffset() const
576 {
577   if(mImpl->mVisualModel)
578   {
579     return mImpl->mVisualModel->GetShadowOffset();
580   }
581   return Vector2::ZERO;
582 }
583
584 const Vector4& View::GetShadowColor() const
585 {
586   if(mImpl->mVisualModel)
587   {
588     return mImpl->mVisualModel->GetShadowColor();
589   }
590   return Vector4::ZERO;
591 }
592
593 const Vector4& View::GetUnderlineColor() const
594 {
595   if(mImpl->mVisualModel)
596   {
597     return mImpl->mVisualModel->GetUnderlineColor();
598   }
599   return Vector4::ZERO;
600 }
601
602 bool View::IsUnderlineEnabled() const
603 {
604   if(mImpl->mVisualModel)
605   {
606     return mImpl->mVisualModel->IsUnderlineEnabled();
607   }
608   return false;
609 }
610
611 const GlyphInfo* View::GetHyphens() const
612 {
613   if(mImpl->mVisualModel)
614   {
615     return mImpl->mVisualModel->mHyphen.glyph.Begin();
616   }
617
618   return nullptr;
619 }
620
621 const Length* View::GetHyphenIndices() const
622 {
623   if(mImpl->mVisualModel)
624   {
625     return mImpl->mVisualModel->mHyphen.index.Begin();
626   }
627
628   return nullptr;
629 }
630
631 Length View::GetHyphensCount() const
632 {
633   if(mImpl->mVisualModel)
634   {
635     return mImpl->mVisualModel->mHyphen.glyph.Size();
636   }
637
638   return 0;
639 }
640 float View::GetUnderlineHeight() const
641 {
642   if(mImpl->mVisualModel)
643   {
644     return mImpl->mVisualModel->GetUnderlineHeight();
645   }
646   return 0.0f;
647 }
648
649 Text::Underline::Type View::GetUnderlineType() const
650 {
651   Text::Underline::Type type = Text::Underline::Type::SOLID;
652   if(mImpl->mVisualModel)
653   {
654     type = mImpl->mVisualModel->GetUnderlineType();
655   }
656   return type;
657 }
658
659 float View::GetDashedUnderlineWidth() const
660 {
661   float width = 0.0f;
662   if(mImpl->mVisualModel)
663   {
664     width = mImpl->mVisualModel->GetDashedUnderlineWidth();
665   }
666   return width;
667 }
668
669 float View::GetDashedUnderlineGap() const
670 {
671   float gap = 0.0f;
672   if(mImpl->mVisualModel)
673   {
674     gap = mImpl->mVisualModel->GetDashedUnderlineGap();
675   }
676   return gap;
677 }
678
679 Length View::GetNumberOfUnderlineRuns() const
680 {
681   if(mImpl->mVisualModel)
682   {
683     return mImpl->mVisualModel->GetNumberOfUnderlineRuns();
684   }
685
686   return 0u;
687 }
688
689 void View::GetUnderlineRuns(UnderlinedGlyphRun* underlineRuns,
690                             UnderlineRunIndex   index,
691                             Length              numberOfRuns) const
692 {
693   if(mImpl->mVisualModel)
694   {
695     mImpl->mVisualModel->GetUnderlineRuns(underlineRuns,
696                                           index,
697                                           numberOfRuns);
698   }
699 }
700
701 const Vector4& View::GetOutlineColor() const
702 {
703   if(mImpl->mVisualModel)
704   {
705     return mImpl->mVisualModel->GetOutlineColor();
706   }
707   return Vector4::ZERO;
708 }
709
710 uint16_t View::GetOutlineWidth() const
711 {
712   if(mImpl->mVisualModel)
713   {
714     return mImpl->mVisualModel->GetOutlineWidth();
715   }
716   return 0u;
717 }
718
719 DevelText::EllipsisPosition::Type View::GetEllipsisPosition() const
720 {
721   DevelText::EllipsisPosition::Type ellipsisPosition = DevelText::EllipsisPosition::END;
722   if(mImpl->mVisualModel)
723   {
724     const VisualModel& model = *mImpl->mVisualModel;
725     ellipsisPosition         = model.GetEllipsisPosition();
726   }
727
728   return ellipsisPosition;
729 }
730
731 bool View::IsTextElideEnabled() const
732 {
733   bool isTextElideEnabled = false;
734
735   if(mImpl->mVisualModel)
736   {
737     const VisualModel& model = *mImpl->mVisualModel;
738     isTextElideEnabled       = model.IsTextElideEnabled();
739   }
740
741   return isTextElideEnabled;
742 }
743
744 GlyphIndex View::GetStartIndexOfElidedGlyphs() const
745 {
746   GlyphIndex startIndexOfElidedGlyphs = 0u;
747
748   if(mImpl->mVisualModel)
749   {
750     const VisualModel& model = *mImpl->mVisualModel;
751     startIndexOfElidedGlyphs = model.GetStartIndexOfElidedGlyphs();
752   }
753
754   return startIndexOfElidedGlyphs;
755 }
756
757 GlyphIndex View::GetEndIndexOfElidedGlyphs() const
758 {
759   GlyphIndex endIndexOfElidedGlyphs = 0u;
760
761   if(mImpl->mVisualModel)
762   {
763     const VisualModel& model = *mImpl->mVisualModel;
764     endIndexOfElidedGlyphs   = model.GetEndIndexOfElidedGlyphs();
765   }
766
767   return endIndexOfElidedGlyphs;
768 }
769
770 GlyphIndex View::GetFirstMiddleIndexOfElidedGlyphs() const
771 {
772   GlyphIndex firstMiddleIndexOfElidedGlyphs = 0u;
773
774   if(mImpl->mVisualModel)
775   {
776     const VisualModel& model       = *mImpl->mVisualModel;
777     firstMiddleIndexOfElidedGlyphs = model.GetFirstMiddleIndexOfElidedGlyphs();
778   }
779
780   return firstMiddleIndexOfElidedGlyphs;
781 }
782
783 GlyphIndex View::GetSecondMiddleIndexOfElidedGlyphs() const
784 {
785   GlyphIndex secondMiddleIndexOfElidedGlyphs = 0u;
786
787   if(mImpl->mVisualModel)
788   {
789     const VisualModel& model        = *mImpl->mVisualModel;
790     secondMiddleIndexOfElidedGlyphs = model.GetSecondMiddleIndexOfElidedGlyphs();
791   }
792
793   return secondMiddleIndexOfElidedGlyphs;
794 }
795
796 const Vector4& View::GetStrikethroughColor() const
797 {
798   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetStrikethroughColor() : Vector4::ZERO;
799 }
800
801 bool View::IsStrikethroughEnabled() const
802 {
803   return (mImpl->mVisualModel) ? mImpl->mVisualModel->IsStrikethroughEnabled() : false;
804 }
805
806 float View::GetStrikethroughHeight() const
807 {
808   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetStrikethroughHeight() : 0.0f;
809 }
810
811 Length View::GetNumberOfStrikethroughRuns() const
812 {
813   if(mImpl->mVisualModel)
814   {
815     return mImpl->mVisualModel->GetNumberOfStrikethroughRuns();
816   }
817
818   return 0u;
819 }
820
821 void View::GetStrikethroughRuns(StrikethroughGlyphRun* strikethroughRuns,
822                                 StrikethroughRunIndex  index,
823                                 Length                 numberOfRuns) const
824 {
825   if(mImpl->mVisualModel)
826   {
827     mImpl->mVisualModel->GetStrikethroughRuns(strikethroughRuns,
828                                               index,
829                                               numberOfRuns);
830   }
831 }
832
833 Length View::GetNumberOfBoundedParagraphRuns() const
834 {
835   if(mImpl->mLogicalModel)
836   {
837     return mImpl->mLogicalModel->GetNumberOfBoundedParagraphRuns();
838   }
839
840   return 0u;
841 }
842
843 const Vector<BoundedParagraphRun>& View::GetBoundedParagraphRuns() const
844 {
845   return mImpl->mLogicalModel->GetBoundedParagraphRuns();
846 }
847
848 Length View::GetNumberOfCharacterSpacingGlyphRuns() const
849 {
850   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetNumberOfCharacterSpacingGlyphRuns() : 0u;
851 }
852
853 const Vector<CharacterSpacingGlyphRun>& View::GetCharacterSpacingGlyphRuns() const
854 {
855   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetCharacterSpacingGlyphRuns() : GetEmptyCharacterSpacingGlyphRuns();
856 }
857
858 const float View::GetCharacterSpacing() const
859 {
860   return (mImpl->mVisualModel) ? mImpl->mVisualModel->GetCharacterSpacing() : 0.f;
861 }
862
863 const Character* View::GetTextBuffer() const
864 {
865   return (mImpl->mVisualModel) ? mImpl->mLogicalModel->mText.Begin() : nullptr;
866 }
867
868 const Vector<CharacterIndex>& View::GetGlyphsToCharacters() const
869 {
870   return mImpl->mVisualModel->GetGlyphsToCharacters();
871 }
872
873 } // namespace Text
874
875 } // namespace Toolkit
876
877 } // namespace Dali