2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/controls/text-view/text-view-line-processor.h>
22 #include <dali-toolkit/internal/controls/text-view/text-view-word-processor.h>
23 #include <dali-toolkit/internal/controls/text-view/text-view-word-group-processor.h>
24 #include <dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h>
25 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
36 namespace TextViewProcessor
43 LineLayoutInfo::LineLayoutInfo()
46 mLineHeightOffset( 0.f ),
47 mWordGroupsLayoutInfo(),
48 mNumberOfCharacters( 0 )
52 LineLayoutInfo::LineLayoutInfo( const LineLayoutInfo& line )
53 : mSize( line.mSize ),
54 mAscender( line.mAscender ),
55 mLineHeightOffset( line.mLineHeightOffset ),
56 mWordGroupsLayoutInfo( line.mWordGroupsLayoutInfo ),
57 mNumberOfCharacters( line.mNumberOfCharacters )
61 LineLayoutInfo& LineLayoutInfo::operator=( const LineLayoutInfo& line )
64 mAscender = line.mAscender;
65 mLineHeightOffset = line.mLineHeightOffset;
66 mWordGroupsLayoutInfo = line.mWordGroupsLayoutInfo;
67 mNumberOfCharacters = line.mNumberOfCharacters;
72 void UpdateLineLayoutInfo( TextViewProcessor::LineLayoutInfo& lineLayoutInfo, const float lineHeightOffset )
74 lineLayoutInfo.mSize = Size();
76 for( WordGroupLayoutInfoContainer::iterator it = lineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lineLayoutInfo.mWordGroupsLayoutInfo.end();
80 WordGroupLayoutInfo& layoutInfo( *it );
82 UpdateSize( lineLayoutInfo.mSize, layoutInfo.mSize );
84 lineLayoutInfo.mSize.height += lineHeightOffset;
87 void CreateLineInfo( const MarkupProcessor::StyledTextArray& line,
88 TextView::RelayoutData& relayoutData,
89 TextViewProcessor::LineLayoutInfo& lineLayoutInfo )
91 // TODO: Split the line in group of words. Each group of words has only left to right characters or right to left characters but not a mix of both.
92 // TODO: set the wordgroup direction (LTR or RTL)
93 std::vector<MarkupProcessor::StyledTextArray> wordGroups;
94 if( TextProcessor::ContainsRightToLeftCharacter( line ) )
96 // If the text is bidirectional, the characters will be converted and reordered
97 // as specified by the Unicode Bidirectional Algorithm.
99 // Reorders the line and converts arabic glyphs (if any).
100 // It also split words in different groups if there are a mix of left to right
101 // and right to left text.
102 // If the whole line is left to right or right to left all words are grouped in the same group.
103 TextProcessor::ConvertBidirectionalText( line,
105 relayoutData.mCharacterLogicalToVisualMap,
106 relayoutData.mCharacterVisualToLogicalMap);
110 // No bidirectional text to process.
114 // Add all words in a group.
115 wordGroups.push_back( line );
117 // Create trivial bidirectional map tables.
118 std::size_t index = 0;
119 for( MarkupProcessor::StyledTextArray::const_iterator it = line.begin(), endIt = line.end(); it != endIt; ++it )
121 const MarkupProcessor::StyledText& styledText( *it );
123 for( std::size_t i = 0, length = styledText.mText.GetLength(); i < length; ++i )
125 relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
126 relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
133 // Traverse all group of words.
134 for( std::vector<MarkupProcessor::StyledTextArray>::const_iterator groupIt = wordGroups.begin(), groupEndIt = wordGroups.end(); groupIt != groupEndIt; ++groupIt )
136 const MarkupProcessor::StyledTextArray& wordGroup( *groupIt );
138 // Data structures for the new group of words.
139 WordGroupLayoutInfo wordGroupLayoutInfo;
141 CreateWordGroupInfo( wordGroup,
142 relayoutData.mTextLayoutInfo,
143 wordGroupLayoutInfo );
145 // Update layout info for the current line.
146 lineLayoutInfo.mAscender = std::max( lineLayoutInfo.mAscender, wordGroupLayoutInfo.mAscender );
147 lineLayoutInfo.mNumberOfCharacters += wordGroupLayoutInfo.mNumberOfCharacters;
148 UpdateSize( lineLayoutInfo.mSize, wordGroupLayoutInfo.mSize );
150 // Add the group of words to the current line.
151 lineLayoutInfo.mWordGroupsLayoutInfo.push_back( wordGroupLayoutInfo );
152 } // end of group of words
155 void RemoveWordGroupsFromLine( const std::size_t groupIndex,
156 const std::size_t numberOfGroups,
157 const PointSize& lineHeightOffset,
158 LineLayoutInfo& lineLayout )
160 // Removes groups of words from a line.
162 // * Check if words or lines can be merged after removing a group of words or a line separator have to be done outside this method.
164 // * Note: Currently it's only used to remove a number of groups of words from the beginning, or
165 // from groupIndex index to the end. This function doesn't merge groups of words (if a whole group is removed)
166 // TODO: merge groups of words if required.
168 const std::size_t groupEndIndex = groupIndex + numberOfGroups;
170 // Remove word groups from layout info.
171 lineLayout.mWordGroupsLayoutInfo.erase( lineLayout.mWordGroupsLayoutInfo.begin() + groupIndex,
172 lineLayout.mWordGroupsLayoutInfo.begin() + groupEndIndex );
174 // Update layout info.
175 lineLayout.mSize = Size();
176 lineLayout.mAscender = 0.f;
177 lineLayout.mNumberOfCharacters = 0;
178 for( WordGroupLayoutInfoContainer::const_iterator it = lineLayout.mWordGroupsLayoutInfo.begin(), endIt = lineLayout.mWordGroupsLayoutInfo.end();
182 const WordGroupLayoutInfo& group( *it );
184 UpdateSize( lineLayout.mSize, group.mSize );
185 lineLayout.mAscender = std::max( lineLayout.mAscender, group.mAscender );
186 lineLayout.mNumberOfCharacters += group.mNumberOfCharacters;
188 lineLayout.mSize.height += lineHeightOffset;
189 lineLayout.mLineHeightOffset = lineHeightOffset;
192 void SplitLine( const TextInfoIndices& indices,
193 const PointSize& lineHeightOffset,
194 LineLayoutInfo& firstLineLayoutInfo,
195 LineLayoutInfo& lastLineLayoutInfo )
197 // Splits a line in two.
198 // A group of words and a word may be split in two as well.
200 // * Split the group of words within the line.
201 // * Add last part of the group of words to the new line.
202 // * Add groups of words from groupPosition + 1 to the end.
203 // * Update layout info of the last line.
204 // * Remove groups of words added to the last part of the line from the first line.
207 if( ( 0 == indices.mGroupIndex ) && ( 0 == indices.mWordIndex ) && ( 0 == indices.mCharacterIndex ) )
209 // the whole line goes to the last part.
210 lastLineLayoutInfo = firstLineLayoutInfo;
212 firstLineLayoutInfo = LineLayoutInfo();
217 if( !firstLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
219 const std::size_t numberOfGroups = firstLineLayoutInfo.mWordGroupsLayoutInfo.size();
220 if( indices.mGroupIndex == numberOfGroups - 1 )
222 const WordGroupLayoutInfo& group( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
224 const std::size_t numberOfWords = group.mWordsLayoutInfo.size();
225 if( indices.mWordIndex == numberOfWords - 1 )
227 const WordLayoutInfo& word( *( group.mWordsLayoutInfo.end() - 1 ) );
228 if( indices.mCharacterIndex == word.mCharactersLayoutInfo.size() )
230 // the whole line goes to the first part.
232 // Just delete whatever there is in the last part of the line.
233 lastLineLayoutInfo = LineLayoutInfo();
241 lastLineLayoutInfo = LineLayoutInfo();
243 // 1) Split the group of words whitin the line.
244 WordGroupLayoutInfo& firstWordGroupLayoutInfo( *( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex ) );
245 WordGroupLayoutInfo lastWordGroupLayoutInfo;
247 SplitWordGroup( indices,
248 firstWordGroupLayoutInfo,
249 lastWordGroupLayoutInfo );
251 // 2) Add last part of the group of words to the new line.
252 if( !lastWordGroupLayoutInfo.mWordsLayoutInfo.empty() )
254 lastLineLayoutInfo.mWordGroupsLayoutInfo.push_back( lastWordGroupLayoutInfo );
257 // 3) Add groups from group-position + 1 to the end.
258 lastLineLayoutInfo.mWordGroupsLayoutInfo.insert( lastLineLayoutInfo.mWordGroupsLayoutInfo.end(),
259 firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + indices.mGroupIndex + 1, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() );
261 // 4) update layout info of the last line.
262 for( WordGroupLayoutInfoContainer::iterator it = lastLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = lastLineLayoutInfo.mWordGroupsLayoutInfo.end();
266 WordGroupLayoutInfo& layoutInfo( *it );
268 lastLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters;
269 UpdateSize( lastLineLayoutInfo.mSize, layoutInfo.mSize );
270 lastLineLayoutInfo.mAscender = std::max( lastLineLayoutInfo.mAscender, layoutInfo.mAscender );
272 lastLineLayoutInfo.mSize.height += lineHeightOffset;
273 lastLineLayoutInfo.mLineHeightOffset = lineHeightOffset;
275 // 5) Remove groups of words added to the last part of the line from the first line.
277 // if the number of characters of the last group of words of the first line is zero, it should be removed.
278 const std::size_t index = ( 0 == firstWordGroupLayoutInfo.mNumberOfCharacters ? indices.mGroupIndex : indices.mGroupIndex + 1 );
280 firstLineLayoutInfo.mWordGroupsLayoutInfo.erase( firstLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, firstLineLayoutInfo.mWordGroupsLayoutInfo.end() );
282 // 6) update layout info of the first line.
283 firstLineLayoutInfo.mNumberOfCharacters = 0;
284 firstLineLayoutInfo.mSize = Size();
285 firstLineLayoutInfo.mAscender = 0.f;
286 for( WordGroupLayoutInfoContainer::iterator it = firstLineLayoutInfo.mWordGroupsLayoutInfo.begin(), endIt = firstLineLayoutInfo.mWordGroupsLayoutInfo.end();
290 WordGroupLayoutInfo& layoutInfo( *it );
292 firstLineLayoutInfo.mNumberOfCharacters += layoutInfo.mNumberOfCharacters;
293 UpdateSize( firstLineLayoutInfo.mSize, layoutInfo.mSize );
294 firstLineLayoutInfo.mAscender = std::max( firstLineLayoutInfo.mAscender, layoutInfo.mAscender );
296 firstLineLayoutInfo.mSize.height += lineHeightOffset;
297 firstLineLayoutInfo.mLineHeightOffset = lineHeightOffset;
300 void MergeLine( LineLayoutInfo& firstLineLineLayoutInfo,
301 const LineLayoutInfo& lastLineLayoutInfo )
303 // Merges two given lines.
305 // Can't merge two lines if the last word of the first one is a line separator (new line character)
309 if( lastLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
311 // Nothing to merge if last line is empty.
315 if( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.empty() )
317 // If first line is empty, just copy the last line to the first one.
318 firstLineLineLayoutInfo = lastLineLayoutInfo;
323 if( 1 == firstLineLineLayoutInfo.mWordGroupsLayoutInfo.size() )
325 WordGroupLayoutInfo& wordGroupLayout( *firstLineLineLayoutInfo.mWordGroupsLayoutInfo.begin() );
327 if( wordGroupLayout.mWordsLayoutInfo.empty() )
329 // If first line is empty, just copy the last line to the first one.
330 firstLineLineLayoutInfo = lastLineLayoutInfo;
336 // Check the last word of the last group of the first line doesn't finish with a new line character.
337 WordGroupLayoutInfo& lastWordGroupLayout( *( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
338 WordLayoutInfo& lastWordLayout( *( lastWordGroupLayout.mWordsLayoutInfo.end() - 1 ) );
339 if( LineSeparator == lastWordLayout.mType )
341 DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeLine(). ERROR: A line can't be merged to another line which finishes with a new line character." );
344 // If the last group of the first line has the same direction than the first group of the last line, both lines can be concatenated.
345 // Otherwise, both groups have to be merged first.
346 const WordGroupLayoutInfo& firstWordGroupLayout( *lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() );
348 std::size_t index = 0;
349 if( lastWordGroupLayout.mDirection == firstWordGroupLayout.mDirection )
351 // Both groups of words have the same direction. They need to be merged.
352 MergeWordGroup( lastWordGroupLayout,
353 firstWordGroupLayout );
355 // After merging two groups of words, the rest of groups need to be added.
356 ++index; // By increasing this index the group of words already merged won't be added again.
360 firstLineLineLayoutInfo.mWordGroupsLayoutInfo.insert( firstLineLineLayoutInfo.mWordGroupsLayoutInfo.end(),
361 lastLineLayoutInfo.mWordGroupsLayoutInfo.begin() + index, lastLineLayoutInfo.mWordGroupsLayoutInfo.end() );
362 UpdateSize( firstLineLineLayoutInfo.mSize, lastLineLayoutInfo.mSize );
363 firstLineLineLayoutInfo.mAscender = std::max( firstLineLineLayoutInfo.mAscender, lastLineLayoutInfo.mAscender );
364 firstLineLineLayoutInfo.mLineHeightOffset = std::max( firstLineLineLayoutInfo.mLineHeightOffset, lastLineLayoutInfo.mLineHeightOffset );
365 firstLineLineLayoutInfo.mNumberOfCharacters += lastLineLayoutInfo.mNumberOfCharacters;
369 WordLayoutInfo GetLastWordLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
371 WordLayoutInfo layoutInfo;
373 if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() )
375 const WordGroupLayoutInfo& groupInfo( *( lineLayoutInfo.mWordGroupsLayoutInfo.end() - 1 ) );
377 if( !groupInfo.mWordsLayoutInfo.empty() )
379 layoutInfo = *( groupInfo.mWordsLayoutInfo.end() - 1 );
386 CharacterLayoutInfo GetFirstCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
388 CharacterLayoutInfo layoutInfo;
390 if( !lineLayoutInfo.mWordGroupsLayoutInfo.empty() )
392 const WordGroupLayoutInfo& groupInfo( *lineLayoutInfo.mWordGroupsLayoutInfo.begin() );
394 if( !groupInfo.mWordsLayoutInfo.empty() )
396 const WordLayoutInfo& wordInfo( *groupInfo.mWordsLayoutInfo.begin() );
398 layoutInfo = GetFirstCharacterLayoutInfo( wordInfo );
405 CharacterLayoutInfo GetLastCharacterLayoutInfo( const LineLayoutInfo& lineLayoutInfo )
407 const WordLayoutInfo wordInfo = GetLastWordLayoutInfo( lineLayoutInfo );
409 return GetLastCharacterLayoutInfo( wordInfo );
412 void CollectTextActorsFromLines( std::vector<TextActor>& textActors, const TextLayoutInfo& textLayoutInfo, const std::size_t lineIndexBegin, const std::size_t lineIndexEnd )
414 for( LineLayoutInfoContainer::const_iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexBegin, lineEndIt = textLayoutInfo.mLinesLayoutInfo.begin() + lineIndexEnd;
418 const LineLayoutInfo& line( *lineIt );
420 CollectTextActorsFromGroups( textActors, line, 0, line.mWordGroupsLayoutInfo.size() );
424 } //namespace TextViewProcessor
426 } //namespace Internal
428 } //namespace Toolkit