TextView - Right to Left implementation.
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-view / split-by-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-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 SplitByChar
36 {
37
38 namespace
39 {
40
41 Vector3 NoShrinkWhenExceedPosition( 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.mIsNewParagraphCharacter ) )
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       // Calculate the line length and the max character height for the current line.
62       TextViewRelayout::LineLayoutInfo subLineInfo;
63       subLineInfo.mLineLength = 0.f;
64       subLineInfo.mMaxCharHeight = 0.f;
65       subLineInfo.mMaxAscender = 0.f;
66       const TextViewProcessor::ParagraphLayoutInfo& paragraphLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + relayoutParameters.mIndices.mParagraphIndex ) );
67
68       TextViewRelayout::CalculateLineLayout( relayoutData.mTextViewSize.width,
69                                              relayoutParameters.mIndices,
70                                              paragraphLayoutInfo,
71                                              TextViewRelayout::WrapByCharacter,
72                                              1.f, // Shrink factor
73                                              subLineInfo );
74
75       Toolkit::TextView::LineLayoutInfo lineInfo;
76       lineInfo.mCharacterGlobalIndex = relayoutParameters.mCharacterGlobalIndex;    // Index to the first character of the next line.
77       lineInfo.mSize = Size( subLineInfo.mLineLength, subLineInfo.mMaxCharHeight ); // Size of this piece of paragraph.
78       lineInfo.mAscender = subLineInfo.mMaxAscender;                                // Ascender of this piece of paragraph.
79       relayoutData.mLines.push_back( lineInfo );
80
81       return Vector3( 0.f, previousPositionY + subLineInfo.mMaxCharHeight + layoutParameters.mLineHeightOffset, 0.f );
82     }
83   }
84   else
85   {
86     return Vector3( wordOffset, previousPositionY, 0.f );
87   }
88 }
89
90 void CalculateSizeAndPosition( const TextView::LayoutParameters& layoutParameters,
91                                TextView::RelayoutData& relayoutData )
92 {
93   TextViewRelayout::RelayoutParameters relayoutParameters;
94
95   // clear
96   relayoutData.mCharacterLayoutInfoTable.clear();
97   relayoutData.mLines.clear();
98   relayoutData.mTextSizeForRelayoutOption = Size();
99
100   // Calculate the text size for split by char.
101   Vector4 minMaxXY( std::numeric_limits<float>::max(),
102                     std::numeric_limits<float>::max(),
103                     std::numeric_limits<float>::min(),
104                     std::numeric_limits<float>::min() );
105
106   relayoutData.mShrinkFactor = 1.f; // Shrink factor used when the exceed policy contains ShrinkToFit
107
108   relayoutParameters.mPositionOffset = Vector3::ZERO;
109   relayoutParameters.mIsFirstCharacter = true;
110   relayoutParameters.mIndices.mParagraphIndex = 0u;
111   relayoutParameters.mCharacterGlobalIndex = 0u;
112
113   for( TextViewProcessor::ParagraphLayoutInfoContainer::iterator paragraphLayoutIt = relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin(),
114          endParagraphLayoutIt = relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.end();
115        paragraphLayoutIt != endParagraphLayoutIt;
116        ++paragraphLayoutIt, ++relayoutParameters.mIndices.mParagraphIndex )
117   {
118     TextViewProcessor::ParagraphLayoutInfo& paragraphLayoutInfo( *paragraphLayoutIt );
119
120     relayoutParameters.mIsNewLine = true;
121     relayoutParameters.mParagraphSize = paragraphLayoutInfo.mSize;
122     relayoutParameters.mIndices.mWordIndex = 0u;
123
124     for( TextViewProcessor::WordLayoutInfoContainer::iterator wordLayoutIt = paragraphLayoutInfo.mWordsLayoutInfo.begin(),
125            endWordLayoutIt = paragraphLayoutInfo.mWordsLayoutInfo.end();
126          wordLayoutIt != endWordLayoutIt;
127          ++wordLayoutIt, ++relayoutParameters.mIndices.mWordIndex )
128     {
129       TextViewProcessor::WordLayoutInfo& wordLayoutInfo( *wordLayoutIt );
130       relayoutParameters.mIsWhiteSpace = TextViewProcessor::WordSeparator == wordLayoutInfo.mType;
131       relayoutParameters.mIsNewParagraphCharacter = TextViewProcessor::ParagraphSeparator == wordLayoutInfo.mType;
132
133       relayoutParameters.mIsFirstCharacterOfWord = true;
134       relayoutParameters.mWordSize = wordLayoutInfo.mSize;
135       relayoutParameters.mIndices.mCharacterIndex = 0u;
136
137       for( TextViewProcessor::CharacterLayoutInfoContainer::iterator characterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.begin(),
138              endCharacterLayoutIt = wordLayoutInfo.mCharactersLayoutInfo.end();
139            characterLayoutIt != endCharacterLayoutIt;
140            ++characterLayoutIt, ++relayoutParameters.mIndices.mCharacterIndex )
141       {
142         TextViewProcessor::CharacterLayoutInfo& characterLayoutInfo( *characterLayoutIt );
143
144         relayoutParameters.mCharacterSize = characterLayoutInfo.mSize;
145
146         switch( layoutParameters.mExceedPolicy )
147         {
148           case TextView::OriginalShrink:
149           case TextView::SplitOriginal:
150           case TextView::SplitFade:
151           case TextView::SplitEllipsizeEnd:
152           case TextView::SplitShrink:
153           case TextView::ShrinkOriginal:
154           case TextView::ShrinkFade:
155           case TextView::Shrink:
156           case TextView::EllipsizeEndOriginal:
157           case TextView::EllipsizeEnd: // Fall Through
158           {
159             DALI_LOG_WARNING( "SplitByChar::CalculateSizeAndPosition() policy not implemented.\n" );
160             break;
161           }
162           case TextView::OriginalFade:
163           case TextView::FadeOriginal:
164           case TextView::Original:
165           case TextView::Fade: // Fall Through
166           {
167             characterLayoutInfo.mPosition = NoShrinkWhenExceedPosition( relayoutParameters,
168                                                                         layoutParameters,
169                                                                         relayoutData );
170
171             relayoutParameters.mPositionOffset = characterLayoutInfo.mPosition + Vector3( characterLayoutInfo.mSize.width, 0.f, 0.f );
172             break;
173           }
174           default:
175           {
176             DALI_LOG_WARNING( "SplitByChar::CalculateSizeAndPosition() policy combination not possible.\n" );
177           }
178         }
179
180         // Get last line info and calculate the bearing (used to align glyphs with the baseline).
181         TextViewRelayout::CalculateBearing( characterLayoutInfo, relayoutData );
182
183         // updates min and max position to calculate the text size for split by char.
184         TextViewRelayout::UpdateLayoutInfoTable( minMaxXY,
185                                                  wordLayoutInfo,
186                                                  characterLayoutInfo,
187                                                  relayoutParameters,
188                                                  relayoutData );
189
190         ++relayoutParameters.mCharacterGlobalIndex;
191         relayoutParameters.mIsFirstCharacter = false;
192         relayoutParameters.mIsNewLine = false;
193       } // end characters
194     } // end words
195   } // end paragraphs
196
197   if( relayoutData.mCharacterLayoutInfoTable.empty() )
198   {
199     relayoutData.mTextSizeForRelayoutOption = Size();
200   }
201   else
202   {
203     relayoutData.mTextSizeForRelayoutOption.width = minMaxXY.z - minMaxXY.x;
204     relayoutData.mTextSizeForRelayoutOption.height = minMaxXY.w - minMaxXY.y;
205   }
206
207   // Check if the last character is a new paragraph character. In that case the height should be added.
208   if( !relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.empty() )
209   {
210     const TextViewProcessor::ParagraphLayoutInfo& paragraphLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.end() - 1u ) );
211
212     if( paragraphLayoutInfo.mWordsLayoutInfo.empty() ) // if it's empty, it means the last character is a new paragraph character.
213     {
214       relayoutData.mTextSizeForRelayoutOption.height += paragraphLayoutInfo.mSize.height;
215     }
216   }
217 }
218
219 } // namespace
220
221 void Relayout( Actor textView,
222                TextView::RelayoutOperationMask relayoutOperationMask,
223                const TextView::LayoutParameters& layoutParameters,
224                const TextView::VisualParameters& visualParameters,
225                TextView::RelayoutData& relayoutData )
226 {
227   if( relayoutOperationMask & TextView::RELAYOUT_SIZE_POSITION )
228   {
229     CalculateSizeAndPosition( layoutParameters,
230                               relayoutData );
231
232     TextViewRelayout::ReorderRightToLeftLayout( relayoutData );
233
234     TextViewRelayout::SetUnderlineInfo( relayoutData );
235   }
236
237   if( relayoutOperationMask & TextView::RELAYOUT_ALIGNMENT )
238   {
239     TextViewRelayout::UpdateAlignment( layoutParameters,
240                                        relayoutData );
241   }
242
243   if( relayoutOperationMask & TextView::RELAYOUT_VISIBILITY )
244   {
245     TextViewRelayout::UpdateVisibility( layoutParameters,
246                                         visualParameters,
247                                         relayoutData );
248   }
249
250   const bool initializeTextActors = relayoutOperationMask & TextView::RELAYOUT_INITIALIZE_TEXT_ACTORS;
251   const bool updateTextActors = relayoutOperationMask & TextView::RELAYOUT_TEXT_ACTOR_UPDATE;
252   if( initializeTextActors || updateTextActors )
253   {
254     TextViewRelayout::UpdateTextActorInfo( visualParameters,
255                                            relayoutData,
256                                            initializeTextActors );
257   }
258
259   if( relayoutOperationMask & TextView::RELAYOUT_INSERT_TO_TEXT_VIEW )
260   {
261     TextViewRelayout::InsertToTextView( textView,
262                                         relayoutData );
263   }
264 }
265
266 } // namespace SplitByChar
267
268 } // namespace Internal
269
270 } // namespace Toolkit
271
272 } // namespace Dali