TextView - Split width exceed policy and EllipsizeEnd height exceed policy combinatio...
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-view / split-by-new-line-char-policies.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.0 (the License);
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://floralicense.org/license/
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an AS IS BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 // FILE HEADER
18
19 #include "split-by-new-line-char-policies.h"
20
21 // EXTERNAL INCLUDES
22
23 // INTERNAL INCLUDES
24
25 #include "relayout-utilities.h"
26 #include "text-view-processor.h"
27 #include <dali/integration-api/debug.h>
28
29 namespace Dali
30 {
31
32 namespace Toolkit
33 {
34
35 namespace Internal
36 {
37
38 namespace SplitByNewLineChar
39 {
40
41 namespace
42 {
43
44 Vector3 SplitPosition( const TextViewRelayout::RelayoutParameters& relayoutParameters,
45                        const TextView::LayoutParameters& layoutParameters,
46                        TextView::RelayoutData& relayoutData )
47 {
48   const float wordOffset = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.x );
49   const float previousPositionY = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.y );
50
51   if( relayoutParameters.mIsNewLine ||
52       relayoutParameters.mIsFirstCharacter ||
53       ( wordOffset + relayoutParameters.mCharacterSize.width > relayoutData.mTextViewSize.width ) )
54   {
55     if( !relayoutParameters.mIsNewLine &&
56         ( relayoutParameters.mIsWhiteSpace || relayoutParameters.mIsNewLineCharacter ) )
57     {
58       // Current character is a white space. Don't want to move a white space to the next line.
59       // These white spaces are placed just in the edge.
60       return Vector3( relayoutData.mTextViewSize.width - relayoutParameters.mWordSize.width, relayoutParameters.mPositionOffset.y, 0.f );
61     }
62     else
63     {
64       const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + relayoutParameters.mIndices.mLineIndex ) );
65
66       TextViewRelayout::SubLineLayoutInfo subLineInfo;
67       subLineInfo.mLineLength = 0.f;
68       subLineInfo.mMaxCharHeight = 0.f;
69       subLineInfo.mMaxAscender = 0.f;
70       TextViewRelayout::CalculateSubLineLayout( relayoutData.mTextViewSize.width,
71                                                 relayoutParameters.mIndices,
72                                                 lineLayoutInfo,
73                                                 TextViewRelayout::WrapByLineAndSplit,
74                                                 1.f, // Shrink factor
75                                                 subLineInfo );
76
77       // Stores some info to calculate the line justification in a post-process.
78       TextView::LineJustificationInfo justificationInfo;
79
80       justificationInfo.mIndices = relayoutParameters.mIndices;
81       justificationInfo.mLineLength = subLineInfo.mLineLength;
82
83       relayoutData.mLineJustificationInfo.push_back( justificationInfo );
84
85       Toolkit::TextView::LineLayoutInfo lineInfo;
86       lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex;    // Index to the first character of the next line.
87       lineInfo.mSize = Size( subLineInfo.mLineLength, subLineInfo.mMaxCharHeight ); // Size of this piece of line.
88       lineInfo.mAscender = subLineInfo.mMaxAscender;                                // Ascender of this piece of line.
89       relayoutData.mLines.push_back( lineInfo );
90
91       return Vector3( 0.f, previousPositionY + subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset, 0.f );
92     }
93   }
94   else
95   {
96     return Vector3( wordOffset, previousPositionY, 0.f );
97   }
98 }
99
100 void CalculateSizeAndPosition( const TextView::LayoutParameters& layoutParameters,
101                                TextView::RelayoutData& relayoutData )
102 {
103   // clear
104   relayoutData.mCharacterLayoutInfoTable.clear();
105   relayoutData.mLines.clear();
106   relayoutData.mTextSizeForRelayoutOption = Size();
107   relayoutData.mShrinkFactor = 1.f;
108
109   // Calculates the text size for split by char.
110   Vector4 minMaxXY( std::numeric_limits<float>::max(),
111                     std::numeric_limits<float>::max(),
112                     std::numeric_limits<float>::min(),
113                     std::numeric_limits<float>::min() );
114
115   TextViewRelayout::RelayoutParameters relayoutParameters;
116
117   if( TextView::ShrinkOriginal == layoutParameters.mExceedPolicy )
118   {
119     if( relayoutData.mTextLayoutInfo.mWholeTextSize.width > relayoutData.mTextViewSize.width )
120     {
121       relayoutData.mShrinkFactor = relayoutData.mTextViewSize.width / relayoutData.mTextLayoutInfo.mWholeTextSize.width;
122     }
123   }
124   else if( TextView::Shrink == layoutParameters.mExceedPolicy )
125   {
126     if( ( relayoutData.mTextLayoutInfo.mWholeTextSize.width > relayoutData.mTextViewSize.width ) ||
127         ( relayoutData.mTextLayoutInfo.mWholeTextSize.height > relayoutData.mTextViewSize.height ) )
128     {
129       relayoutData.mShrinkFactor = std::min( relayoutData.mTextViewSize.width / relayoutData.mTextLayoutInfo.mWholeTextSize.width,
130                                              relayoutData.mTextViewSize.height / relayoutData.mTextLayoutInfo.mWholeTextSize.height );
131     }
132   }
133
134   relayoutParameters.mIsFirstCharacter = true;
135   relayoutParameters.mIndices.mLineIndex = 0;
136   relayoutParameters.mPositionOffset = Vector3::ZERO;
137   relayoutParameters.mCharacterGlobalIndex = 0;
138
139   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(),
140        endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
141        lineLayoutIt != endLineLayoutIt;
142        ++lineLayoutIt, ++relayoutParameters.mIndices.mLineIndex )
143   {
144     TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt );
145
146     relayoutParameters.mLineSize = lineLayoutInfo.mSize * relayoutData.mShrinkFactor;
147
148     relayoutParameters.mIsNewLine = true;
149     relayoutParameters.mIndices.mGroupIndex = 0;
150
151     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(),
152          endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
153          groupLayoutIt != endGroupLayoutIt;
154          ++groupLayoutIt, ++relayoutParameters.mIndices.mGroupIndex )
155     {
156       TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt );
157
158       relayoutParameters.mIndices.mWordIndex = 0;
159
160       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(),
161            endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
162            wordLayoutIt != endWordLayoutIt;
163            ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex )
164       {
165         TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt );
166         relayoutParameters.mIsWhiteSpace = TextViewProcessor::WordSeparator == wordLayoutInfo.mType;
167         relayoutParameters.mIsNewLineCharacter = TextViewProcessor::LineSeparator == wordLayoutInfo.mType;
168
169         relayoutParameters.mIsFirstCharacterOfWord = true;
170         relayoutParameters.mWordSize = wordLayoutInfo.mSize;
171         relayoutParameters.mIndices.mCharacterIndex = 0;
172
173         if( relayoutParameters.mIsNewLine )
174         {
175           // Stores some info to calculate the line justification in a post-process.
176           const bool isSplitOriginal = layoutParameters.mExceedPolicy == TextView::SplitOriginal;
177
178           if( !isSplitOriginal )
179           {
180             TextView::LineJustificationInfo justificationInfo;
181
182             justificationInfo.mIndices = relayoutParameters.mIndices;
183             justificationInfo.mLineLength = relayoutParameters.mLineSize.width;
184
185             relayoutData.mLineJustificationInfo.push_back( justificationInfo );
186           }
187         }
188
189         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(),
190              endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end();
191              characterLayoutIt != endCharacterLayoutIt;
192              ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex )
193         {
194           TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt );
195           relayoutParameters.mCharacterSize = characterLayoutInfo.mSize;
196
197           relayoutParameters.mCharacterSize = characterLayoutInfo.mSize;
198
199           switch( layoutParameters.mExceedPolicy )
200           {
201             case TextView::OriginalShrink:
202             case TextView::SplitShrink:
203             case TextView::ShrinkFade: // Fall Through
204             {
205               DALI_LOG_WARNING( "SplitByNewLineChar::CalculateSizeAndPosition() policy not implemented.\n" );
206               break;
207             }
208             case TextView::Original: // Fall Through
209             case TextView::ShrinkOriginal: // Fall Through
210             case TextView::Shrink: // Fall Through
211             case TextView::OriginalFade: // Fall Through
212             case TextView::FadeOriginal: // Fall Through
213             case TextView::Fade: // Fall Through
214             case TextView::EllipsizeEndOriginal: // Fall Through
215             case TextView::EllipsizeEnd: // Fall Through
216             {
217               if( relayoutParameters.mIsNewLine )
218               {
219                 relayoutParameters.mPositionOffset.x = 0.f;
220                 relayoutParameters.mPositionOffset.y += lineLayoutInfo.mSize.height * relayoutData.mShrinkFactor;
221               }
222
223               characterLayoutInfo.mPosition = relayoutParameters.mPositionOffset;
224
225               relayoutParameters.mPositionOffset.x += characterLayoutInfo.mSize.width * relayoutData.mShrinkFactor;
226
227               if( relayoutParameters.mIsNewLine ||
228                   relayoutParameters.mIsFirstCharacter )
229               {
230                 Toolkit::TextView::LineLayoutInfo lineInfo;
231                 lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex;  // Index to the first character of the next line.
232                 lineInfo.mSize = relayoutParameters.mLineSize;                              // Size of this piece of line.
233                 lineInfo.mAscender = lineLayoutInfo.mAscender * relayoutData.mShrinkFactor; // Ascender of this piece of line.
234                 relayoutData.mLines.push_back( lineInfo );
235               }
236               break;
237             }
238             case TextView::SplitOriginal:
239             case TextView::SplitFade:
240             case TextView::SplitEllipsizeEnd: // Fall Through
241             {
242               characterLayoutInfo.mPosition = SplitPosition( relayoutParameters,
243                                                              layoutParameters,
244                                                              relayoutData );
245
246               relayoutParameters.mPositionOffset = characterLayoutInfo.mPosition + Vector3( characterLayoutInfo.mSize.width, 0.f, 0.f );
247               break;
248             }
249             default:
250             {
251               DALI_LOG_WARNING( "SplitByNewLineChar::CalculateSizeAndPosition() Layout configuration not possible.\n" );
252               break;
253             }
254           }
255
256           // Get last line info and calculate the bearing (used to align glyphs with the baseline).
257           TextViewRelayout::CalculateBearing( characterLayoutInfo, relayoutData );
258
259           // updates min and max position to calculate the text size for split by new line char.
260           TextViewRelayout::UpdateLayoutInfoTable( minMaxXY,
261                                                    wordGroupLayoutInfo,
262                                                    wordLayoutInfo,
263                                                    characterLayoutInfo,
264                                                    relayoutParameters,
265                                                    relayoutData );
266
267           ++relayoutParameters.mCharacterGlobalIndex;
268           relayoutParameters.mIsFirstCharacter = false;
269           relayoutParameters.mIsNewLine = false;
270         } // end characters
271       } // end words
272     } // end group of words
273   } // end lines
274
275   if( relayoutData.mCharacterLayoutInfoTable.empty() )
276   {
277     relayoutData.mTextSizeForRelayoutOption = Size();
278   }
279   else
280   {
281     relayoutData.mTextSizeForRelayoutOption.width = minMaxXY.z - minMaxXY.x;
282     relayoutData.mTextSizeForRelayoutOption.height = minMaxXY.w - minMaxXY.y;
283   }
284
285   // Check if the last character is a new line character. In that case the height should be added.
286   if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() )
287   {
288     const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) );
289
290     if( lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) // if it's empty, it means the last character is a new line character.
291     {
292       relayoutData.mTextSizeForRelayoutOption.height += lineLayoutInfo.mSize.height * relayoutData.mShrinkFactor;
293     }
294   }
295 }
296
297 } // namespace
298
299 void Relayout( Actor textView,
300                TextView::RelayoutOperationMask relayoutOperationMask,
301                const TextView::LayoutParameters& layoutParameters,
302                const TextView::VisualParameters& visualParameters,
303                TextView::RelayoutData& relayoutData )
304 {
305   if( relayoutOperationMask & TextView::RELAYOUT_SIZE_POSITION )
306   {
307     relayoutData.mLineJustificationInfo.clear();
308     CalculateSizeAndPosition( layoutParameters,
309                               relayoutData );
310
311     TextViewRelayout::SetUnderlineInfo( relayoutData );
312   }
313
314   if( relayoutOperationMask & TextView::RELAYOUT_ALIGNMENT )
315   {
316     TextViewRelayout::UpdateAlignment( layoutParameters,
317                                        relayoutData );
318   }
319
320   if( relayoutOperationMask & TextView::RELAYOUT_VISIBILITY )
321   {
322     TextViewRelayout::UpdateVisibility( layoutParameters,
323                                         visualParameters,
324                                         relayoutData );
325   }
326
327   if( relayoutOperationMask & TextView::RELAYOUT_INITIALIZE_TEXT_ACTORS )
328   {
329     TextViewProcessor::InitializeTextActorInfo( relayoutData );
330   }
331
332   if( relayoutOperationMask & TextView::RELAYOUT_TEXT_ACTOR_UPDATE )
333   {
334     TextViewRelayout::UpdateTextActorInfo( visualParameters,
335                                            relayoutData );
336   }
337
338   if( ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_VIEW ) ||
339       ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ) )
340   {
341     TextViewRelayout::InsertToTextView( relayoutOperationMask,
342                                         textView,
343                                         relayoutData );
344   }
345 }
346
347 } // namespace SplitByNewLineChar
348
349 } // namespace Internal
350
351 } // namespace Toolkit
352
353 } // namespace Dali