License conversion from Flora to Apache 2.0
[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
20 #include "split-by-new-line-char-policies.h"
21
22 // EXTERNAL INCLUDES
23
24 // INTERNAL INCLUDES
25
26 #include "relayout-utilities.h"
27 #include "text-view-processor.h"
28 #include <dali/integration-api/debug.h>
29
30 namespace Dali
31 {
32
33 namespace Toolkit
34 {
35
36 namespace Internal
37 {
38
39 namespace SplitByNewLineChar
40 {
41
42 namespace
43 {
44
45 Vector3 SplitPosition( const TextViewRelayout::RelayoutParameters& relayoutParameters,
46                        const TextView::LayoutParameters& layoutParameters,
47                        TextView::RelayoutData& relayoutData )
48 {
49   const float wordOffset = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.x );
50   const float previousPositionY = ( relayoutParameters.mIsFirstCharacter ? 0.f : relayoutParameters.mPositionOffset.y );
51
52   if( relayoutParameters.mIsNewLine ||
53       relayoutParameters.mIsFirstCharacter ||
54       ( wordOffset + relayoutParameters.mCharacterSize.width > relayoutData.mTextViewSize.width ) )
55   {
56     if( !relayoutParameters.mIsNewLine &&
57         ( relayoutParameters.mIsWhiteSpace || relayoutParameters.mIsNewLineCharacter ) )
58     {
59       // Current character is a white space. Don't want to move a white space to the next line.
60       // These white spaces are placed just in the edge.
61       return Vector3( relayoutData.mTextViewSize.width - relayoutParameters.mWordSize.width, relayoutParameters.mPositionOffset.y, 0.f );
62     }
63     else
64     {
65       const TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + relayoutParameters.mIndices.mLineIndex ) );
66
67       TextViewRelayout::SubLineLayoutInfo subLineInfo;
68       subLineInfo.mLineLength = 0.f;
69       subLineInfo.mMaxCharHeight = 0.f;
70       subLineInfo.mMaxAscender = 0.f;
71       TextViewRelayout::CalculateSubLineLayout( relayoutData.mTextViewSize.width,
72                                                 relayoutParameters.mIndices,
73                                                 lineLayoutInfo,
74                                                 TextViewRelayout::WrapByLineAndSplit,
75                                                 1.f, // Shrink factor
76                                                 subLineInfo );
77
78       // Stores some info to calculate the line justification in a post-process.
79       TextView::LineJustificationInfo justificationInfo;
80
81       justificationInfo.mIndices = relayoutParameters.mIndices;
82       justificationInfo.mLineLength = subLineInfo.mLineLength;
83
84       relayoutData.mLineJustificationInfo.push_back( justificationInfo );
85
86       Toolkit::TextView::LineLayoutInfo lineInfo;
87       lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex;    // Index to the first character of the next line.
88       lineInfo.mSize = Size( subLineInfo.mLineLength, subLineInfo.mMaxCharHeight ); // Size of this piece of line.
89       lineInfo.mAscender = subLineInfo.mMaxAscender;                                // Ascender of this piece of line.
90       relayoutData.mLines.push_back( lineInfo );
91
92       return Vector3( 0.f, previousPositionY + subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset, 0.f );
93     }
94   }
95   else
96   {
97     return Vector3( wordOffset, previousPositionY, 0.f );
98   }
99 }
100
101 void CalculateSizeAndPosition( const TextView::LayoutParameters& layoutParameters,
102                                TextView::RelayoutData& relayoutData )
103 {
104   // clear
105   relayoutData.mCharacterLayoutInfoTable.clear();
106   relayoutData.mLines.clear();
107   relayoutData.mTextSizeForRelayoutOption = Size();
108   relayoutData.mShrinkFactor = 1.f;
109
110   // Calculates the text size for split by char.
111   Vector4 minMaxXY( std::numeric_limits<float>::max(),
112                     std::numeric_limits<float>::max(),
113                     std::numeric_limits<float>::min(),
114                     std::numeric_limits<float>::min() );
115
116   TextViewRelayout::RelayoutParameters relayoutParameters;
117
118   if( TextView::ShrinkOriginal == layoutParameters.mExceedPolicy )
119   {
120     if( relayoutData.mTextLayoutInfo.mWholeTextSize.width > relayoutData.mTextViewSize.width )
121     {
122       relayoutData.mShrinkFactor = relayoutData.mTextViewSize.width / relayoutData.mTextLayoutInfo.mWholeTextSize.width;
123     }
124   }
125   else if( TextView::Shrink == layoutParameters.mExceedPolicy )
126   {
127     if( ( relayoutData.mTextLayoutInfo.mWholeTextSize.width > relayoutData.mTextViewSize.width ) ||
128         ( relayoutData.mTextLayoutInfo.mWholeTextSize.height > relayoutData.mTextViewSize.height ) )
129     {
130       relayoutData.mShrinkFactor = std::min( relayoutData.mTextViewSize.width / relayoutData.mTextLayoutInfo.mWholeTextSize.width,
131                                              relayoutData.mTextViewSize.height / relayoutData.mTextLayoutInfo.mWholeTextSize.height );
132     }
133   }
134
135   relayoutParameters.mIsFirstCharacter = true;
136   relayoutParameters.mIndices.mLineIndex = 0;
137   relayoutParameters.mPositionOffset = Vector3::ZERO;
138   relayoutParameters.mCharacterGlobalIndex = 0;
139
140   for( TextViewProcessor::LineLayoutInfoContainer::iterator lineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(),
141        endLineLayoutIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
142        lineLayoutIt != endLineLayoutIt;
143        ++lineLayoutIt, ++relayoutParameters.mIndices.mLineIndex )
144   {
145     TextViewProcessor::LineLayoutInfo& lineLayoutInfo( *lineLayoutIt );
146
147     relayoutParameters.mLineSize = lineLayoutInfo.mSize * relayoutData.mShrinkFactor;
148
149     relayoutParameters.mIsNewLine = true;
150     relayoutParameters.mIndices.mGroupIndex = 0;
151
152     for( TextViewProcessor::WordGroupLayoutInfoContainer::iterator groupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.begin(),
153          endGroupLayoutIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
154          groupLayoutIt != endGroupLayoutIt;
155          ++groupLayoutIt, ++relayoutParameters.mIndices.mGroupIndex )
156     {
157       TextViewProcessor::WordGroupLayoutInfo& wordGroupLayoutInfo( *groupLayoutIt );
158
159       relayoutParameters.mIndices.mWordIndex = 0;
160
161       for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.begin(),
162            endWordLayoutIt = wordGroupLayoutInfo.mWordsLayoutInfo.end();
163            wordLayoutIt != endWordLayoutIt;
164            ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex )
165       {
166         TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt );
167         relayoutParameters.mIsWhiteSpace = TextViewProcessor::WordSeparator == wordLayoutInfo.mType;
168         relayoutParameters.mIsNewLineCharacter = TextViewProcessor::LineSeparator == wordLayoutInfo.mType;
169
170         relayoutParameters.mIsFirstCharacterOfWord = true;
171         relayoutParameters.mWordSize = wordLayoutInfo.mSize;
172         relayoutParameters.mIndices.mCharacterIndex = 0;
173
174         if( relayoutParameters.mIsNewLine )
175         {
176           // Stores some info to calculate the line justification in a post-process.
177           const bool isSplitOriginal = layoutParameters.mExceedPolicy == TextView::SplitOriginal;
178
179           if( !isSplitOriginal )
180           {
181             TextView::LineJustificationInfo justificationInfo;
182
183             justificationInfo.mIndices = relayoutParameters.mIndices;
184             justificationInfo.mLineLength = relayoutParameters.mLineSize.width;
185
186             relayoutData.mLineJustificationInfo.push_back( justificationInfo );
187           }
188         }
189
190         for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(),
191              endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end();
192              characterLayoutIt != endCharacterLayoutIt;
193              ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex )
194         {
195           TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt );
196           relayoutParameters.mCharacterSize = characterLayoutInfo.mSize;
197
198           relayoutParameters.mCharacterSize = characterLayoutInfo.mSize;
199
200           switch( layoutParameters.mExceedPolicy )
201           {
202             case TextView::OriginalShrink:
203             case TextView::SplitShrink:
204             case TextView::ShrinkFade: // Fall Through
205             {
206               DALI_LOG_WARNING( "SplitByNewLineChar::CalculateSizeAndPosition() policy not implemented.\n" );
207               break;
208             }
209             case TextView::Original: // Fall Through
210             case TextView::ShrinkOriginal: // Fall Through
211             case TextView::Shrink: // Fall Through
212             case TextView::OriginalFade: // Fall Through
213             case TextView::FadeOriginal: // Fall Through
214             case TextView::Fade: // Fall Through
215             case TextView::EllipsizeEndOriginal: // Fall Through
216             case TextView::EllipsizeEnd: // Fall Through
217             {
218               if( relayoutParameters.mIsNewLine )
219               {
220                 relayoutParameters.mPositionOffset.x = 0.f;
221                 relayoutParameters.mPositionOffset.y += lineLayoutInfo.mSize.height * relayoutData.mShrinkFactor;
222               }
223
224               characterLayoutInfo.mPosition = relayoutParameters.mPositionOffset;
225
226               relayoutParameters.mPositionOffset.x += characterLayoutInfo.mSize.width * relayoutData.mShrinkFactor;
227
228               if( relayoutParameters.mIsNewLine ||
229                   relayoutParameters.mIsFirstCharacter )
230               {
231                 Toolkit::TextView::LineLayoutInfo lineInfo;
232                 lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex;  // Index to the first character of the next line.
233                 lineInfo.mSize = relayoutParameters.mLineSize;                              // Size of this piece of line.
234                 lineInfo.mAscender = lineLayoutInfo.mAscender * relayoutData.mShrinkFactor; // Ascender of this piece of line.
235                 relayoutData.mLines.push_back( lineInfo );
236               }
237               break;
238             }
239             case TextView::SplitOriginal:
240             case TextView::SplitFade: // 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