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