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