Split dali-toolkit into Base & Optional
[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: // Fall Through
240             {
241               characterLayoutInfo.mPosition = SplitPosition( relayoutParameters,
242                                                              layoutParameters,
243                                                              relayoutData );
244
245               relayoutParameters.mPositionOffset = characterLayoutInfo.mPosition + Vector3( characterLayoutInfo.mSize.width, 0.f, 0.f );
246               break;
247             }
248             default:
249             {
250               DALI_LOG_WARNING( "SplitByNewLineChar::CalculateSizeAndPosition() Layout configuration not possible.\n" );
251               break;
252             }
253           }
254
255           // Get last line info and calculate the bearing (used to align glyphs with the baseline).
256           TextViewRelayout::CalculateBearing( characterLayoutInfo, relayoutData );
257
258           // updates min and max position to calculate the text size for split by new line char.
259           TextViewRelayout::UpdateLayoutInfoTable( minMaxXY,
260                                                    wordGroupLayoutInfo,
261                                                    wordLayoutInfo,
262                                                    characterLayoutInfo,
263                                                    relayoutParameters,
264                                                    relayoutData );
265
266           ++relayoutParameters.mCharacterGlobalIndex;
267           relayoutParameters.mIsFirstCharacter = false;
268           relayoutParameters.mIsNewLine = false;
269         } // end characters
270       } // end words
271     } // end group of words
272   } // end lines
273
274   if( relayoutData.mCharacterLayoutInfoTable.empty() )
275   {
276     relayoutData.mTextSizeForRelayoutOption = Size();
277   }
278   else
279   {
280     relayoutData.mTextSizeForRelayoutOption.width = minMaxXY.z - minMaxXY.x;
281     relayoutData.mTextSizeForRelayoutOption.height = minMaxXY.w - minMaxXY.y;
282   }
283
284   // Check if the last character is a new line character. In that case the height should be added.
285   if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() )
286   {
287     const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) );
288
289     if( lineLayoutInfo.mWordGroupsLayoutInfo.empty() ) // if it's empty, it means the last character is a new line character.
290     {
291       relayoutData.mTextSizeForRelayoutOption.height += lineLayoutInfo.mSize.height * relayoutData.mShrinkFactor;
292     }
293   }
294 }
295
296 } // namespace
297
298 void Relayout( Actor textView,
299                TextView::RelayoutOperationMask relayoutOperationMask,
300                const TextView::LayoutParameters& layoutParameters,
301                const TextView::VisualParameters& visualParameters,
302                TextView::RelayoutData& relayoutData )
303 {
304   if( relayoutOperationMask & TextView::RELAYOUT_SIZE_POSITION )
305   {
306     relayoutData.mLineJustificationInfo.clear();
307     CalculateSizeAndPosition( layoutParameters,
308                               relayoutData );
309
310     TextViewRelayout::SetUnderlineInfo( relayoutData );
311   }
312
313   if( relayoutOperationMask & TextView::RELAYOUT_ALIGNMENT )
314   {
315     TextViewRelayout::UpdateAlignment( layoutParameters,
316                                        relayoutData );
317   }
318
319   if( relayoutOperationMask & TextView::RELAYOUT_VISIBILITY )
320   {
321     TextViewRelayout::UpdateVisibility( layoutParameters,
322                                         visualParameters,
323                                         relayoutData );
324   }
325
326   if( relayoutOperationMask & TextView::RELAYOUT_INITIALIZE_TEXT_ACTORS )
327   {
328     TextViewProcessor::InitializeTextActorInfo( relayoutData );
329   }
330
331   if( relayoutOperationMask & TextView::RELAYOUT_TEXT_ACTOR_UPDATE )
332   {
333     TextViewRelayout::UpdateTextActorInfo( visualParameters,
334                                            relayoutData );
335   }
336
337   if( ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_VIEW ) ||
338       ( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST ) )
339   {
340     TextViewRelayout::InsertToTextView( relayoutOperationMask,
341                                         textView,
342                                         relayoutData );
343   }
344 }
345
346 } // namespace SplitByNewLineChar
347
348 } // namespace Internal
349
350 } // namespace Toolkit
351
352 } // namespace Dali