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