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-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-line-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
42 * Update text layout info.
44 * Updates the size of the whole text, the maximum width of all words and the number of characters.
46 * @param[in,out] textLayoutInfo
48 void UpdateTextLayoutInfo( TextLayoutInfo& textLayoutInfo )
50 // Initialize members to be updated.
51 textLayoutInfo.mWholeTextSize = Size();
52 textLayoutInfo.mMaxWordWidth = 0.f;
53 textLayoutInfo.mNumberOfCharacters = 0;
55 // Traverse all text updating values.
56 for( LineLayoutInfoContainer::const_iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = textLayoutInfo.mLinesLayoutInfo.end();
60 const LineLayoutInfo& line( *lineIt );
62 // Updates text size with the size of all lines.
63 UpdateSize( textLayoutInfo.mWholeTextSize, line.mSize, GrowHeight );
65 // Updates number of characters.
66 textLayoutInfo.mNumberOfCharacters += line.mNumberOfCharacters;
68 for( WordLayoutInfoContainer::const_iterator wordIt = line.mWordsLayoutInfo.begin(), wordEndIt = line.mWordsLayoutInfo.end();
72 const WordLayoutInfo& word( *wordIt );
74 textLayoutInfo.mMaxWordWidth = std::max( textLayoutInfo.mMaxWordWidth, word.mSize.width );
81 // Constructors and assignment operators
83 TextInfoIndices::TextInfoIndices()
90 TextInfoIndices::TextInfoIndices( const std::size_t lineIndex,
91 const std::size_t wordIndex,
92 const std::size_t characterIndex )
93 : mLineIndex( lineIndex ),
94 mWordIndex( wordIndex ),
95 mCharacterIndex( characterIndex )
99 bool TextInfoIndices::operator==( const TextInfoIndices& indices ) const
101 return ( ( mLineIndex == indices.mLineIndex ) &&
102 ( mWordIndex == indices.mWordIndex ) &&
103 ( mCharacterIndex == indices.mCharacterIndex ) );
106 /////////////////////
108 /////////////////////
110 TextLayoutInfo::TextLayoutInfo()
112 mMaxWordWidth( 0.f ),
114 mNumberOfCharacters( 0 ),
115 mMaxItalicsOffset( 0.f ),
116 mEllipsizeLayoutInfo()
120 TextLayoutInfo::TextLayoutInfo( const TextLayoutInfo& text )
121 : mWholeTextSize( text.mWholeTextSize ),
122 mMaxWordWidth( text.mMaxWordWidth ),
123 mLinesLayoutInfo( text.mLinesLayoutInfo ),
124 mNumberOfCharacters( text.mNumberOfCharacters ),
125 mMaxItalicsOffset( text.mMaxItalicsOffset ),
126 mEllipsizeLayoutInfo( text.mEllipsizeLayoutInfo )
130 TextLayoutInfo& TextLayoutInfo::operator=( const TextLayoutInfo& text )
132 mWholeTextSize = text.mWholeTextSize;
133 mMaxWordWidth = text.mMaxWordWidth;
134 mLinesLayoutInfo = text.mLinesLayoutInfo;
135 mNumberOfCharacters = text.mNumberOfCharacters;
136 mMaxItalicsOffset = text.mMaxItalicsOffset;
137 mEllipsizeLayoutInfo = text.mEllipsizeLayoutInfo;
142 /////////////////////////////////////////////////////////////////////////////////////////////
144 void CreateTextInfo( const MarkupProcessor::StyledTextArray& text,
145 const TextView::LayoutParameters& layoutParameters,
146 TextView::RelayoutData& relayoutData )
148 // * Traverse the given text spliting it in lines and each line in words.
149 // * White spaces and new line characters are alone in one word.
150 // * Bidirectional text is processed in each line.
151 // * Generates a layout data structure to store layout information (size, position, ascender, text direction, etc) and metrics of all characters.
152 // * Generates a text-actor data structure to store text, style and text-actors.
153 // TODO: finish and test the bidirectional implementation.
155 // Collect previously created text-actors.
156 std::vector<TextActor> textActors;
157 CollectTextActorsFromLines( textActors, relayoutData.mTextLayoutInfo, 0, relayoutData.mTextLayoutInfo.mLinesLayoutInfo.size() );
159 if( !textActors.empty() )
161 // Add text-actors to the cache.
162 relayoutData.mTextActorCache.InsertTextActors( textActors );
163 relayoutData.mTextActorCache.ClearTexts();
166 // Store the ellipsize layout info before clearing the previous created info.
167 const WordLayoutInfo ellipsizeInfo = relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo;
169 // clear previous created info.
170 relayoutData.mTextLayoutInfo = TextLayoutInfo();
171 relayoutData.mCharacterLogicalToVisualMap.clear();
172 relayoutData.mCharacterVisualToLogicalMap.clear();
174 // Sets the ellipsize layout info.
175 relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = ellipsizeInfo;
177 // Split the whole text in lines.
178 std::vector<MarkupProcessor::StyledTextArray> lines;
179 TextProcessor::SplitInLines( text,
182 // Traverse all lines
183 for( std::vector<MarkupProcessor::StyledTextArray>::const_iterator lineIt = lines.begin(), lineEndIt = lines.end(); lineIt != lineEndIt; ++lineIt )
185 const MarkupProcessor::StyledTextArray& line( *lineIt );
187 // Data structures for the new line
188 LineLayoutInfo lineLayoutInfo;
190 // Fills the line data structures with the layout info.
191 CreateLineInfo( line,
195 if( 0 < lineLayoutInfo.mNumberOfCharacters )
197 // do not add the line offset if the line has no characters.
198 lineLayoutInfo.mSize.height += layoutParameters.mLineHeightOffset;
199 lineLayoutInfo.mLineHeightOffset = layoutParameters.mLineHeightOffset;
203 // line height needs to be added for the last line.
205 float lineHeight = 0.f;
206 // Get the last character of the last line.
207 if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() )
209 const LineLayoutInfo& lineInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) );
211 const CharacterLayoutInfo characterInfo = GetLastCharacterLayoutInfo( lineInfo );
213 lineHeight = characterInfo.mSize.height;
216 lineLayoutInfo.mSize.height = lineHeight;
219 // Update layout info for the whole text.
220 UpdateSize( relayoutData.mTextLayoutInfo.mWholeTextSize, lineLayoutInfo.mSize, GrowHeight );
221 relayoutData.mTextLayoutInfo.mNumberOfCharacters += lineLayoutInfo.mNumberOfCharacters;
223 // Add the line to the current text.
224 relayoutData.mTextLayoutInfo.mLinesLayoutInfo.push_back( lineLayoutInfo );
228 void UpdateTextInfo( const std::size_t position,
229 const MarkupProcessor::StyledTextArray& text,
230 const TextView::LayoutParameters& layoutParameters,
231 TextView::RelayoutData& relayoutData )
233 // Update current internal data structure with added text.
235 // * Creates layout info for the given text.
236 // * With the given position, find where to add the text.
237 // * If the new text is not added at the end of current text, a line need to be split.
238 // * Merge the last line of the new text to the last part or the split line.
239 // * Add lines between first and last of the new text.
240 // * Merge the first part of the split line with the first line of the new text.
241 // * Update layout info and create new text actors if needed.
247 // nothing to do if the input text is empty.
251 if( 0 == relayoutData.mTextLayoutInfo.mNumberOfCharacters )
253 // Current text is empty. There is no need to update current data structure,
254 // just create a new one with the new input text.
255 CreateTextInfo( text,
262 if( position > relayoutData.mTextLayoutInfo.mNumberOfCharacters )
264 // Asserts if text is to be added out of bounds.
265 DALI_ASSERT_ALWAYS( !"TextViewProcessor::UpdateTextInfo (insert). Trying to insert text out of bounds." );
268 TextView::RelayoutData relayoutDataForNewText;
270 // Creates layout info for the given text.
271 // It doesn't create text-actors as text could be added to an existing one.
272 CreateTextInfo( text,
274 relayoutDataForNewText );
276 // Update logical-to-visual and visual-to-logical tables.
277 // TODO: check that for mixed RTL/LTR text.
278 std::size_t index = 0;
279 for( std::size_t i = 0; i < relayoutDataForNewText.mTextLayoutInfo.mNumberOfCharacters; ++i )
281 relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
282 relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
286 // If a line is split, it stores the last part of the line.
287 LineLayoutInfo lastLineLayoutInfo;
289 // Stores indices to the line, word and character of the given position.
290 TextInfoIndices textInfoIndices;
292 if( position < relayoutData.mTextLayoutInfo.mNumberOfCharacters )
294 // Get line, word and character indices for given position.
295 GetIndicesFromGlobalCharacterIndex( position,
296 relayoutData.mTextLayoutInfo,
301 // Split a line in two is needed, then merge the first part of the split line
302 // with the first line of the new text, add subsequent lines and merge the last line
303 // of the new text with the last part of the split line.
305 // Implementation notes!
307 // These references to the first line are declared in this scope because if new lines are inserted in step 2,
308 // they become invalid, making the algorithm to crash if used again.
309 // In the step 3, references to the first line are needed and declared again.
311 // Stores the first part of the split line.
312 LineLayoutInfo& firstLineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndices.mLineIndex ) );
314 SplitLine( textInfoIndices,
315 PointSize( layoutParameters.mLineHeightOffset ),
317 lastLineLayoutInfo );
321 // Position is just after the last character.
322 // Calculates indices for that position.
323 if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() )
325 textInfoIndices.mLineIndex = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.size() - 1;
326 const LineLayoutInfo& lineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) );
328 if( !lineLayoutInfo.mWordsLayoutInfo.empty() )
330 textInfoIndices.mWordIndex = lineLayoutInfo.mWordsLayoutInfo.size() - 1;
332 const WordLayoutInfo& wordLayoutInfo( *( lineLayoutInfo.mWordsLayoutInfo.end() - 1 ) );
333 textInfoIndices.mCharacterIndex = wordLayoutInfo.mCharactersLayoutInfo.size();
338 // 2) If the new text has more than 1 line, merge the last line of the input text with the last part of the split line.
339 //TODO check this cases ( num lines ==1, >1, >2 ) if it could be simplified.
340 if( relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.size() > 1 )
342 LineLayoutInfo& lastInputLineLayoutInfo( *( relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 ) );
344 MergeLine( lastInputLineLayoutInfo,
345 lastLineLayoutInfo );
347 if( relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.size() > 2 )
349 // Insert all lines except first and last in the text.
350 relayoutData.mTextLayoutInfo.mLinesLayoutInfo.insert( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndices.mLineIndex + 1,
351 relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.begin() + 1,
352 relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.end() - 1 );
355 // Insert the last line to the text
356 relayoutData.mTextLayoutInfo.mLinesLayoutInfo.insert( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndices.mLineIndex + relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.size() - 1,
357 lastInputLineLayoutInfo );
361 // Merge the new line to the last part of the split line.
362 LineLayoutInfo& inputLineLayoutInfo( *relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.begin() );
364 MergeLine( inputLineLayoutInfo,
365 lastLineLayoutInfo );
368 // 3) Merge the first line of the split text with the first line of the input text.
369 LineLayoutInfo& firstLineLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndices.mLineIndex ) );
370 LineLayoutInfo& firstInputLineLayoutInfo( *relayoutDataForNewText.mTextLayoutInfo.mLinesLayoutInfo.begin() );
372 MergeLine( firstLineLayoutInfo,
373 firstInputLineLayoutInfo );
375 // 4) Update text info.
377 // Updates the whole text size, maximum word size, etc.
378 UpdateTextLayoutInfo( relayoutData.mTextLayoutInfo );
381 void UpdateTextInfo( const std::size_t position,
382 const std::size_t numberOfCharacters,
383 const TextView::LayoutParameters& layoutParameters,
384 TextView::RelayoutData& relayoutData,
385 const TextOperationOnRemove clearText )
387 // Removes 'numberOfCharacters' starting from 'position'.
389 // * It checks if text to be deleted is in the same line or not:
390 // * If it is not, check which lines need to be split/merged or deleted.
391 // * If it is but all characters of the line are going to be deleted, just delete the line (nothing needs to be split/merged)
392 // * If only some characters of the same line are going to be deleted, proceed similarly: check if text to be deleted is in the same word.
393 // * If it is not, split/merge words.
394 // * Check if the whole word needs to be deleted.
395 // * Check if only some characters of the word need to be deleted.
396 // * Updates layout info.
398 // * The algorithm checks if a word separator is deleted (in that case, different words need to be merged) and if a new line separator is deleted (two lines need to be merged).
402 if( 0u == numberOfCharacters )
404 DALI_ASSERT_DEBUG( !"TextViewProcessor::UpdateTextInfo. WARNING: trying to delete 0 characters!" );
406 // nothing to do if no characters are deleted.
410 // Asserts if trying to delete text out of bounds.
411 DALI_ASSERT_ALWAYS( position + numberOfCharacters <= relayoutData.mTextLayoutInfo.mNumberOfCharacters && "TextViewProcessor::UpdateTextInfo. ERROR: trying to delete characters out of boundary" );
413 // Remove characters from character to visual map and vs //TODO: check this for RTL text!!
414 relayoutData.mCharacterLogicalToVisualMap.erase( relayoutData.mCharacterLogicalToVisualMap.end() - numberOfCharacters, relayoutData.mCharacterLogicalToVisualMap.end() );
415 relayoutData.mCharacterVisualToLogicalMap.erase( relayoutData.mCharacterVisualToLogicalMap.end() - numberOfCharacters, relayoutData.mCharacterVisualToLogicalMap.end() );
417 // Get line, word and character indices for the given start position.
418 TextInfoIndices textInfoIndicesBegin;
419 GetIndicesFromGlobalCharacterIndex( position,
420 relayoutData.mTextLayoutInfo,
421 textInfoIndicesBegin );
423 // Get line, word and character indices for the given end position (start position + number of characters to be deleted).
424 TextInfoIndices textInfoIndicesEnd;
425 GetIndicesFromGlobalCharacterIndex( position + numberOfCharacters - 1u,
426 relayoutData.mTextLayoutInfo,
427 textInfoIndicesEnd );
429 // Vectors used to temporary store text-actors removed from text.
430 // Three vectors are needed because text-actors are not removed in order
431 // but insert them in order is required to reuse them later.
432 std::vector<TextActor> removedTextActorsFromBegin;
433 std::vector<TextActor> removedTextActorsFromMid;
434 std::vector<TextActor> removedTextActorsFromEnd;
436 // Whether lines and words need to be merged.
437 bool mergeLines = false;
438 bool mergeWords = false;
440 // Indices of the lines and words to be merged.
441 TextInfoIndices textInfoMergeIndicesBegin;
442 TextInfoIndices textInfoMergeIndicesEnd;
444 const LineLayoutInfo& lineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesBegin.mLineIndex ) ); // used to check the number of characters of the line
445 // if all characters to be deleted are in the same line.
446 if( textInfoIndicesBegin.mLineIndex < textInfoIndicesEnd.mLineIndex )
448 // Deleted text is from different lines. It may need to split two lines, and merge first part of the first one with last part of the last one.
450 // whether first or last line need to be split and merged with the last part.
451 bool mergeFirstLine = false;
452 bool mergeLastLine = true;
454 textInfoMergeIndicesBegin.mLineIndex = textInfoIndicesBegin.mLineIndex;
455 textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesEnd.mLineIndex;
457 if( ( textInfoIndicesBegin.mWordIndex > 0u ) || ( textInfoIndicesBegin.mCharacterIndex > 0u ) )
459 // first character to be deleted is not the first one of the current line.
460 ++textInfoIndicesBegin.mLineIndex; // won't delete current line
462 // As some characters remain, this line could be merged with the last one.
463 mergeFirstLine = true;
466 // Check if all characters of the last line are going to be deleted.
467 bool wholeLineDeleted = false;
468 const LineLayoutInfo& lastLineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesEnd.mLineIndex ) );
469 if( textInfoIndicesEnd.mWordIndex + 1u == lastLineLayout.mWordsLayoutInfo.size() )
471 const WordLayoutInfo& lastWordLayout( *( lastLineLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
472 if( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() )
474 // All characters of the last line are going to be deleted.
475 ++textInfoIndicesEnd.mLineIndex; // will delete the last line.
477 // the whole last line is deleted. Need to check if the next line could be merged.
478 mergeLastLine = false;
479 wholeLineDeleted = true;
483 if( wholeLineDeleted )
485 // It means the whole last line is deleted completely.
486 // It's needed to check if there is another line after that could be merged.
487 if( textInfoIndicesEnd.mLineIndex < relayoutData.mTextLayoutInfo.mLinesLayoutInfo.size() )
489 mergeLastLine = true;
491 // Point the first characters of the next line.
492 textInfoIndicesEnd.mWordIndex = 0u;
493 textInfoIndicesEnd.mCharacterIndex = 0u;
494 textInfoMergeIndicesEnd.mLineIndex = textInfoIndicesEnd.mLineIndex;
498 // If some characters remain in the first and last line, they need to be merged.
499 mergeLines = mergeFirstLine && mergeLastLine;
503 // last line is going to be merged with the first one, so is not needed.
504 ++textInfoIndicesEnd.mLineIndex; // will delete the last line.
509 // Remove characters from the first line.
511 // Vectors used to temporary store text-actors removed from the line.
512 // Three vectors are needed because text-actors are not removed in order
513 // but insert them in order is required to reuse them later.
514 std::vector<TextActor> removedTextActorsFromFirstWord;
515 std::vector<TextActor> removedTextActorsFromFirstLine;
517 // As lineIndexBegin has been increased just to not to remove the line, decrease now is needed to access it.
518 LineLayoutInfo& lineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesBegin.mLineIndex - 1u ) );
520 if( ( textInfoIndicesBegin.mWordIndex + 1u < lineLayout.mWordsLayoutInfo.size() ) || ( 0u == textInfoIndicesBegin.mCharacterIndex ) )
522 // Remove extra words within current line. (and current word if whole characters are removed)
523 // 0 == characterIndexBegin means the whole word is deleted.
524 const std::size_t wordIndex = ( ( 0u == textInfoIndicesBegin.mCharacterIndex ) ? textInfoIndicesBegin.mWordIndex : textInfoIndicesBegin.mWordIndex + 1u );
526 // Store text-actors before removing them.
527 CollectTextActorsFromWords( removedTextActorsFromFirstLine, lineLayout, wordIndex, lineLayout.mWordsLayoutInfo.size() );
529 RemoveWordsFromLine( wordIndex,
530 lineLayout.mWordsLayoutInfo.size() - wordIndex,
531 layoutParameters.mLineHeightOffset,
535 if( ( textInfoIndicesBegin.mWordIndex < lineLayout.mWordsLayoutInfo.size() ) && ( textInfoIndicesBegin.mCharacterIndex > 0u ) )
537 // Only some characters of the word need to be removed.
538 WordLayoutInfo& wordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
540 // Store text-actors before removing them.
541 CollectTextActors( removedTextActorsFromFirstWord, wordLayout, textInfoIndicesBegin.mCharacterIndex, wordLayout.mCharactersLayoutInfo.size() );
543 const std::size_t wordNumberCharacters = wordLayout.mCharactersLayoutInfo.size();
544 RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
545 wordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex,
548 // discount the removed number of characters.
549 const std::size_t removedNumberOfCharacters = ( wordNumberCharacters - wordLayout.mCharactersLayoutInfo.size() );
550 lineLayout.mNumberOfCharacters -= removedNumberOfCharacters;
552 UpdateLineLayoutInfo( lineLayout, layoutParameters.mLineHeightOffset );
554 // Insert the text-actors in order.
555 removedTextActorsFromBegin.insert( removedTextActorsFromBegin.end(), removedTextActorsFromFirstWord.begin(), removedTextActorsFromFirstWord.end() );
556 removedTextActorsFromBegin.insert( removedTextActorsFromBegin.end(), removedTextActorsFromFirstLine.begin(), removedTextActorsFromFirstLine.end() );
559 if( mergeLastLine && !wholeLineDeleted )
561 // Some characters from the last line need to be removed.
563 // Vectors used to temporary store text-actors removed from the line.
564 // Three vectors are needed because text-actors are not removed in order
565 // but insert them in order is required to reuse them later.
566 std::vector<TextActor> removedTextActorsFromFirstWord;
567 std::vector<TextActor> removedTextActorsFromFirstLine;
569 // lineIndexEnd was increased to delete the last line if lines need to be merged.
570 // To access now the last line we need to decrease the index.
571 const std::size_t lineIndex = ( mergeLines ? textInfoIndicesEnd.mLineIndex - 1u : textInfoIndicesEnd.mLineIndex );
573 // Get the last line.
574 LineLayoutInfo& lineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + lineIndex ) );
576 // Check if is needed remove the whole word. (If the character index is pointing just after the end of the word)
577 const WordLayoutInfo& wordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
578 bool removeWholeWord = wordLayout.mCharactersLayoutInfo.size() == textInfoIndicesEnd.mCharacterIndex + 1u;
580 if( ( textInfoIndicesEnd.mWordIndex > 0u ) || ( removeWholeWord ) )
582 // Store text-actors before removing them.
583 CollectTextActorsFromWords( removedTextActorsFromFirstLine,
586 ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1u : textInfoIndicesEnd.mWordIndex );
588 // Remove extra words. (and current word if whole characters are removed)
589 RemoveWordsFromLine( 0u,
590 ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1u : textInfoIndicesEnd.mWordIndex,
591 layoutParameters.mLineHeightOffset,
595 if( !removeWholeWord )
597 // Only some characters of the word need to be deleted.
599 // After removing all extra words. The word with the characters to be removed is the first one.
600 WordLayoutInfo& wordLayout( *lineLayout.mWordsLayoutInfo.begin() );
602 // Store text-actors before removing them.
603 CollectTextActors( removedTextActorsFromFirstWord, wordLayout, 0u, textInfoIndicesEnd.mCharacterIndex + 1u );
605 const std::size_t wordNumberCharacters = wordLayout.mCharactersLayoutInfo.size();
606 RemoveCharactersFromWord( 0u,
607 textInfoIndicesEnd.mCharacterIndex + 1u,
610 // discount the removed number of characters.
611 const std::size_t removedNumberOfCharacters = ( wordNumberCharacters - wordLayout.mCharactersLayoutInfo.size() );
612 lineLayout.mNumberOfCharacters -= removedNumberOfCharacters;
614 UpdateLineLayoutInfo( lineLayout, layoutParameters.mLineHeightOffset );
616 // Insert the text-actors in order.
617 removedTextActorsFromEnd.insert( removedTextActorsFromEnd.end(), removedTextActorsFromFirstWord.begin(), removedTextActorsFromFirstWord.end() );
618 removedTextActorsFromEnd.insert( removedTextActorsFromEnd.end(), removedTextActorsFromFirstLine.begin(), removedTextActorsFromFirstLine.end() );
620 } // end delete text from different lines
621 else if( ( textInfoIndicesBegin.mLineIndex == textInfoIndicesEnd.mLineIndex ) && ( lineLayout.mNumberOfCharacters == numberOfCharacters ) )
623 // the whole line needs to be deleted.
624 ++textInfoIndicesEnd.mLineIndex; // will delete current line.
628 // deleted text is within the same line. (merge lines could be needed if the line separator character is deleted)
630 // Line which contains the characters to be deleted.
631 LineLayoutInfo& lineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesBegin.mLineIndex ) );
633 // Remove the characters from the line layout info. It returns whether the current line can be merged with the next one.
634 RemoveCharactersFromLineInfo( relayoutData,
638 textInfoIndicesBegin,
640 textInfoMergeIndicesBegin,
641 textInfoMergeIndicesEnd,
643 removedTextActorsFromBegin,
644 removedTextActorsFromEnd );
648 // Merges words pointed by textInfoMergeIndicesBegin.mWordIndex and textInfoMergeIndicesEnd.mWordIndex calculated previously.
649 DALI_ASSERT_DEBUG( ( textInfoMergeIndicesBegin.mWordIndex < lineLayout.mWordsLayoutInfo.size() ) && "TextViewProcessor::UpdateTextInfo (delete). Word index (begin) out of bounds." );
650 DALI_ASSERT_DEBUG( ( textInfoMergeIndicesEnd.mWordIndex < lineLayout.mWordsLayoutInfo.size() ) && "TextViewProcessor::UpdateTextInfo (delete). Word index (end) out of bounds." );
652 WordLayoutInfo& firstWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoMergeIndicesBegin.mWordIndex ) );
653 WordLayoutInfo& lastWordLayout( *( lineLayout.mWordsLayoutInfo.begin() + textInfoMergeIndicesEnd.mWordIndex ) );
655 MergeWord( firstWordLayout,
659 // Store text-actors before removing them.
660 const std::size_t endIndex = ( mergeWords && ( textInfoIndicesEnd.mWordIndex > 0u ) ) ? textInfoIndicesEnd.mWordIndex - 1u : textInfoIndicesEnd.mWordIndex; // text-actors from the last word may have been added in the merge above.
661 CollectTextActorsFromWords( removedTextActorsFromMid, lineLayout, textInfoIndicesBegin.mWordIndex, endIndex );
663 // Remove unwanted words using previously calculated indices. (including the last part of the merged word)
664 lineLayout.mWordsLayoutInfo.erase( lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex, lineLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex );
667 UpdateLineLayoutInfo( lineLayout, layoutParameters.mLineHeightOffset );
668 }// end delete text from same line.
672 // Merges lines pointed by textInfoMergeIndicesBegin.mLineIndex and textInfoMergeIndicesEnd.mLineIndex calculated previously.
674 LineLayoutInfo& firstLineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoMergeIndicesBegin.mLineIndex ) );
676 const LineLayoutInfo& lastLineLayout( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoMergeIndicesEnd.mLineIndex ) );
678 MergeLine( firstLineLayout,
682 // Store text-actors before removing them.
683 const std::size_t endIndex = ( mergeLines && ( textInfoIndicesEnd.mLineIndex > 0u ) ) ? textInfoIndicesEnd.mLineIndex - 1u : textInfoIndicesEnd.mLineIndex; // text-actors from the last line may have been added in the merge above.
684 CollectTextActorsFromLines( removedTextActorsFromMid,
685 relayoutData.mTextLayoutInfo,
686 textInfoIndicesBegin.mLineIndex,
689 // Remove unwanted lines using previously calculated indices. (including the last part of the merged line)
690 relayoutData.mTextLayoutInfo.mLinesLayoutInfo.erase( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesBegin.mLineIndex,
691 relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin() + textInfoIndicesEnd.mLineIndex );
694 UpdateTextLayoutInfo( relayoutData.mTextLayoutInfo );
696 // If the last character of the last line is a new line character, an empty line need to be added.
697 if( !relayoutData.mTextLayoutInfo.mLinesLayoutInfo.empty() )
699 const WordLayoutInfo lastWordLayout = GetLastWordLayoutInfo( *( relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end() - 1u ) );
701 if( LineSeparator == lastWordLayout.mType )
703 LineLayoutInfo lastLineLayout;
705 const CharacterLayoutInfo layoutInfo = GetLastCharacterLayoutInfo( lastWordLayout );
706 lastLineLayout.mSize.height = layoutInfo.mSize.height;
708 relayoutData.mTextLayoutInfo.mLinesLayoutInfo.push_back( lastLineLayout );
710 relayoutData.mTextLayoutInfo.mWholeTextSize.height += layoutInfo.mSize.height;
714 // Clear the text from the text-actors if required.
715 if( CLEAR_TEXT == clearText )
717 ClearText( removedTextActorsFromEnd );
718 ClearText( removedTextActorsFromMid );
719 ClearText( removedTextActorsFromBegin );
722 // Insert text-actors into the cache.
723 // Text-actors are inserted in reverse order to use first the first removed.
724 relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromEnd );
725 relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromMid );
726 relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromBegin );
729 void UpdateTextInfo( const std::size_t position,
730 const std::size_t numberOfCharacters,
731 const MarkupProcessor::StyledTextArray& text,
732 const TextView::LayoutParameters& layoutParameters,
733 TextView::RelayoutData& relayoutData )
735 // Replaces 'numberOfCharacters' of text starting from 'position' with the given text.
737 // TODO: Temporary implementation with remove and insert.
740 UpdateTextInfo( position,
744 KEEP_TEXT ); // Do not clear the text from the text-actors.
747 UpdateTextInfo( position,
753 void UpdateTextInfo( const float lineHeightOffset,
754 TextLayoutInfo& textLayoutInfo )
756 // Updates the space between lines with the new offset value.
758 float newTextHeight = 0.f;
760 for( LineLayoutInfoContainer::iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = textLayoutInfo.mLinesLayoutInfo.end();
764 LineLayoutInfo& lineLayoutInfo( *lineIt );
766 lineLayoutInfo.mSize.height += ( lineHeightOffset - lineLayoutInfo.mLineHeightOffset );
767 newTextHeight += lineLayoutInfo.mSize.height;
769 lineLayoutInfo.mLineHeightOffset = lineHeightOffset;
772 textLayoutInfo.mWholeTextSize.height = newTextHeight;
775 void UpdateTextInfo( const TextStyle& style,
776 const TextStyle::Mask mask,
777 TextView::RelayoutData& relayoutData )
779 // Change text style for all text-actors.
781 for( LineLayoutInfoContainer::iterator lineIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = relayoutData.mTextLayoutInfo.mLinesLayoutInfo.end();
785 LineLayoutInfo& line( *lineIt );
787 for( WordLayoutInfoContainer::iterator wordIt = line.mWordsLayoutInfo.begin(), wordEndIt = line.mWordsLayoutInfo.end();
791 WordLayoutInfo& word( *wordIt );
793 for( CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end();
794 characterIt != characterEndIt;
797 CharacterLayoutInfo& characterLayout( *characterIt );
799 characterLayout.mStyledText.mStyle.Copy( style, mask );
801 // Checks if the font family supports all glyphs. If not, chooses a most suitable one.
802 ChooseFontFamilyName( characterLayout.mStyledText );
804 // Mark the character to be set the new style into the text-actor.
805 characterLayout.mSetStyle = true;
811 void InitializeTextActorInfo( TextView::RelayoutData& relayoutData )
813 TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo;
814 if( textLayoutInfo.mLinesLayoutInfo.empty() )
816 // nothing to do if there is no lines.
820 std::size_t characterGlobalIndex = 0; // Index to the global character (within the whole text).
821 std::size_t lineLayoutInfoIndex = 0; // Index to the laid out line info.
822 const std::size_t lineLayoutInfoSize = relayoutData.mLines.size(); // Number or laid out lines.
823 bool lineLayoutEnd = false; // Whether lineLayoutInfoIndex points at the last laid out line.
824 bool glyphActorCreatedForLine = false; // Whether a renderable actor has been created for this line.
826 RenderableActor currentGlyphActor; // text-actor used when the edit mode is disabled.
827 TextStyle currentStyle; // style for the current text-actor.
828 Vector4 currentGradientColor; // gradient color for the current text-actor.
829 Vector2 currentStartPoint; // start point for the current text-actor.
830 Vector2 currentEndPoint; // end point for the current text-actor.
831 bool currentIsColorGlyph = false; // Whether current glyph is an emoticon.
833 std::vector<TextActor> textActorsToRemove; // Keep a vector of text-actors to be included into the cache.
835 for( LineLayoutInfoContainer::iterator lineIt = textLayoutInfo.mLinesLayoutInfo.begin(), lineEndIt = textLayoutInfo.mLinesLayoutInfo.end();
839 LineLayoutInfo& line( *lineIt );
841 for( WordLayoutInfoContainer::iterator wordIt = line.mWordsLayoutInfo.begin(), wordEndIt = line.mWordsLayoutInfo.end();
845 WordLayoutInfo& word( *wordIt );
847 for( CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end();
848 characterIt != characterEndIt;
851 CharacterLayoutInfo& characterLayout( *characterIt );
853 // Check if there is a new line.
854 const bool newLine = !lineLayoutEnd && ( characterGlobalIndex == relayoutData.mLines[lineLayoutInfoIndex].mCharacterGlobalIndex );
858 // Point to the next line.
859 ++lineLayoutInfoIndex;
860 if( lineLayoutInfoIndex >= lineLayoutInfoSize )
862 // Arrived at last line.
863 lineLayoutEnd = true; // Avoids access out of bounds in the relayoutData.mLines vector.
865 glyphActorCreatedForLine = false;
868 if( !characterLayout.mStyledText.mText.IsEmpty() )
870 // Do not create a glyph-actor if there is no text.
871 const Character character = characterLayout.mStyledText.mText[0]; // there are only one character per character layout.
873 if( characterLayout.mIsColorGlyph ||
874 !character.IsWhiteSpace() || // A new line character is also a white space.
875 ( character.IsWhiteSpace() && characterLayout.mStyledText.mStyle.IsUnderlineEnabled() ) )
877 // Do not create a glyph-actor if it's a white space (without underline) or a new line character.
879 // Creates one glyph-actor per each counsecutive group of characters, with the same style, per line, or if it's an emoticon.
881 if( !glyphActorCreatedForLine ||
882 characterLayout.mIsColorGlyph ||
883 ( characterLayout.mStyledText.mStyle != currentStyle ) ||
884 ( characterLayout.mGradientColor != currentGradientColor ) ||
885 ( characterLayout.mStartPoint != currentStartPoint ) ||
886 ( characterLayout.mEndPoint != currentEndPoint ) ||
887 ( characterLayout.mIsColorGlyph != currentIsColorGlyph ) )
889 characterLayout.mSetText = false;
890 characterLayout.mSetStyle = false;
892 // There is a new style or a new line.
893 glyphActorCreatedForLine = true;
895 if( characterLayout.mIsColorGlyph )
897 ImageActor imageActor = ImageActor::DownCast( characterLayout.mGlyphActor );
900 characterLayout.mGlyphActor = ImageActor::New();
901 characterLayout.mSetText = true;
906 TextActor textActor = TextActor::DownCast( characterLayout.mGlyphActor );
910 // Try to reuse first the text-actor of this character.
911 textActor.SetTextStyle( characterLayout.mStyledText.mStyle );
912 currentGlyphActor = textActor;
916 // If there is no text-actor, try to retrieve one from the cache.
917 textActor = relayoutData.mTextActorCache.RetrieveTextActor();
919 // If still there is no text-actor, create one.
922 TextActorParameters parameters( characterLayout.mStyledText.mStyle, TextActorParameters::FONT_DETECTION_OFF );
923 textActor = TextActor::New( NULL, parameters );
927 textActor.SetTextStyle( characterLayout.mStyledText.mStyle );
930 currentGlyphActor = textActor;
932 characterLayout.mGlyphActor = currentGlyphActor;
935 // Update style to be checked with next characters.
936 currentStyle = characterLayout.mStyledText.mStyle;
937 currentGradientColor = characterLayout.mGradientColor;
938 currentStartPoint = characterLayout.mStartPoint;
939 currentEndPoint = characterLayout.mEndPoint;
940 currentIsColorGlyph = characterLayout.mIsColorGlyph;
942 characterLayout.mGlyphActor.SetParentOrigin( ParentOrigin::TOP_LEFT );
943 characterLayout.mGlyphActor.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT );
947 DALI_ASSERT_DEBUG( !characterLayout.mIsColorGlyph && "TextViewProcessor::InitializeTextActorInfo. An image-actor doesn't store more than one emoticon." );
949 // Same style than previous one.
950 TextActor textActor = TextActor::DownCast( characterLayout.mGlyphActor );
953 // There is a previously created text-actor for this character.
954 // If this character has another one put it into the cache.
955 textActor.SetText( "" );
956 textActorsToRemove.push_back( textActor );
959 if( characterLayout.mGlyphActor )
961 characterLayout.mGlyphActor.Reset();
964 } // no white space / new line char
967 ++characterGlobalIndex;
972 // Insert the spare text-actors into the cache.
973 relayoutData.mTextActorCache.InsertTextActors( textActorsToRemove );
976 } // namespace TextViewProcessor
978 } // namespace Internal
980 } // namespace Toolkit