dbec30e861013d6874819e3e2caf4348eff72cf8
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-view / relayout-utilities.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // FILE HEADER
18 #include "relayout-utilities.h"
19
20 // EXTERNAL INCLUDES
21 #include <cmath>
22
23 // INTERNAL INCLUDES
24 #include <dali/dali.h>
25 #include <dali-toolkit/public-api/controls/text-view/text-view.h>
26 #include "text-view-line-processor.h"
27 #include "text-view-word-processor.h"
28 #include "text-view-processor-helper-functions.h"
29 #include "text-view-processor-dbg.h"
30
31 namespace Dali
32 {
33
34 namespace Toolkit
35 {
36
37 namespace Internal
38 {
39
40 namespace TextViewRelayout
41 {
42
43 const float MINIMUM_FADE_BOUNDARY = 0.05f; // When the fade boundary is the same as the text-view boundary, this constant reduces it in order to avoid a zero division.
44
45 RelayoutParameters::RelayoutParameters()
46 : mPositionOffset(),
47   mLineSize(),
48   mWordSize(),
49   mCharacterSize(),
50   mIndices(),
51   mCharacterGlobalIndex( 0u ),
52   mIsFirstCharacter( false ),
53   mIsFirstCharacterOfWord( false ),
54   mIsNewLine( false ),
55   mIsNewLineCharacter( false ),
56   mIsWhiteSpace( false ),
57   mIsVisible( false )
58 {
59 }
60
61 RelayoutParameters::~RelayoutParameters()
62 {
63 }
64
65 FadeParameters::FadeParameters()
66 : mRightFadeBoundary( 0.f ),
67   mRightFadeThreshold( 0.f ),
68   mRightFadeBoundaryOffset( 0.f ),
69   mRightFadeThresholdOffset( 0.f ),
70   mRightAlphaCoeficients(),
71   mLeftFadeBoundary( 0.f ),
72   mLeftFadeThreshold( 0.f ),
73   mLeftFadeBoundaryOffset( 0.f ),
74   mLeftFadeThresholdOffset( 0.f ),
75   mLeftAlphaCoeficients(),
76   mTopFadeBoundary( 0.f ),
77   mTopFadeThreshold( 0.f ),
78   mTopFadeBoundaryOffset( 0.f ),
79   mTopFadeThresholdOffset( 0.f ),
80   mTopAlphaCoeficients(),
81   mBottomFadeBoundary( 0.f ),
82   mBottomFadeThreshold( 0.f ),
83   mBottomFadeBoundaryOffset( 0.f ),
84   mBottomFadeThresholdOffset( 0.f ),
85   mBottomAlphaCoeficients(),
86   mIsPartiallyVisible( false )
87 {
88 }
89
90 FadeParameters::~FadeParameters()
91 {
92 }
93
94 EllipsizeParameters::EllipsizeParameters()
95 : mPosition(),
96   mLineDescender( 0.f ),
97   mLineWidth( 0.f ),
98   mEllipsizeBoundary(),
99   mFirstIndex( 0u ),
100   mLastIndex( 0u ),
101   mEllipsizeLine( false ),
102   mIsLineWidthFullyVisible( false ),
103   mIsLineHeightFullyVisible( false ),
104   mIsNextLineFullyVisibleHeight( false ),
105   mCreateEllipsizedTextActors( false ),
106   mLineFits( false ),
107   mWordFits( false )
108 {
109 }
110
111 EllipsizeParameters::~EllipsizeParameters()
112 {
113 }
114
115 UnderlineInfo::UnderlineInfo()
116 : mMaxHeight( 0.f ),
117   mMaxThickness( 0.f ),
118   mPosition( 0.f )
119 {
120 }
121
122 UnderlineInfo::~UnderlineInfo()
123 {
124 }
125
126 TextUnderlineStatus::TextUnderlineStatus()
127 : mUnderlineInfo(),
128   mCharacterGlobalIndex( 0u ),
129   mLineGlobalIndex( 0u ),
130   mCurrentUnderlineStatus( false )
131 {
132 }
133
134 TextUnderlineStatus::~TextUnderlineStatus()
135 {
136 }
137
138 SubLineLayoutInfo::SubLineLayoutInfo()
139 : mLineLength( 0.f ),
140   mMaxCharHeight( 0.f ),
141   mMaxAscender( 0.f )
142 {
143 }
144
145 SubLineLayoutInfo::~SubLineLayoutInfo()
146 {
147 }
148
149 /**
150  * Whether the given text-actor exceeds the left or the right boundary of the text-view.
151  *
152  * @param[in] position The position of the text-actor.
153  * @param[in] size The size of the text-actor.
154  * @param[in] parantSize The size of the text-view.
155  *
156  * @return \e true if the text-actor exceeds the left or the right boundary of the text-view.
157  */
158 bool IsExceedingWidth( const Vector3& position, const Size& size, const Size& parentSize )
159 {
160   return ( ( position.x < 0.f ) ||
161            ( position.x + size.width > parentSize.width ) );
162 }
163
164 /**
165  * Whether the given text-actor exceeds the top or the bottom boundary of the text-view.
166  *
167  * @param[in] position The position of the text-actor.
168  * @param[in] size The size of the text-actor.
169  * @param[in] parantSize The size of the text-view.
170  *
171  * @return \e true if the text-actor exceeds the top or the bottom boundary of the text-view.
172  */
173 bool IsExceedingHeight( const Vector3& position, const Size& size, const Size& parentSize )
174 {
175   return ( ( position.y > parentSize.height ) ||
176            ( position.y < size.height ) );
177 }
178
179 /**
180  * Calculates the line length adding the new word or character width.
181  *
182  * It also returns the length of white spaces if they are at the end of the line.
183  *
184  * @param[in] isWhiteSpace Whether the word is a white space.
185  * @param[in] width The width of the character or word.
186  * @param[in] parentWidth The parent width.
187  * @param[out] found Whether the sum of the new character or word is exceding the parent's width.
188  * @param[out] lineLength The length of the portion of line which doesn't exceed the parant's width
189  * @param[out] endWhiteSpaceLength The length of white spaces which are at the end of the line.
190  */
191 void CalculateLineLength( const bool isWhiteSpace, const float width, const float parentWidth, bool& found, float& lineLength, float& endWhiteSpaceLength )
192 {
193   if( lineLength + width > parentWidth )
194   {
195     found = true;
196     lineLength -= endWhiteSpaceLength;
197   }
198   else
199   {
200     lineLength += width;
201
202     if( isWhiteSpace )
203     {
204       endWhiteSpaceLength += width;
205     }
206     else
207     {
208       endWhiteSpaceLength = 0.f;
209     }
210   }
211 }
212
213 struct CurrentTextActorInfo
214 {
215   TextActor textActor;
216   Text text;
217   Vector3 position;
218   Size size;
219   Vector4 color;
220   Vector4 gradientColor;
221   Vector2 startPoint;
222   Vector2 endPoint;
223 };
224
225 void SetVisualParameters( CurrentTextActorInfo& currentTextActorInfo,
226                           const TextView::VisualParameters& visualParameters,
227                           TextView::RelayoutData& relayoutData,
228                           const float lineHeight )
229 {
230   currentTextActorInfo.textActor.SetTextColor( currentTextActorInfo.color );
231   currentTextActorInfo.textActor.SetGradientColor( currentTextActorInfo.gradientColor );
232   currentTextActorInfo.textActor.SetGradientStartPoint( currentTextActorInfo.startPoint );
233   currentTextActorInfo.textActor.SetGradientEndPoint( currentTextActorInfo.endPoint );
234
235   // The italics offset is used in the offscreen rendering. When text is in italics, it may exceed the text-view's boundary
236   // due to the trick used to implement it.
237   const Radian& italicsAngle = currentTextActorInfo.textActor.GetItalicsAngle();
238   const float italicsOffset = lineHeight * std::tan( italicsAngle );
239   relayoutData.mTextLayoutInfo.mMaxItalicsOffset = std::max( relayoutData.mTextLayoutInfo.mMaxItalicsOffset, italicsOffset );
240
241   // Sets the sort modifier value.
242   currentTextActorInfo.textActor.SetSortModifier( visualParameters.mSortModifier );
243
244   // Enables or disables the blending.
245   currentTextActorInfo.textActor.SetBlendMode( !visualParameters.mSnapshotModeEnabled ? BlendingMode::ON : BlendingMode::OFF );
246 }
247
248 void CalculateSubLineLayout( const float parentWidth,
249                              const TextViewProcessor::TextInfoIndices& indices,
250                              const TextViewProcessor::LineLayoutInfo& lineLayoutInfo,
251                              const HorizontalWrapType splitPolicy,
252                              const float shrinkFactor,
253                              SubLineLayoutInfo& subLineInfo )
254 {
255   subLineInfo.mLineLength = 0.f;
256   subLineInfo.mMaxCharHeight = 0.f;
257   subLineInfo.mMaxAscender = 0.f;
258
259   float endWhiteSpaceLength = 0.f;
260
261   std::size_t wordIndex = indices.mWordIndex;
262   std::size_t characterIndex = indices.mCharacterIndex;
263   float lineOffset = 0.f;
264   bool found = false;
265   bool isFirstCharacter = true;
266   for( TextViewProcessor::WordGroupLayoutInfoContainer::const_iterator wordGroupIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex,
267          wordGroupEndIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
268        ( wordGroupIt != wordGroupEndIt ) && !found;
269        ++wordGroupIt )
270   {
271     const TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *wordGroupIt );
272
273     for( TextViewProcessor::WordLayoutInfoContainer::const_iterator wordIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin() + wordIndex,
274            wordEndIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
275          ( wordIt != wordEndIt ) && !found;
276          ++wordIt )
277     {
278       const TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordIt );
279
280       const float shrunkWordWidth = wordLayoutInfo.mSize.width * shrinkFactor;
281       const bool isWhiteSpace = TextViewProcessor::WordSeparator == wordLayoutInfo.mType;
282
283       bool splitByCharacter = false;
284
285       switch( splitPolicy )
286       {
287         case WrapByCharacter:
288         {
289           splitByCharacter = true;
290           break;
291         }
292         case WrapByWord:
293         case WrapByLine: // Fall through
294         {
295           splitByCharacter = false;
296           break;
297         }
298         case WrapByWordAndSplit:
299         {
300           splitByCharacter = ( shrunkWordWidth > parentWidth );
301           break;
302         }
303         case WrapByLineAndSplit:
304         {
305           if( ( 0 != characterIndex ) ||
306               ( ( 0 == characterIndex ) && ( lineOffset + shrunkWordWidth > parentWidth ) ) )
307           {
308             splitByCharacter = true;
309           }
310           else
311           {
312             lineOffset += shrunkWordWidth;
313             splitByCharacter = false;
314           }
315         }
316       }
317
318       if( splitByCharacter )
319       {
320         for( TextViewProcessor::CharacterLayoutInfoContainer::const_iterator charIt = wordLayoutInfo.mCharactersLayoutInfo.begin() + characterIndex,
321                charEndIt = wordLayoutInfo.mCharactersLayoutInfo.end();
322              ( charIt != charEndIt ) && !found;
323              ++charIt )
324         {
325           const TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *charIt );
326           CalculateLineLength( isWhiteSpace, characterLayoutInfo.mSize.width * shrinkFactor, parentWidth, found, subLineInfo.mLineLength, endWhiteSpaceLength );
327           if( !found || isFirstCharacter )
328           {
329             subLineInfo.mMaxCharHeight = std::max( subLineInfo.mMaxCharHeight, characterLayoutInfo.mSize.height );
330             subLineInfo.mMaxAscender = std::max( subLineInfo.mMaxAscender, characterLayoutInfo.mAscender );
331           }
332
333           // All characters for word 'wordIndex' have been processed.
334           // Next word need to process all characters, so the characterIndex is reset to 0.
335           characterIndex = 0;
336           isFirstCharacter = false;
337         }
338
339         lineOffset += subLineInfo.mLineLength;
340       }
341       else
342       {
343         CalculateLineLength( isWhiteSpace, shrunkWordWidth, parentWidth, found, subLineInfo.mLineLength, endWhiteSpaceLength );
344         if( !found || isFirstCharacter )
345         {
346           subLineInfo.mMaxCharHeight = std::max( subLineInfo.mMaxCharHeight, wordLayoutInfo.mSize.height );
347           subLineInfo.mMaxAscender = std::max( subLineInfo.mMaxAscender, wordLayoutInfo.mAscender );
348         }
349         isFirstCharacter = false;
350       }
351     }
352
353     // All words for group 'groupIndex' have been processed.
354     // Next group need to process all words, so the wordIndex is reset to 0.
355     wordIndex = 0;
356   }
357
358   subLineInfo.mMaxCharHeight *= shrinkFactor;
359   subLineInfo.mMaxAscender *= shrinkFactor;
360 }
361
362 float CalculateXoffset( const Toolkit::Alignment::Type horizontalTextAlignment, const float parentWidth, const float wholeTextWidth )
363 {
364   float xOffset( 0.f );
365   switch( horizontalTextAlignment )
366   {
367     case Toolkit::Alignment::HorizontalLeft:
368     {
369       // nothing to do.
370       break;
371     }
372     case Toolkit::Alignment::HorizontalCenter:
373     {
374       xOffset = 0.5f * ( parentWidth - wholeTextWidth );
375       break;
376     }
377     case Toolkit::Alignment::HorizontalRight:
378     {
379       xOffset = parentWidth - wholeTextWidth;
380       break;
381     }
382     default:
383     {
384       DALI_ASSERT_ALWAYS( !"TextViewRelayout::CalculateXoffset: Wrong horizontal text alignment. Did you set a vertical one?" );
385     }
386   }
387
388   return xOffset;
389 }
390
391 float CalculateYoffset( const Toolkit::Alignment::Type verticalTextAlignment, const float parentHeight, const float wholeTextHeight )
392 {
393   float yOffset( 0.f );
394   switch( verticalTextAlignment )
395   {
396     case Toolkit::Alignment::VerticalTop:
397     {
398       // nothing to do.
399       break;
400     }
401     case Toolkit::Alignment::VerticalCenter:
402     {
403       yOffset = 0.5f * ( parentHeight - wholeTextHeight );
404       break;
405     }
406     case Toolkit::Alignment::VerticalBottom:
407     {
408       yOffset = parentHeight - wholeTextHeight;
409       break;
410     }
411     default:
412     {
413       DALI_ASSERT_ALWAYS( !"TextViewRelayout::CalculateXoffset: Wrong vertical text alignment. Did you set an horizontal one?" );
414     }
415   }
416
417   return yOffset;
418 }
419
420 float CalculateJustificationOffset( const Toolkit::TextView::LineJustification justification, const float wholeTextWidth, const float lineLength )
421 {
422   float offset = 0.f;
423   switch( justification )
424   {
425     case Toolkit::TextView::Left:
426     {
427       offset = 0.f;
428       break;
429     }
430     case Toolkit::TextView::Center:
431     {
432       offset = 0.5f * ( wholeTextWidth - lineLength );
433       break;
434     }
435     case Toolkit::TextView::Right:
436     {
437       offset = wholeTextWidth - lineLength;
438       break;
439     }
440     case Toolkit::TextView::Justified:
441     {
442       offset = 0.f;
443       break;
444     }
445   }
446
447   return offset;
448 }
449
450 bool IsVisible( const Vector3& position, const Size& size, const Size& parentSize, const VisibilityTestType type )
451 {
452   bool visible = false;
453
454   switch( type )
455   {
456     case FULLY_VISIBLE:
457     {
458       // Whether the text-actor is fully inside the boundaries of the text-view.
459       visible = ( ( position.x >= 0.f ) && ( position.x + size.width <= parentSize.width ) &&
460                   ( position.y >= size.height ) && ( position.y <= parentSize.height ) );
461       break;
462     }
463     case FULLY_VISIBLE_WIDTH:
464     {
465       // Whether the text-actor is between the left and right boundaries of the text-view.
466       visible = ( ( position.x >= 0.f ) && ( position.x + size.width <= parentSize.width ) );
467       break;
468     }
469     case FULLY_VISIBLE_HEIGHT:
470     {
471       // Whether the text-actor is between the top and bottom boundaries of the text-view.
472       visible = ( ( position.y >= size.height ) && ( position.y <= parentSize.height ) );
473       break;
474     }
475     case PARTIALLY_VISIBLE:
476     {
477       // Whether the text-actor is partially inside the boundaries of the text-view.
478       visible = ( ( position.x < parentSize.width ) &&
479                   ( position.x + size.width > 0.f ) &&
480                   ( position.y > 0.f ) &&
481                   ( position.y - size.height < parentSize.height ) );
482       break;
483     }
484     case PARTIALLY_VISIBLE_WIDTH:
485     {
486       // Whether the text-actor is partially inside the area defined by the left and the right boundaries of the text-view.
487       // It may not be partially inside the text-view.
488       visible = ( ( position.x < parentSize.width ) &&
489                   ( position.x + size.width > 0.f ) );
490       break;
491     }
492     case PARTIALLY_VISIBLE_HEIGHT:
493     {
494       // Whether the text-actor is partially inside the area defined by the top and the bottom boundaries of the text-view.
495       // It may not be partially inside the text-view.
496       visible = ( ( position.y > 0.f ) &&
497                   ( position.y - size.height < parentSize.height ) );
498       break;
499     }
500   }
501
502   return visible;
503 }
504
505 Vector2 CalculateRectParameters( const Vector2& p0, const Vector2& p1 )
506 {
507   const float gradient = ( p1.y - p0.y ) / ( p1.x - p0.x );
508
509   return Vector2( gradient, p0.y - gradient * p0.x );
510 }
511
512 void UpdateAlignment( const TextView::LayoutParameters& layoutParameters,
513                       TextView::RelayoutData& relayoutData )
514 {
515   // Calculates an offset to align the whole text within the text-view's boundary accordingly with the set alignment and justification options.
516   // The offset could be negative if the whole text is bigger than the boundary of the text-view.
517
518   // If the exceed policy is ellipsize at the end, negative offsets are not wanted.
519   // In that case, it will align the line to the left and/or top, and ellipsize the end.
520   const bool ellipsizeAlignToLeft = ( layoutParameters.mExceedPolicy == TextView::EllipsizeEndOriginal ) ||
521                                     ( layoutParameters.mExceedPolicy == TextView::EllipsizeEnd ) ||
522                                     ( layoutParameters.mExceedPolicy == TextView::SplitEllipsizeEnd );
523   const bool ellipsizeAlignToTop = ( layoutParameters.mExceedPolicy == TextView::EllipsizeEnd ) ||
524                                    ( layoutParameters.mExceedPolicy == TextView::SplitEllipsizeEnd );
525
526   RelayoutParameters relayoutParameters;
527
528   // Calculates the vertical and horizontal offsets.
529   const float textHorizontalOffset = CalculateXoffset( layoutParameters.mHorizontalAlignment, relayoutData.mTextViewSize.width, relayoutData.mTextSizeForRelayoutOption.width );
530   const float textVerticalOffset = CalculateYoffset( layoutParameters.mVerticalAlignment, relayoutData.mTextViewSize.height, relayoutData.mTextSizeForRelayoutOption.height );
531
532   std::size_t lineJustificationIndex = 0; // Index to the first position of the vector which stores all line justification info.
533   std::size_t infoTableCharacterIndex = 0;
534
535   relayoutParameters.mIndices.mLineIndex = 0;
536
537   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(),
538          endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
539        lineLayoutIt != endLineLayoutIt;
540        ++lineLayoutIt, ++relayoutParameters.mIndices.mLineIndex )
541   {
542     TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt );
543
544     relayoutParameters.mIndices.mGroupIndex = 0;
545     float justificationOffset = 0.f;
546
547     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(),
548            endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
549          groupLayoutIt != endGroupLayoutIt;
550          ++groupLayoutIt, ++relayoutParameters.mIndices.mGroupIndex )
551     {
552       TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt );
553
554       relayoutParameters.mIndices.mWordIndex = 0;
555
556       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(),
557              endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
558            wordLayoutIt != endWordLayoutIt;
559            ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex )
560       {
561         TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt );
562
563         relayoutParameters.mIndices.mCharacterIndex = 0;
564
565         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(),
566                endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end();
567              characterLayoutIt != endCharacterLayoutIt;
568              ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex, ++infoTableCharacterIndex )
569         {
570           TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt );
571
572           // Calculate line justification offset.
573           if( lineJustificationIndex < relayoutData.mLineJustificationInfo.size() )
574           {
575             const TextView::LineJustificationInfo lineJustificationInfo( *( relayoutData.mLineJustificationInfo.begin() + lineJustificationIndex ) );
576
577             if( relayoutParameters.mIndices == lineJustificationInfo.mIndices )
578             {
579               justificationOffset = CalculateJustificationOffset( layoutParameters.mLineJustification, relayoutData.mTextSizeForRelayoutOption.width, lineJustificationInfo.mLineLength );
580               ++lineJustificationIndex; // increase the index to point the next position in the vector.
581             }
582           }
583
584           // Deletes the offsets if the exceed policies are EllipsizeEnd.
585           const float horizontalOffset = textHorizontalOffset + justificationOffset;
586           characterLayoutInfo.mOffset.x = ( ellipsizeAlignToLeft && ( horizontalOffset < 0.f ) ) ? 0.f : horizontalOffset;
587           characterLayoutInfo.mOffset.y = ( ellipsizeAlignToTop && ( textVerticalOffset < 0.f ) ) ? 0.f : textVerticalOffset;
588
589           // Updates the size and position table for text-input with the alignment offset.
590           Vector3 positionOffset( characterLayoutInfo.mPosition );
591
592           std::vector<Toolkit::TextView::CharacterLayoutInfo>::iterator infoTableIt = relayoutData.mCharacterLayoutInfoTable.begin() + infoTableCharacterIndex;
593           Toolkit::TextView::CharacterLayoutInfo& characterTableInfo( *infoTableIt );
594
595           characterTableInfo.mPosition.x = positionOffset.x + characterLayoutInfo.mOffset.x;
596           characterTableInfo.mPosition.y = positionOffset.y + characterLayoutInfo.mOffset.y;
597
598           positionOffset.x += characterLayoutInfo.mAdvance * relayoutData.mShrinkFactor;
599         } // end characters
600       } // end words
601     } // end group of words
602   } // end lines
603 }
604
605 void CalculateBearing( TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo,
606                        TextView::RelayoutData& relayoutData )
607 {
608   // No bearing used.
609   //
610   //            gggggggggg
611   //            gggggggggg
612   //          gggg      gggg
613   //          gggg      gggg
614   //          gggg      gggg
615   //          gggg      gggg
616   //          gggg      gggg
617   //          gggg      gggg
618   //  ggggg     gggggggggg        bb         ggggg
619   // gg   gg    gggggggggg        bb        gg   gg
620   // gg   gg            gggg      bb        gg   gg
621   // gg   gg            gggg      bb        gg   gg
622   //  ggggg   gg        gggg      bbbbbbb    ggggg
623   //      gg  gg        gggg      bb    bb       gg
624   // g    gg    gggggggggg        bb    bb  g    gg
625   //  ggggg     gggggggggg        bbbbbbb    ggggg
626   //
627   // Bearing used.
628   //
629   //            gggggggggg
630   //            gggggggggg
631   //          gggg      gggg      bb
632   //          gggg      gggg      bb
633   //          gggg      gggg      bb
634   //  ggggg   gggg      gggg      bb         ggggg
635   // gg   gg  gggg      gggg      bbbbbbb   gg   gg
636   // gg   gg  gggg      gggg      bb    bb  gg   gg
637   // gg   gg    gggggggggg        bb    bb  gg   gg
638   //  ggggg     gggggggggg        bbbbbbb    ggggg
639   //      gg            gggg                     gg
640   // g    gg            gggg                g    gg
641   //  ggggg   gg        gggg                 ggggg
642   //          gg        gggg
643   //            gggggggggg
644   //            gggggggggg
645
646   const Toolkit::TextView::LineLayoutInfo& lineInfo( *( relayoutData.mLines.end() - 1 ) );
647   const float bearingOffset = ( lineInfo.mSize.height - lineInfo.mAscender ) - ( characterLayoutInfo.mSize.height - characterLayoutInfo.mAscender );
648
649   characterLayoutInfo.mPosition.y -= bearingOffset * relayoutData.mShrinkFactor;
650 }
651
652 void UpdateLayoutInfoTable( Vector4& minMaxXY,
653                             TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo,
654                             TextViewProcessor::WordLayoutInfo& wordLayoutInfo,
655                             TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo,
656                             RelayoutParameters& relayoutParameters,
657                             TextView::RelayoutData& relayoutData )
658 {
659   // updates min and max position to calculate the text size for multiline policies.
660   minMaxXY.x = std::min( minMaxXY.x, characterLayoutInfo.mPosition.x );
661   minMaxXY.z = std::max( minMaxXY.z, characterLayoutInfo.mPosition.x + characterLayoutInfo.mSize.width * relayoutData.mShrinkFactor );
662
663   minMaxXY.y = std::min( minMaxXY.y, characterLayoutInfo.mPosition.y - characterLayoutInfo.mSize.height * relayoutData.mShrinkFactor );
664   minMaxXY.w = std::max( minMaxXY.w, characterLayoutInfo.mPosition.y   );
665
666   // Adds layout info to be retrieved by external controls or applications.
667   Vector3 positionOffset( characterLayoutInfo.mPosition );
668
669   const float descender = characterLayoutInfo.mSize.height - characterLayoutInfo.mAscender;
670
671   const Toolkit::TextView::CharacterLayoutInfo characterLayoutTableInfo( Size( characterLayoutInfo.mAdvance * relayoutData.mShrinkFactor,
672                                                                                characterLayoutInfo.mHeight * relayoutData.mShrinkFactor ),
673                                                                          positionOffset,
674                                                                          ( TextViewProcessor::LineSeparator == wordLayoutInfo.mType ),
675                                                                          ( TextViewProcessor::RTL == wordGroupLayoutInfo.mDirection ),
676                                                                          true,
677                                                                          descender );
678
679   relayoutData.mCharacterLayoutInfoTable.push_back( characterLayoutTableInfo );
680
681   positionOffset.x += characterLayoutInfo.mAdvance * relayoutData.mShrinkFactor;
682 }
683
684 void CalculateVisibilityForFade( const Internal::TextView::LayoutParameters& layoutParameters,
685                                  TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo,
686                                  RelayoutParameters& relayoutParameters,
687                                  FadeParameters& fadeParameters,
688                                  TextView::RelayoutData& relayoutData )
689 {
690   if( ( TextView::Fade != layoutParameters.mExceedPolicy ) &&
691       ( TextView::SplitFade != layoutParameters.mExceedPolicy ) &&
692       ( TextView::FadeOriginal != layoutParameters.mExceedPolicy ) &&
693       ( TextView::OriginalFade != layoutParameters.mExceedPolicy ) )
694   {
695     // nothing to fade
696     return;
697   }
698
699   // Calculates visibility of a text-actor according the exceed policies.
700
701   // position + alignment offset.
702   const Vector3 position( characterLayoutInfo.mPosition.x + characterLayoutInfo.mOffset.x,
703                           characterLayoutInfo.mPosition.y + characterLayoutInfo.mOffset.y,
704                           characterLayoutInfo.mPosition.z );
705
706   // Whether the text actor is fully, partially or non visible (according exceed policies).
707   switch( layoutParameters.mExceedPolicy )
708   {
709     case TextView::Fade:
710     {
711       // All text-actors which are not completely inside the text-view's boundaries are set as non visible.
712       // All text-actors which are partially inside the text-view's boundaries are set as partially visible.
713       if( !IsVisible( position,
714                       characterLayoutInfo.mSize,
715                       relayoutData.mTextViewSize,
716                       FULLY_VISIBLE ) )
717       {
718         relayoutParameters.mIsVisible = false;
719         if( IsVisible( position,
720                        characterLayoutInfo.mSize,
721                        relayoutData.mTextViewSize,
722                        PARTIALLY_VISIBLE ) )
723         {
724           fadeParameters.mIsPartiallyVisible = true;
725
726           // Checks if a text-actor is exceeding more than one boundary as this case is not supported.
727           if( IsExceedingWidth( position,
728                                 characterLayoutInfo.mSize,
729                                 relayoutData.mTextViewSize ) &&
730               IsExceedingHeight( position,
731                                  characterLayoutInfo.mSize,
732                                  relayoutData.mTextViewSize ) )
733           {
734             // Combination not fully supported by text-view.
735             // Need to check if text-actor really supports this combination.
736             fadeParameters.mIsPartiallyVisible = false;
737           }
738         }
739       }
740       break;
741     }
742     case TextView::FadeOriginal:
743     {
744       // All text-actors which are not completely between the left and right text-view's boundaries are set as non visible.
745       // All text-actors which are partially inside the text-view's boundaries are set as partially visible.
746       if( !IsVisible( position,
747                       characterLayoutInfo.mSize,
748                       relayoutData.mTextViewSize,
749                       FULLY_VISIBLE_WIDTH ) )
750       {
751         relayoutParameters.mIsVisible = false;
752         if( IsVisible( position,
753                        characterLayoutInfo.mSize,
754                        relayoutData.mTextViewSize,
755                        PARTIALLY_VISIBLE_WIDTH ) )
756         {
757           fadeParameters.mIsPartiallyVisible = true;
758         }
759       }
760       break;
761     }
762     case TextView::OriginalFade:
763     case TextView::SplitFade: // Fallthrough
764     {
765       // All text-actors which are not completely between the top and bottom text-view's boundaries are set as non visible.
766       // All text-actors which are partially inside the text-view's boundaries are set as partially visible.
767       if( !IsVisible( position,
768                       characterLayoutInfo.mSize,
769                       relayoutData.mTextViewSize,
770                       FULLY_VISIBLE_HEIGHT ) )
771       {
772         relayoutParameters.mIsVisible = false;
773         if( IsVisible( position,
774                        characterLayoutInfo.mSize,
775                        relayoutData.mTextViewSize,
776                        PARTIALLY_VISIBLE_HEIGHT ) )
777         {
778           fadeParameters.mIsPartiallyVisible = true;
779         }
780       }
781       break;
782     }
783     default:
784     {
785       DALI_ASSERT_ALWAYS( !"TextViewRelayout::CalculateVisibilityForFade. Wrong exceed policies." )
786       break;
787     }
788   }
789
790   if( relayoutParameters.mIsVisible || fadeParameters.mIsPartiallyVisible )
791   {
792     characterLayoutInfo.mIsVisible = true;
793
794     const Size size = characterLayoutInfo.mSize * relayoutData.mShrinkFactor;
795     const float characterPositionPlusWidth = position.x + size.width;
796     const float characterPositionMinusHeight = position.y - size.height;
797
798     // Calculates which edges need to be faded-out.
799     bool rightFadeOut = false;
800     bool leftFadeOut = false;
801     bool bottomFadeOut = false;
802     bool topFadeOut = false;
803
804     switch( layoutParameters.mExceedPolicy )
805     {
806       case TextView::Fade:
807       {
808         // All text-actors exceeding any of the boundaries will be faded-out.
809         rightFadeOut = ( characterPositionPlusWidth > fadeParameters.mRightFadeThreshold );
810         leftFadeOut = ( position.x < fadeParameters.mLeftFadeThreshold );
811         bottomFadeOut = ( position.y > fadeParameters.mBottomFadeThreshold );
812         topFadeOut = ( characterPositionMinusHeight < fadeParameters.mTopFadeThreshold );
813         break;
814       }
815       case TextView::FadeOriginal:
816       {
817         // Only text-actors exceeding the left or the right boundaries will be faded-out.
818         rightFadeOut = ( characterPositionPlusWidth > fadeParameters.mRightFadeThreshold );
819         leftFadeOut = ( position.x < fadeParameters.mLeftFadeThreshold );
820         break;
821       }
822       case TextView::SplitFade:
823       case TextView::OriginalFade: //Fallthrough
824       {
825         // Only text-actors exceeding the top or the bottom boundaries will be faded-out.
826         bottomFadeOut = ( position.y > fadeParameters.mBottomFadeThreshold );
827         topFadeOut = ( characterPositionMinusHeight < fadeParameters.mTopFadeThreshold );
828         break;
829       }
830       default:
831       {
832         DALI_ASSERT_ALWAYS( !"TextViewRelayout::CalculateVisibilityForFade. Wrong exceed policies." );
833         break;
834       }
835     }
836
837     // Calculates gradient parameters for a text-actor.
838     Vector4 gradientColor = Vector4::ZERO;
839     Vector2 startPoint = Vector2::ZERO;
840     Vector2 endPoint = Vector2::ZERO;
841
842     if( !( rightFadeOut && leftFadeOut ) )
843     {
844       // Current implementation can't set gradient parameters for a text-actor exceeding at the same time the left and the right boundaries.
845       if( rightFadeOut )
846       {
847         gradientColor = characterLayoutInfo.mStyledText.mStyle.GetTextColor();
848
849         // Calculates gradient coeficients.
850         characterLayoutInfo.mColorAlpha = gradientColor.a * std::min( 1.f, fadeParameters.mRightAlphaCoeficients.x * position.x + fadeParameters.mRightAlphaCoeficients.y );
851         gradientColor.a *= std::max( 0.f, fadeParameters.mRightAlphaCoeficients.x * characterPositionPlusWidth + fadeParameters.mRightAlphaCoeficients.y );
852
853         startPoint = Vector2( std::max( 0.f, ( fadeParameters.mRightFadeThresholdOffset - position.x ) / size.width ), 0.5f );
854         endPoint = Vector2( std::min( 1.f, ( relayoutData.mTextViewSize.width - position.x ) / size.width ), 0.5f );
855       }
856       else if( leftFadeOut )
857       {
858         gradientColor = characterLayoutInfo.mStyledText.mStyle.GetTextColor();
859
860         // Calculates gradient coeficients.
861         characterLayoutInfo.mColorAlpha = std::min( 1.f, fadeParameters.mLeftAlphaCoeficients.x * characterPositionPlusWidth + fadeParameters.mLeftAlphaCoeficients.y );
862         gradientColor.a *= gradientColor.a * std::max( 0.f, fadeParameters.mLeftAlphaCoeficients.x * position.x + fadeParameters.mLeftAlphaCoeficients.y );
863
864         startPoint = Vector2( std::max( 0.f, ( fadeParameters.mLeftFadeThresholdOffset - position.x ) / size.width ), 0.5f );
865         endPoint = Vector2( std::min( 1.f, -position.x / size.width ), 0.5f );
866       }
867     }
868
869     if( !( bottomFadeOut && topFadeOut ) )
870     {
871       // Current implementation can't set gradient parameters for a text-actor exceeding at the same time the top and the bottom boundaries.
872       if( bottomFadeOut )
873       {
874         gradientColor = characterLayoutInfo.mStyledText.mStyle.GetTextColor();
875
876         // Calculates gradient coeficients.
877         characterLayoutInfo.mColorAlpha = gradientColor.a * std::min( 1.f, fadeParameters.mBottomAlphaCoeficients.x * characterPositionMinusHeight + fadeParameters.mBottomAlphaCoeficients.y );
878         gradientColor.a *= std::max( 0.f, fadeParameters.mBottomAlphaCoeficients.x * position.y + fadeParameters.mBottomAlphaCoeficients.y );
879
880         startPoint = Vector2( 0.5f, std::max( 0.f, ( fadeParameters.mBottomFadeThresholdOffset - characterPositionMinusHeight ) / size.height ) );
881         endPoint = Vector2( 0.5f, std::min( 1.f, ( relayoutData.mTextViewSize.height - characterPositionMinusHeight ) / size.height ) );
882       }
883       else if( topFadeOut )
884       {
885         gradientColor = characterLayoutInfo.mStyledText.mStyle.GetTextColor();
886
887         // Calculates gradient coeficients.
888         characterLayoutInfo.mColorAlpha *= gradientColor.a * std::min( 1.f, fadeParameters.mTopAlphaCoeficients.x * position.y + fadeParameters.mTopAlphaCoeficients.y );
889         gradientColor.a *= std::max( 0.f, fadeParameters.mTopAlphaCoeficients.x * characterPositionMinusHeight + fadeParameters.mTopAlphaCoeficients.y );
890
891         startPoint = Vector2( 0.5f, std::max( 0.f, ( fadeParameters.mTopFadeThresholdOffset - characterPositionMinusHeight ) / size.height ) );
892         endPoint = Vector2( 0.5f, std::min( 1.f,  -characterPositionMinusHeight / size.height ) );
893       }
894     }
895
896     characterLayoutInfo.mGradientColor = gradientColor;
897     characterLayoutInfo.mStartPoint = startPoint;
898     characterLayoutInfo.mEndPoint = endPoint;
899   }
900   else
901   {
902     characterLayoutInfo.mIsVisible = false;
903   }
904 }
905
906 bool CalculateVisibilityForEllipsizeEndOriginal( TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo,
907                                                  const EllipsizeParameters& ellipsizeParameters )
908 {
909   bool isPartiallyVisible = false;
910
911   if( !IsVisible( ellipsizeParameters.mPosition,
912                   characterLayoutInfo.mSize,
913                   ellipsizeParameters.mEllipsizeBoundary,
914                   FULLY_VISIBLE_WIDTH ) )
915   {
916     // The character doesn't fit in the text-view's width.
917     characterLayoutInfo.mIsVisible = false;
918
919     // Checks if the character is partially visible (it's cut by the boundary)
920     isPartiallyVisible = IsVisible( ellipsizeParameters.mPosition,
921                                     characterLayoutInfo.mSize,
922                                     ellipsizeParameters.mEllipsizeBoundary,
923                                     PARTIALLY_VISIBLE_WIDTH );
924   }
925   else
926   {
927     // The character fits in the text-view's width. Set it to visible.
928     characterLayoutInfo.mIsVisible = true;
929   }
930
931   return isPartiallyVisible;
932 }
933
934 bool CalculateVisibilityForEllipsizeEnd( TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo,
935                                          const EllipsizeParameters& ellipsizeParameters )
936 {
937   bool isPartiallyVisible = false;
938
939   if( !IsVisible( ellipsizeParameters.mPosition,
940                   characterLayoutInfo.mSize,
941                   ellipsizeParameters.mEllipsizeBoundary,
942                   FULLY_VISIBLE ) )
943   {
944     // The character is not fully visible. Needs to check if it's partially visible.
945     characterLayoutInfo.mIsVisible = false;
946
947     // Checks if the character doesn't cut the bottom edge of the text-view.
948     const bool fullyVisibleHeight = IsVisible( ellipsizeParameters.mPosition,
949                                                characterLayoutInfo.mSize,
950                                                ellipsizeParameters.mEllipsizeBoundary,
951                                                FULLY_VISIBLE_HEIGHT );
952
953     // Checks if the character cuts the right edge of the text-view.
954     const bool partiallyVisibleWidth = IsVisible( ellipsizeParameters.mPosition,
955                                                   characterLayoutInfo.mSize,
956                                                   ellipsizeParameters.mEllipsizeBoundary,
957                                                   PARTIALLY_VISIBLE_WIDTH );
958
959     // Character will be ellipsized if it cuts the right edge of the text-view but fits completely in the text-view's height.
960     isPartiallyVisible = ( fullyVisibleHeight && partiallyVisibleWidth );
961   }
962   else
963   {
964     // The character fits in the boundary of the text-view. Set it to visible.
965     characterLayoutInfo.mIsVisible = true;
966   }
967
968   return isPartiallyVisible;
969 }
970
971 void CalculateVisibilityForEllipsize( const Internal::TextView::LayoutParameters& layoutParameters,
972                                       TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo,
973                                       EllipsizeParameters& ellipsizeParameters,
974                                       TextView::RelayoutData& relayoutData )
975 {
976   // Calculates visibility for EllipsizeEnd exceed policies.
977
978   // It defines a boundary on the right side of the text-view by substracting the ellipsize-text's size (...) to the text-view's size.
979   // If a character is cut by this boundary and the whole line (if the multi-line policy is split-by-new-line-char)
980   // or the whole word (if the multi-line policy is split-by-word) doesn't fit in the text-view's width, then it's replaced by the ellipsize-text.
981
982   // Position of the character used to do the visibility test.
983   ellipsizeParameters.mPosition = Vector3( characterLayoutInfo.mPosition.x + characterLayoutInfo.mOffset.x,
984                                            characterLayoutInfo.mPosition.y + characterLayoutInfo.mOffset.y,
985                                            characterLayoutInfo.mPosition.z );
986
987   // Text will be ellipsized if a character is partially visible (it's cut by the boundary defined in the right side of the text-view).
988   bool isPartiallyVisible = false;
989
990   // Checks if the whole line or the whole word fits in the text-view's width accordingly with the multiline policy.
991   const bool fitsInWidth = ( Toolkit::TextView::SplitByNewLineChar == layoutParameters.mMultilinePolicy ) ? ellipsizeParameters.mLineFits: ellipsizeParameters.mWordFits;
992
993   // Will only ellipsize the text if it cuts the right vertical edge and it doesn't fit in the text-view's width.
994   if( fitsInWidth )
995   {
996     // The line or word fits completely inside the text-view's width. Nothing else to do.
997     characterLayoutInfo.mIsVisible = true;
998   }
999   else
1000   {
1001     // The line or word doesn't fit in the text-view's width.
1002
1003     // Calculates visibility for each type of ellipsize policies.
1004     switch( layoutParameters.mExceedPolicy )
1005     {
1006       case TextView::EllipsizeEndOriginal:
1007       {
1008         // Ellipsizes the text if it doesn't fit in the width but it doesn't ellipsize if the text doesn't fit in the height.
1009
1010         isPartiallyVisible = CalculateVisibilityForEllipsizeEndOriginal( characterLayoutInfo,
1011                                                                          ellipsizeParameters );
1012
1013         break;
1014       }
1015       case TextView::SplitEllipsizeEnd:
1016       case TextView::EllipsizeEnd:
1017       {
1018         // Ellipsizes the text if it doesn't fit in the width and fully fits in the text-view's height.
1019
1020         isPartiallyVisible = CalculateVisibilityForEllipsizeEnd( characterLayoutInfo,
1021                                                                  ellipsizeParameters );
1022
1023         break;
1024       }
1025       default:
1026       {
1027         DALI_ASSERT_DEBUG( !"TextViewRelayout::CalculateVisibilityForEllipsize. Wrong exceed value." );
1028         break;
1029       }
1030     }
1031   }
1032
1033   // If the current character is not fully visible but is partially visible, it is cut by the boundary of the text-view.
1034   // In that case, the charater needs to be replaced by the ellipsize text.
1035   ellipsizeParameters.mCreateEllipsizedTextActors = ( !characterLayoutInfo.mIsVisible && isPartiallyVisible );
1036 }
1037
1038 void CreateEllipsizeTextActor( const EllipsizeParameters& ellipsizeParameters,
1039                                TextView::RelayoutData& relayoutData )
1040 {
1041   // The default ellipsize text is '...' and all dots have the same style. However, a differernt ellipsize text could be set and it can have characters with differernt styles.
1042   // The code bellow creates the text-actors needed for the ellipsize text.
1043
1044   // Set ellipsize's position by the end of visible text.
1045   Vector3 ellipsizePosition = ellipsizeParameters.mPosition;
1046   // Stores current ellipsize text.
1047   Text ellipsizeText;
1048   // Stores current ellipsize style.
1049   TextStyle ellipsizeStyle;
1050   // Stores the current size.
1051   Size ellipsizeSize;
1052   //Whether current glyph is an emoticon.
1053   bool isColorGlyph = false;
1054
1055   float bearingOffset = 0.f;
1056
1057   // Create ellipsize text-actor.
1058   for( TextViewProcessor::CharacterLayoutInfoContainer::const_iterator ellipsizeCharacterLayoutIt = relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo.mCharactersLayoutInfo.begin(),
1059          endEllipsizeCharacterLayoutIt = relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo.mCharactersLayoutInfo.end();
1060        ellipsizeCharacterLayoutIt != endEllipsizeCharacterLayoutIt;
1061        ++ellipsizeCharacterLayoutIt )
1062   {
1063     const TextViewProcessor::CharacterLayoutInfo& ellipsizeCharacterLayoutInfo( *ellipsizeCharacterLayoutIt );
1064
1065     if( isColorGlyph ||
1066         ( isColorGlyph != ellipsizeCharacterLayoutInfo.mIsColorGlyph ) ||
1067         ( ellipsizeStyle != ellipsizeCharacterLayoutInfo.mStyledText.mStyle ) )
1068     {
1069       // The style is different, so a new text-actor is needed.
1070       if( !ellipsizeText.IsEmpty() )
1071       {
1072         // It only creates a text-actor if there is any text.
1073         RenderableActor ellipsizeGlyphActor = CreateGlyphActor( ellipsizeText, ellipsizeStyle, relayoutData.mTextActorCache );
1074         ellipsizeGlyphActor.SetSize( ellipsizeSize );
1075         ellipsizeGlyphActor.SetPosition( Vector3( ellipsizePosition.x, ellipsizePosition.y - bearingOffset, ellipsizePosition.z ) );
1076
1077         // Updates the position for the next text-actor.
1078         ellipsizePosition.x += ellipsizeSize.width;
1079
1080         // Adds the text-actor to the list.
1081         relayoutData.mEllipsizedGlyphActors.push_back( ellipsizeGlyphActor );
1082       }
1083
1084       // Resets the current ellipsize info.
1085       ellipsizeText = ellipsizeCharacterLayoutInfo.mStyledText.mText;
1086       ellipsizeStyle = ellipsizeCharacterLayoutInfo.mStyledText.mStyle;
1087       ellipsizeSize = ellipsizeCharacterLayoutInfo.mSize;
1088       isColorGlyph = ellipsizeCharacterLayoutInfo.mIsColorGlyph;
1089
1090       bearingOffset = ( ellipsizeParameters.mLineDescender - ( ellipsizeCharacterLayoutInfo.mSize.height - ellipsizeCharacterLayoutInfo.mAscender ) ) * relayoutData.mShrinkFactor;
1091     }
1092     else
1093     {
1094       // Updates text and size with the new character.
1095       ellipsizeText.Append( ellipsizeCharacterLayoutInfo.mStyledText.mText );
1096       TextViewProcessor::UpdateSize( ellipsizeSize, ellipsizeCharacterLayoutInfo.mSize );
1097     }
1098
1099   }
1100
1101   if( !ellipsizeText.IsEmpty() )
1102   {
1103     // Creates the last glyph-actor.
1104     RenderableActor ellipsizeGlyphActor = CreateGlyphActor( ellipsizeText, ellipsizeStyle, relayoutData.mTextActorCache );
1105     ellipsizeGlyphActor.SetSize( ellipsizeSize );
1106     ellipsizeGlyphActor.SetPosition( Vector3( ellipsizePosition.x, ellipsizePosition.y - bearingOffset, ellipsizePosition.z ) );
1107
1108     // Adds the glyph-actor to the list.
1109     relayoutData.mEllipsizedGlyphActors.push_back( ellipsizeGlyphActor );
1110   }
1111 }
1112
1113 void EllipsizeLine( const TextView::LayoutParameters& layoutParameters,
1114                     EllipsizeParameters& ellipsizeParameters,
1115                     TextView::RelayoutData& relayoutData )
1116 {
1117   // Traverses the text layout info from the first character of the laid out line
1118   // to the last one setting to each character its visibility. If needed, it adds the ellipsize text (...).
1119
1120   // Indices to the first character of the laid out line.
1121   TextViewProcessor::TextInfoIndices firstIndices;
1122   TextViewProcessor::GetIndicesFromGlobalCharacterIndex( ellipsizeParameters.mFirstIndex,
1123                                                          relayoutData.mTextLayoutInfo,
1124                                                          firstIndices );
1125
1126   // Indices to the last character of the laid out line.
1127   TextViewProcessor::TextInfoIndices lastIndices;
1128   TextViewProcessor::GetIndicesFromGlobalCharacterIndex( ellipsizeParameters.mLastIndex,
1129                                                          relayoutData.mTextLayoutInfo,
1130                                                          lastIndices );
1131
1132   // Defines a boundary by substracting the ellipsize-text's width to the text-view's width.
1133   // This is the boundary used to check if a character have to be ellipsized.
1134   ellipsizeParameters.mEllipsizeBoundary = relayoutData.mTextViewSize;
1135   ellipsizeParameters.mEllipsizeBoundary.width -= relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo.mSize.width;
1136
1137   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + firstIndices.mLineIndex,
1138          endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + lastIndices.mLineIndex + 1;
1139        lineLayoutIt != endLineLayoutIt;
1140        ++lineLayoutIt )
1141   {
1142     TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt );
1143
1144     ellipsizeParameters.mLineFits = ellipsizeParameters.mIsLineWidthFullyVisible && ellipsizeParameters.mIsLineHeightFullyVisible && ellipsizeParameters.mIsNextLineFullyVisibleHeight;
1145
1146     if( !ellipsizeParameters.mIsNextLineFullyVisibleHeight )
1147     {
1148       ellipsizeParameters.mEllipsizeBoundary.width = ellipsizeParameters.mLineWidth;
1149     }
1150
1151     bool firstGroup = true;
1152     bool lastGroup = false;
1153     std::size_t groupCount = 0;
1154
1155     bool firstWord = true;
1156     bool lastWord = false;
1157
1158     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin() + firstIndices.mGroupIndex,
1159            endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin() + lastIndices.mGroupIndex + 1;
1160          groupLayoutIt != endGroupLayoutIt;
1161          ++groupLayoutIt, ++groupCount )
1162     {
1163       TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt );
1164
1165       if( groupCount == lastIndices.mGroupIndex - firstIndices.mGroupIndex )
1166       {
1167         lastGroup = true;
1168       }
1169
1170       std::size_t wordCount = 0;
1171       const std::size_t firstWordIndex = firstGroup ? firstIndices.mWordIndex : 0u;
1172       const std::size_t lastWordIndex = lastGroup ? lastIndices.mWordIndex : wordGroupLayoutInfo.mWordsLayoutInfo.size() - 1;
1173
1174       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin() + firstWordIndex,
1175              endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin() + lastWordIndex + 1;
1176            wordLayoutIt != endWordLayoutIt;
1177            ++wordLayoutIt, ++wordCount )
1178       {
1179         TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt );
1180
1181         if( lastGroup && ( wordCount == lastIndices.mWordIndex - firstWordIndex ) )
1182         {
1183           lastWord = true;
1184         }
1185
1186         const std::size_t firstCharacterIndex = firstWord ? firstIndices.mCharacterIndex : 0u;
1187         const std::size_t lastCharacterIndex = lastWord ? lastIndices.mCharacterIndex : wordLayoutInfo.mCharactersLayoutInfo.size() - 1;
1188         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin() + firstCharacterIndex,
1189                endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin() + lastCharacterIndex + 1;
1190              characterLayoutIt != endCharacterLayoutIt;
1191              ++characterLayoutIt )
1192         {
1193           TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt );
1194
1195           if( ellipsizeParameters.mEllipsizeLine )
1196           {
1197             // Calculates the character visibility and whether it needs to be replace by ellipsized text.
1198             CalculateVisibilityForEllipsize( layoutParameters,
1199                                              characterLayoutInfo,
1200                                              ellipsizeParameters,
1201                                              relayoutData );
1202
1203             if( ellipsizeParameters.mCreateEllipsizedTextActors )
1204             {
1205               // Create ellipsize text-actors if the character needs to be replaced.
1206               CreateEllipsizeTextActor( ellipsizeParameters,
1207                                         relayoutData );
1208             }
1209           }
1210           else
1211           {
1212             if( ( TextView::EllipsizeEnd == layoutParameters.mExceedPolicy ) ||
1213                 ( TextView::SplitEllipsizeEnd == layoutParameters.mExceedPolicy ))
1214             {
1215               if( !ellipsizeParameters.mIsLineHeightFullyVisible )
1216               {
1217                 // Make characters invisible.
1218                 characterLayoutInfo.mIsVisible = false;
1219               }
1220             }
1221           }
1222         } // end characters
1223         firstWord = false;
1224       } // end words
1225       firstGroup = false;
1226     } // end groups
1227   } // end lines
1228 }
1229
1230 void SetTextVisible( TextView::RelayoutData& relayoutData )
1231 {
1232   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(),
1233          endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
1234        lineLayoutIt != endLineLayoutIt;
1235        ++lineLayoutIt )
1236   {
1237     TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt );
1238
1239     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(),
1240            endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
1241          groupLayoutIt != endGroupLayoutIt;
1242          ++groupLayoutIt )
1243     {
1244       TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt );
1245
1246       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(),
1247              endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
1248            wordLayoutIt != endWordLayoutIt;
1249            ++wordLayoutIt )
1250       {
1251         TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt );
1252
1253         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(),
1254                endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end();
1255              characterLayoutIt != endCharacterLayoutIt;
1256              ++characterLayoutIt )
1257         {
1258           TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt );
1259
1260           characterLayoutInfo.mIsVisible = true;
1261           characterLayoutInfo.mGradientColor = Vector4::ZERO;
1262           characterLayoutInfo.mStartPoint = Vector2::ZERO;
1263           characterLayoutInfo.mEndPoint = Vector2::ZERO;
1264         } // end characters
1265       } // end words
1266     } // end group of words
1267   } // end lines
1268
1269   // Updates the visibility for text-input..
1270   for( std::vector<Toolkit::TextView::CharacterLayoutInfo>::iterator it = relayoutData.mCharacterLayoutInfoTable.begin(),
1271          endIt = relayoutData.mCharacterLayoutInfoTable.end();
1272        it != endIt;
1273        ++it )
1274   {
1275     Toolkit::TextView::CharacterLayoutInfo& characterLayoutInfo( *it );
1276
1277     characterLayoutInfo.mIsVisible = true;
1278   }
1279 }
1280
1281 void UpdateVisibilityForFade( const TextView::LayoutParameters& layoutParameters,
1282                               const TextView::VisualParameters& visualParameters,
1283                               TextView::RelayoutData& relayoutData )
1284 {
1285   RelayoutParameters relayoutParameters;
1286   FadeParameters fadeParameters;
1287
1288   // Calculates the fade thresholds (from where the text starts to fade out). If any of the fade boundaries is zero, it sets a very small value just to avoid a zero division.
1289   fadeParameters.mRightFadeBoundary = static_cast<float>( visualParameters.mFadeBoundary.mRight );
1290   fadeParameters.mRightFadeBoundaryOffset = ( visualParameters.mFadeBoundary.mRight > 0 ? fadeParameters.mRightFadeBoundary : MINIMUM_FADE_BOUNDARY );
1291   fadeParameters.mRightFadeThreshold = relayoutData.mTextViewSize.width - fadeParameters.mRightFadeBoundary;
1292   fadeParameters.mRightFadeThresholdOffset = relayoutData.mTextViewSize.width - fadeParameters.mRightFadeBoundaryOffset;
1293   fadeParameters.mLeftFadeBoundary = static_cast<float>( visualParameters.mFadeBoundary.mLeft );
1294   fadeParameters.mLeftFadeBoundaryOffset = ( visualParameters.mFadeBoundary.mLeft > 0 ? fadeParameters.mLeftFadeBoundary : MINIMUM_FADE_BOUNDARY );
1295   fadeParameters.mLeftFadeThreshold = fadeParameters.mLeftFadeBoundary;
1296   fadeParameters.mLeftFadeThresholdOffset = fadeParameters.mLeftFadeBoundaryOffset;
1297   fadeParameters.mTopFadeBoundary = static_cast<float>( visualParameters.mFadeBoundary.mTop );
1298   fadeParameters.mTopFadeBoundaryOffset = ( visualParameters.mFadeBoundary.mTop > 0 ? fadeParameters.mTopFadeBoundary : MINIMUM_FADE_BOUNDARY );
1299   fadeParameters.mTopFadeThreshold = fadeParameters.mTopFadeBoundary;
1300   fadeParameters.mTopFadeThresholdOffset = fadeParameters.mTopFadeBoundaryOffset;
1301   fadeParameters.mBottomFadeBoundary = static_cast<float>( visualParameters.mFadeBoundary.mBottom );
1302   fadeParameters.mBottomFadeBoundaryOffset = ( visualParameters.mFadeBoundary.mBottom > 0 ? fadeParameters.mBottomFadeBoundary : MINIMUM_FADE_BOUNDARY );
1303   fadeParameters.mBottomFadeThreshold = relayoutData.mTextViewSize.height - fadeParameters.mBottomFadeBoundary;
1304   fadeParameters.mBottomFadeThresholdOffset = relayoutData.mTextViewSize.height - fadeParameters.mBottomFadeBoundaryOffset;
1305
1306   // Calculates the fade out rect coeficients for the right, left, top and bottom sides of the text-view.
1307   fadeParameters.mRightAlphaCoeficients = CalculateRectParameters( Vector2( fadeParameters.mRightFadeThresholdOffset, 1.f ), Vector2( relayoutData.mTextViewSize.width, 0.f ) );
1308   fadeParameters.mLeftAlphaCoeficients = CalculateRectParameters( Vector2( fadeParameters.mLeftFadeThresholdOffset, 1.f ), Vector2( 0.f, 0.f ) );
1309   fadeParameters.mTopAlphaCoeficients = CalculateRectParameters( Vector2( fadeParameters.mTopFadeThresholdOffset, 1.f ), Vector2( 0.f, 0.f ) );
1310   fadeParameters.mBottomAlphaCoeficients = CalculateRectParameters( Vector2( fadeParameters.mBottomFadeThresholdOffset, 1.f ), Vector2( relayoutData.mTextViewSize.height, 0.f ) );
1311
1312   // Traverses all groups of characters and calculates the visibility.
1313
1314   std::size_t infoTableCharacterIndex = 0;
1315
1316   relayoutParameters.mIndices.mLineIndex = 0;
1317
1318   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(),
1319          endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
1320        lineLayoutIt != endLineLayoutIt;
1321        ++lineLayoutIt, ++relayoutParameters.mIndices.mLineIndex )
1322   {
1323     TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt );
1324
1325     relayoutParameters.mIndices.mGroupIndex = 0;
1326
1327     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(),
1328            endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
1329          groupLayoutIt != endGroupLayoutIt;
1330          ++groupLayoutIt, ++relayoutParameters.mIndices.mGroupIndex )
1331     {
1332       TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt );
1333
1334       relayoutParameters.mIndices.mWordIndex = 0;
1335
1336       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(),
1337              endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
1338            wordLayoutIt != endWordLayoutIt;
1339            ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex )
1340       {
1341         TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt );
1342
1343         relayoutParameters.mIsFirstCharacterOfWord = true;
1344         relayoutParameters.mWordSize = wordLayoutInfo.mSize;
1345         relayoutParameters.mIndices.mCharacterIndex = 0;
1346
1347         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(),
1348                endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end();
1349              characterLayoutIt != endCharacterLayoutIt;
1350              ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex, ++infoTableCharacterIndex )
1351         {
1352           TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt );
1353
1354           relayoutParameters.mIsVisible = true;
1355           fadeParameters.mIsPartiallyVisible = false;
1356
1357           // Calculates the visibility for the current group of characters.
1358           CalculateVisibilityForFade( layoutParameters,
1359                                       characterLayoutInfo,
1360                                       relayoutParameters,
1361                                       fadeParameters,
1362                                       relayoutData );
1363
1364           // Updates the visibility for text-input..
1365           std::vector<Toolkit::TextView::CharacterLayoutInfo>::iterator it = relayoutData.mCharacterLayoutInfoTable.begin() + infoTableCharacterIndex;
1366
1367           Toolkit::TextView::CharacterLayoutInfo& characterLayoutTableInfo( *it );
1368
1369           characterLayoutTableInfo.mIsVisible = relayoutParameters.mIsVisible;
1370
1371           relayoutParameters.mIsFirstCharacterOfWord = false;
1372         } // end group of character
1373       } // end words
1374     } // end group of words
1375   } // end lines
1376 }
1377
1378 void UpdateVisibilityForEllipsize( const TextView::LayoutParameters& layoutParameters,
1379                                    const TextView::VisualParameters& visualParameters,
1380                                    TextView::RelayoutData& relayoutData )
1381 {
1382   // Traverses the laid-out lines and checks which ones doesn't fit in the text-view's boundary.
1383   for( Toolkit::TextView::LineLayoutInfoContainer::const_iterator lineInfoIt = relayoutData.mLines.begin(), endLineInfoIt = relayoutData.mLines.end();
1384        lineInfoIt != endLineInfoIt;
1385        ++lineInfoIt )
1386   {
1387     const Toolkit::TextView::LineLayoutInfo& lineInfo( *lineInfoIt );
1388
1389     // To check if a laid-out line fits in the text-view's boundary,
1390     // get the position of the first character is needed and do the test
1391     // with the laid-out line size.
1392
1393     // An bearing offset may have been applied to the first character so it's needed to
1394     // get the start position of the line.
1395
1396     // Some parameters used in the CalculateVisibilityForEllipsize() function.
1397     EllipsizeParameters ellipsizeParameters;
1398
1399     // Retrieves the first index and the last index of the line.
1400     ellipsizeParameters.mFirstIndex = lineInfo.mCharacterGlobalIndex;
1401     ellipsizeParameters.mLastIndex = 0;
1402     if( ( lineInfoIt + 1 ) != endLineInfoIt )
1403     {
1404       const Toolkit::TextView::LineLayoutInfo& nextLineInfo( *( lineInfoIt + 1 ) );
1405       ellipsizeParameters.mLastIndex = nextLineInfo.mCharacterGlobalIndex - 1;
1406     }
1407     else
1408     {
1409       ellipsizeParameters.mLastIndex = relayoutData.mCharacterLayoutInfoTable.size() - 1;
1410     }
1411
1412     // Retrieves the first character of the line and build the position of the line with the bearing.
1413     const Toolkit::TextView::CharacterLayoutInfo& characterInfo = *( relayoutData.mCharacterLayoutInfoTable.begin() + ellipsizeParameters.mFirstIndex );
1414
1415     // Calculates the bearing offset applied to the first character.
1416     const float bearingOffset = ( lineInfo.mSize.height - lineInfo.mAscender ) - characterInfo.mDescender;
1417
1418     // Build the position of the line by removing the bearing offset from the first character's position.
1419     const Vector3 position( characterInfo.mPosition.x,
1420                             characterInfo.mPosition.y + bearingOffset,
1421                             characterInfo.mPosition.z );
1422
1423     // Checks if the line needs to be ellipsized,
1424     ellipsizeParameters.mIsLineWidthFullyVisible = IsVisible( position,
1425                                                               lineInfo.mSize,
1426                                                               relayoutData.mTextViewSize,
1427                                                               FULLY_VISIBLE_WIDTH );
1428
1429     // If the exceed policy is EllipsizeEndOriginal it's enough to check
1430     // if the line fits in the width.
1431     ellipsizeParameters.mEllipsizeLine = !ellipsizeParameters.mIsLineWidthFullyVisible;
1432
1433     // If the exceed policy is EllipsizeEnd, it's needed to check if the next line exceeds the text-view's height.
1434     // If the next line exceeds the text-view height then it's going to be invisible and current line needs to be ellipsized.
1435     ellipsizeParameters.mIsLineHeightFullyVisible = true;
1436     ellipsizeParameters.mIsNextLineFullyVisibleHeight = true;
1437     if( ( TextView::EllipsizeEnd == layoutParameters.mExceedPolicy ) ||
1438         ( TextView::SplitEllipsizeEnd == layoutParameters.mExceedPolicy ) )
1439     {
1440       // Need to check if there is lines which doesn't fit in the height.
1441
1442       ellipsizeParameters.mIsLineHeightFullyVisible = IsVisible( position,
1443                                                                  lineInfo.mSize,
1444                                                                  relayoutData.mTextViewSize,
1445                                                                  FULLY_VISIBLE_HEIGHT );
1446
1447       ellipsizeParameters.mEllipsizeLine = ellipsizeParameters.mEllipsizeLine && ellipsizeParameters.mIsLineHeightFullyVisible;
1448
1449       if( ellipsizeParameters.mIsLineHeightFullyVisible && !ellipsizeParameters.mEllipsizeLine )
1450       {
1451         // Current line is not ellipsized.
1452         // Need to check if there is a next line and if it's not visible. If there is, current line needs to be ellipsized.
1453         Toolkit::TextView::LineLayoutInfoContainer::const_iterator nextLineInfoIt = lineInfoIt + 1;
1454         if( nextLineInfoIt != endLineInfoIt )
1455         {
1456           // Retrives the position of the first character of the line and remove
1457           // the bearing offset to build to build the position of the line.
1458           const Toolkit::TextView::LineLayoutInfo& nextLineInfo( *nextLineInfoIt );
1459           const Toolkit::TextView::CharacterLayoutInfo& characterInfo = *( relayoutData.mCharacterLayoutInfoTable.begin() + nextLineInfo.mCharacterGlobalIndex );
1460
1461           const float bearingOffset = ( ( lineInfo.mSize.height - lineInfo.mAscender ) - characterInfo.mDescender ) * relayoutData.mShrinkFactor;
1462
1463           const Vector3 position( characterInfo.mPosition.x,
1464                                   characterInfo.mPosition.y + bearingOffset,
1465                                   characterInfo.mPosition.z );
1466
1467           ellipsizeParameters.mIsNextLineFullyVisibleHeight = IsVisible( position,
1468                                                                          nextLineInfo.mSize,
1469                                                                          relayoutData.mTextViewSize,
1470                                                                          FULLY_VISIBLE_HEIGHT );
1471
1472           // If the next line is not visible, current line have to be ellipsized.
1473           ellipsizeParameters.mEllipsizeLine = !ellipsizeParameters.mIsNextLineFullyVisibleHeight;
1474         }
1475       }
1476     }
1477
1478     if( !ellipsizeParameters.mIsNextLineFullyVisibleHeight )
1479     {
1480       ellipsizeParameters.mLineWidth = position.x + lineInfo.mSize.width - relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo.mSize.width;
1481     }
1482
1483     // Sets the line descender.
1484     ellipsizeParameters.mLineDescender = lineInfo.mSize.height - lineInfo.mAscender;
1485
1486     // At this point, ellipsizeLine distinguish if a piece of line have to be ellipsized or not.
1487     EllipsizeLine( layoutParameters, ellipsizeParameters, relayoutData );
1488   }
1489 }
1490
1491 void UpdateVisibility( const TextView::LayoutParameters& layoutParameters,
1492                        const TextView::VisualParameters& visualParameters,
1493                        TextView::RelayoutData& relayoutData )
1494 {
1495   switch( layoutParameters.mExceedPolicy )
1496   {
1497     case TextView::FadeOriginal:
1498     case TextView::OriginalFade:
1499     case TextView::Fade:
1500     case TextView::SplitFade: // Fall through
1501     {
1502       UpdateVisibilityForFade( layoutParameters,
1503                                visualParameters,
1504                                relayoutData );
1505       break;
1506     }
1507     case TextView::EllipsizeEndOriginal:
1508     case TextView::SplitEllipsizeEnd:
1509     case TextView::EllipsizeEnd: // Fall through
1510     {
1511       // Set first all characters to visible as UpdateVisibilityForEllipsize() doesn't traverse all of them.
1512       SetTextVisible( relayoutData );
1513
1514       UpdateVisibilityForEllipsize( layoutParameters,
1515                                     visualParameters,
1516                                     relayoutData );
1517       break;
1518     }
1519     default:
1520     {
1521       SetTextVisible( relayoutData );
1522       break;
1523     }
1524   }
1525 }
1526
1527 void UpdateTextActorInfo( const TextView::VisualParameters& visualParameters,
1528                           TextView::RelayoutData& relayoutData )
1529 {
1530   CurrentTextActorInfo currentTextActorInfo;
1531
1532   // Traverses the text-actor and layout info data structures.
1533   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(),
1534          endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
1535        lineLayoutIt != endLineLayoutIt;
1536        ++lineLayoutIt )
1537   {
1538     TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt );
1539
1540     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(),
1541            endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
1542          groupLayoutIt != endGroupLayoutIt;
1543          ++groupLayoutIt )
1544     {
1545       TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt );
1546
1547       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(),
1548              endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
1549            wordLayoutIt != endWordLayoutIt;
1550            ++wordLayoutIt )
1551       {
1552         TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt );
1553
1554         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(),
1555                endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end();
1556              characterLayoutIt != endCharacterLayoutIt;
1557              ++characterLayoutIt )
1558         {
1559           TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt );
1560
1561           if( characterLayoutInfo.mIsColorGlyph )
1562           {
1563             ImageActor imageActor = ImageActor::DownCast( characterLayoutInfo.mGlyphActor );
1564
1565             if( characterLayoutInfo.mSetText )
1566             {
1567               GlyphImage image = GlyphImage::New( characterLayoutInfo.mStyledText.mText[0] );
1568
1569               if( image )
1570               {
1571                 imageActor.SetImage( image );
1572               }
1573               characterLayoutInfo.mSetText = false;
1574             }
1575
1576             imageActor.SetPosition( Vector3( characterLayoutInfo.mPosition.x + characterLayoutInfo.mOffset.x,
1577                                              characterLayoutInfo.mPosition.y + characterLayoutInfo.mOffset.y,
1578                                              characterLayoutInfo.mPosition.z ) );
1579             imageActor.SetSize( characterLayoutInfo.mSize );
1580
1581             // Sets the sort modifier value.
1582             imageActor.SetSortModifier( visualParameters.mSortModifier );
1583           }
1584           else
1585           {
1586             TextActor textActor = TextActor::DownCast( characterLayoutInfo.mGlyphActor );
1587             if( textActor )
1588             {
1589               // There is a new text-actor. Set text and everything to the previous one.
1590               if( currentTextActorInfo.textActor )
1591               {
1592                 currentTextActorInfo.textActor.SetText( currentTextActorInfo.text );
1593                 currentTextActorInfo.textActor.SetPosition( currentTextActorInfo.position );
1594                 currentTextActorInfo.textActor.SetSize( currentTextActorInfo.size );
1595
1596                 SetVisualParameters( currentTextActorInfo,
1597                                      visualParameters,
1598                                      relayoutData,
1599                                      lineLayoutInfo.mSize.height );
1600               }
1601
1602               currentTextActorInfo.text = characterLayoutInfo.mStyledText.mText;
1603               currentTextActorInfo.position = Vector3( characterLayoutInfo.mPosition.x + characterLayoutInfo.mOffset.x,
1604                                                        characterLayoutInfo.mPosition.y + characterLayoutInfo.mOffset.y,
1605                                                        characterLayoutInfo.mPosition.z );
1606               currentTextActorInfo.size = characterLayoutInfo.mSize * relayoutData.mShrinkFactor;
1607
1608               currentTextActorInfo.color = characterLayoutInfo.mStyledText.mStyle.GetTextColor();
1609               currentTextActorInfo.color.a = characterLayoutInfo.mColorAlpha;
1610
1611               currentTextActorInfo.gradientColor = characterLayoutInfo.mGradientColor;
1612               currentTextActorInfo.startPoint = characterLayoutInfo.mStartPoint;
1613               currentTextActorInfo.endPoint = characterLayoutInfo.mEndPoint;
1614
1615               // Update the current text-actor.
1616               currentTextActorInfo.textActor = textActor;
1617             }
1618             else
1619             {
1620               // If this character layout has no text-actor is because this character has the same style than previous one.
1621               // Add the character to the current text-actor and update the size.
1622               if( characterLayoutInfo.mIsVisible && ( TextViewProcessor::LineSeparator != wordLayoutInfo.mType ) )
1623               {
1624                 currentTextActorInfo.text.Append( characterLayoutInfo.mStyledText.mText );
1625
1626                 currentTextActorInfo.position.y = std::min( currentTextActorInfo.position.y, ( characterLayoutInfo.mPosition.y + characterLayoutInfo.mOffset.y ) );
1627                 currentTextActorInfo.size.width += characterLayoutInfo.mSize.width * relayoutData.mShrinkFactor;
1628                 currentTextActorInfo.size.height = std::max( currentTextActorInfo.size.height, characterLayoutInfo.mSize.height * relayoutData.mShrinkFactor );
1629               }
1630             }
1631           }
1632         } // end characters
1633       } // end words
1634
1635       if( !currentTextActorInfo.text.IsEmpty() )
1636       {
1637         if( currentTextActorInfo.textActor )
1638         {
1639           currentTextActorInfo.textActor.SetText( currentTextActorInfo.text );
1640           currentTextActorInfo.textActor.SetPosition( currentTextActorInfo.position );
1641           currentTextActorInfo.textActor.SetSize( currentTextActorInfo.size );
1642
1643           SetVisualParameters( currentTextActorInfo,
1644                                visualParameters,
1645                                relayoutData,
1646                                lineLayoutInfo.mSize.height );
1647         }
1648       }
1649     } //end groups of words
1650   } // end lines
1651
1652   for( std::vector<RenderableActor>::iterator it = relayoutData.mEllipsizedGlyphActors.begin(),
1653          endIt = relayoutData.mEllipsizedGlyphActors.end();
1654        it != endIt;
1655        ++it )
1656   {
1657     RenderableActor glyphActor = ( *it );
1658
1659     glyphActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
1660     glyphActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
1661
1662     // Sets the sort modifier value.
1663     glyphActor.SetSortModifier( visualParameters.mSortModifier );
1664
1665     // Enables or disables the blending.
1666     glyphActor.SetBlendMode( !visualParameters.mSnapshotModeEnabled ? BlendingMode::ON : BlendingMode::OFF );
1667   }
1668 }
1669
1670 void CalculateUnderlineInfo( TextView::RelayoutData& relayoutData, TextViewRelayout::TextUnderlineStatus& textUnderlineStatus )
1671 {
1672   // Traverse the whole text to find all groups of consecutive underlined characters in the same laid-out line.
1673   //
1674   // Note that relayoutData.mTextLayoutInfo contains layout info per line but these lines are the result of split the whole text every time a '\n' is found.
1675   // According with the layout option, one of this lines could be laid-out in more than one.
1676
1677   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
1678        lineIt != lineEndIt;
1679        ++lineIt )
1680   {
1681     TextViewProcessor::LineLayoutInfo& line( *lineIt );
1682
1683     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupIt = line.mWordGroupsLayoutInfo.begin(), groupEndIt = line.mWordGroupsLayoutInfo.end();
1684          groupIt != groupEndIt;
1685          ++groupIt )
1686     {
1687       TextViewProcessor::WordGroupLayoutInfo& group( *groupIt );
1688
1689       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordIt = group.mWordsLayoutInfo.begin(), wordEndIt = group.mWordsLayoutInfo.end();
1690            wordIt != wordEndIt;
1691            ++wordIt )
1692       {
1693         TextViewProcessor::WordLayoutInfo& word( *wordIt );
1694
1695         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end();
1696              characterIt != characterEndIt;
1697              ++characterIt )
1698         {
1699           TextViewProcessor::CharacterLayoutInfo& characterGroup( *characterIt );
1700
1701           // Check if current character is the first of a new laid-out line
1702           const bool isNewLine = ( textUnderlineStatus.mLineGlobalIndex < relayoutData.mLines.size() ) &&
1703                                  ( textUnderlineStatus.mCharacterGlobalIndex == ( *( relayoutData.mLines.begin() + textUnderlineStatus.mLineGlobalIndex ) ).mCharacterGlobalIndex );
1704           if( isNewLine )
1705           {
1706             ++textUnderlineStatus.mLineGlobalIndex; // If it's a new line, point to the next one.
1707           }
1708
1709           if( characterGroup.mStyledText.mStyle.GetUnderline() )
1710           {
1711             if( !textUnderlineStatus.mCurrentUnderlineStatus || // Current character is underlined but previous one it wasn't.
1712                 isNewLine )                                     // Current character is underlined and is the first of current laid-out line.
1713             {
1714               // Create a new underline info for the current underlined characters.
1715               UnderlineInfo underlineInfo;
1716               underlineInfo.mMaxHeight = characterGroup.mSize.height;
1717               underlineInfo.mMaxThickness = characterGroup.mUnderlineThickness;
1718               underlineInfo.mPosition = characterGroup.mUnderlinePosition;
1719
1720               textUnderlineStatus.mUnderlineInfo.push_back( underlineInfo );
1721
1722               textUnderlineStatus.mCurrentUnderlineStatus = true; // Set the current text is underlined.
1723             }
1724             else
1725             {
1726               // Retrieve last underline info and update it if current underline thickness is bigger.
1727               UnderlineInfo& underlineInfo( *( textUnderlineStatus.mUnderlineInfo.end() - 1 ) );
1728
1729               underlineInfo.mMaxHeight = std::max( underlineInfo.mMaxHeight, characterGroup.mSize.height );
1730
1731               if( characterGroup.mUnderlineThickness > underlineInfo.mMaxThickness )
1732               {
1733                 underlineInfo.mMaxThickness = characterGroup.mUnderlineThickness;
1734                 underlineInfo.mPosition = characterGroup.mUnderlinePosition;
1735               }
1736             }
1737           }
1738           else
1739           {
1740             textUnderlineStatus.mCurrentUnderlineStatus = false;
1741           }
1742
1743           ++textUnderlineStatus.mCharacterGlobalIndex;
1744         } // end group of characters.
1745       } // end words.
1746     } // end group of words.
1747   } // end lines.
1748 }
1749
1750 void SetUnderlineInfo( TextView::RelayoutData& relayoutData )
1751 {
1752   // Stores for each group of consecutive underlined text in each laid-out line its maximum thicknes, its position of that thickness and the maximum character's height.
1753   TextViewRelayout::TextUnderlineStatus textUnderlineStatus;
1754
1755   // Traverse the whole text to find all groups of consecutive underlined characters in the same laid-out line.
1756   CalculateUnderlineInfo( relayoutData, textUnderlineStatus );
1757
1758   if( textUnderlineStatus.mUnderlineInfo.empty() )
1759   {
1760     // There is no underlined text. Just exit.
1761     return;
1762   }
1763
1764   // At this point textUnderlineStatus.mUnderlineInfo has for each group of consecutive underlined characters their maximum thickness, position and maximum height.
1765   // Traverse the whole text and set the previously stored underline info in the text style.
1766
1767   std::vector<UnderlineInfo>::const_iterator underlineInfoIt = textUnderlineStatus.mUnderlineInfo.begin();
1768   std::vector<UnderlineInfo>::const_iterator underlineInfoEndIt = textUnderlineStatus.mUnderlineInfo.end();
1769
1770   UnderlineInfo underlineInfo;
1771
1772   if( underlineInfoIt < underlineInfoEndIt )
1773   {
1774     underlineInfo = ( *underlineInfoIt );
1775   }
1776
1777   // Whether current text is underlined.
1778   textUnderlineStatus.mCurrentUnderlineStatus = false;
1779   textUnderlineStatus.mCharacterGlobalIndex = 0;
1780   textUnderlineStatus.mLineGlobalIndex = 0;
1781
1782   float currentLineHeight = 0.f;
1783   float currentLineAscender = 0.f;
1784
1785   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
1786        lineIt != lineEndIt;
1787        ++lineIt )
1788   {
1789     TextViewProcessor::LineLayoutInfo& line( *lineIt );
1790
1791     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupIt = line.mWordGroupsLayoutInfo.begin(), groupEndIt = line.mWordGroupsLayoutInfo.end();
1792          groupIt != groupEndIt;
1793          ++groupIt )
1794     {
1795       TextViewProcessor::WordGroupLayoutInfo& group( *groupIt );
1796
1797       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordIt = group.mWordsLayoutInfo.begin(), wordEndIt = group.mWordsLayoutInfo.end();
1798            wordIt != wordEndIt;
1799            ++wordIt )
1800       {
1801         TextViewProcessor::WordLayoutInfo& word( *wordIt );
1802
1803         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end();
1804              characterIt != characterEndIt;
1805              ++characterIt )
1806         {
1807           TextViewProcessor::CharacterLayoutInfo& characterGroup( *characterIt );
1808
1809           // Check if current character is the first of a new laid-out line
1810
1811           bool isNewLine = false;
1812
1813           if( textUnderlineStatus.mLineGlobalIndex < relayoutData.mLines.size() )
1814           {
1815             const Toolkit::TextView::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mLines.begin() + textUnderlineStatus.mLineGlobalIndex ) );
1816             isNewLine = ( textUnderlineStatus.mCharacterGlobalIndex == lineLayoutInfo.mCharacterGlobalIndex );
1817
1818             if( isNewLine )
1819             {
1820               currentLineHeight = lineLayoutInfo.mSize.height;
1821               currentLineAscender = lineLayoutInfo.mAscender;
1822               ++textUnderlineStatus.mLineGlobalIndex; // If it's a new line, point to the next one.
1823             }
1824           }
1825
1826           if( characterGroup.mStyledText.mStyle.GetUnderline() )
1827           {
1828             if( textUnderlineStatus.mCurrentUnderlineStatus )
1829             {
1830               if( isNewLine )
1831               {
1832                 // Retrieves the thickness and position for the next piece of underlined text.
1833                 if( underlineInfoIt < underlineInfoEndIt )
1834                 {
1835                   ++underlineInfoIt;
1836                   if( underlineInfoIt < underlineInfoEndIt )
1837                   {
1838                     underlineInfo = *underlineInfoIt;
1839                   }
1840                 }
1841               }
1842             }
1843
1844             textUnderlineStatus.mCurrentUnderlineStatus = true;
1845
1846             // Sets the underline's thickness.
1847             characterGroup.mStyledText.mStyle.SetUnderlineThickness( underlineInfo.mMaxThickness );
1848
1849             // Before setting the position it needs to be adjusted to match the base line.
1850             const float bearingOffset = ( currentLineHeight - currentLineAscender ) - ( characterGroup.mSize.height - characterGroup.mAscender );
1851             const float positionOffset = ( underlineInfo.mMaxHeight - characterGroup.mSize.height ) - bearingOffset;
1852
1853             // Sets the underline's position.
1854             characterGroup.mStyledText.mStyle.SetUnderlinePosition( underlineInfo.mPosition - positionOffset );
1855
1856             // Mark the group of characters to be set the new style into the text-actor.
1857             characterGroup.mSetStyle = true;
1858           }
1859           else
1860           {
1861             if( textUnderlineStatus.mCurrentUnderlineStatus )
1862             {
1863               textUnderlineStatus.mCurrentUnderlineStatus = false;
1864
1865               // Retrieves the thickness and position for the next piece of underlined text.
1866               if( underlineInfoIt < underlineInfoEndIt )
1867               {
1868                 ++underlineInfoIt;
1869                 if( underlineInfoIt < underlineInfoEndIt )
1870                 {
1871                   underlineInfo = *underlineInfoIt;
1872                 }
1873               }
1874             }
1875           }
1876
1877           ++textUnderlineStatus.mCharacterGlobalIndex;
1878         } // end of group of characters.
1879       } // end of word.
1880     } // end of group of words.
1881   } // end of lines.
1882 }
1883
1884 void RemoveGlyphActors( Actor textView,
1885                         const std::vector<RenderableActor>& glyphActors )
1886 {
1887   // Removes previously inserted renderable-actors.
1888   // The SplitByNewLineChar::Relayout(), SplitByWord::Relayout() and SplitByChar::Relayout() functions add
1889   // renderable-actors to the text-view. A handle to these renderable-actors are stored and passed to this function
1890   // in order to remove 'only' renderable-actors added by these functions.
1891   // Any other actor added by a programmer or application won't be removed.
1892
1893   for( std::vector<RenderableActor>::const_reverse_iterator it = glyphActors.rbegin(), endIt = glyphActors.rend(); it != endIt; ++it )
1894   {
1895     textView.Remove( *it );
1896   }
1897 }
1898
1899 void InsertToTextView( const TextView::RelayoutOperationMask relayoutOperationMask,
1900                        Actor textView,
1901                        TextView::RelayoutData& relayoutData )
1902 {
1903   const bool insertToTextView = relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_VIEW;
1904   const bool insertToTextActorList = relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST;
1905
1906   // Add text-actors to the text-view.
1907
1908   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(),
1909          endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
1910        lineLayoutIt != endLineLayoutIt;
1911        ++lineLayoutIt )
1912   {
1913     TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt );
1914
1915     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(),
1916            endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
1917          groupLayoutIt != endGroupLayoutIt;
1918          ++groupLayoutIt )
1919     {
1920       TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt );
1921
1922       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(),
1923              endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
1924            wordLayoutIt != endWordLayoutIt;
1925            ++wordLayoutIt )
1926       {
1927         TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt );
1928
1929         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(),
1930                endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end();
1931              characterLayoutIt != endCharacterLayoutIt;
1932              ++characterLayoutIt )
1933         {
1934           TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt );
1935
1936           if( characterLayoutInfo.mIsVisible && characterLayoutInfo.mGlyphActor ) // White spaces and '\n' characters doesn't have a text-actor.
1937           {
1938             //Add to the text-view.
1939             if( insertToTextView )
1940             {
1941               textView.Add( characterLayoutInfo.mGlyphActor );
1942             }
1943             if( insertToTextActorList )
1944             {
1945               relayoutData.mGlyphActors.push_back( characterLayoutInfo.mGlyphActor );
1946             }
1947           }
1948         } // end group of character
1949       } // end words
1950     } // end group of words
1951   } // end lines
1952
1953   for( std::vector<RenderableActor>::iterator it = relayoutData.mEllipsizedGlyphActors.begin(),
1954          endIt = relayoutData.mEllipsizedGlyphActors.end();
1955        it != endIt;
1956        ++it )
1957   {
1958     RenderableActor glyphActor = ( *it );
1959
1960     //Add to the text-view.
1961     if( insertToTextView )
1962     {
1963       textView.Add( glyphActor );
1964     }
1965     if( insertToTextActorList )
1966     {
1967       relayoutData.mGlyphActors.push_back( glyphActor );
1968     }
1969   }
1970   relayoutData.mEllipsizedGlyphActors.clear();
1971 }
1972
1973 RenderableActor CreateGlyphActor( const Text& text, const TextStyle& style, TextActorCache& cache )
1974 {
1975   TextActor textActor = cache.RetrieveTextActor();
1976
1977   if( textActor )
1978   {
1979     // Update the text-actor.
1980     textActor.SetText( text );
1981     textActor.SetTextStyle( style );
1982   }
1983   else
1984   {
1985     // The text-actor cache is empty. Create a new one.
1986     textActor = TextActor::New( text, style, false, true );
1987   }
1988
1989   return textActor;
1990 }
1991
1992 } // namespace TextViewRelayout
1993
1994 } // namespace Internal
1995
1996 } // namespace Toolkit
1997
1998 } // namespace Dali