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