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