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/integration-api/debug.h>
23 #include <dali-toolkit/internal/controls/text-view/text-view-word-processor.h>
24 #include <dali-toolkit/internal/controls/text-view/text-view-paragraph-processor.h>
25 #include <dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h>
26 #include <dali-toolkit/internal/controls/text-view/text-processor.h>
27 #include <dali-toolkit/internal/controls/text-view/text-processor-bidirectional-info.h>
38 namespace TextViewProcessor
44 * Update text layout info.
46 * Updates the size of the whole text, the maximum width of all words and the number of characters.
48 * @param[in,out] textLayoutInfo
50 void UpdateLayoutInfo( TextLayoutInfo& textLayoutInfo )
52 // Initialize members to be updated.
53 textLayoutInfo.mWholeTextSize = Size::ZERO;
54 textLayoutInfo.mMaxWordWidth = 0.f;
55 textLayoutInfo.mNumberOfCharacters = 0u;
57 // Traverse all text updating values.
58 for( ParagraphLayoutInfoContainer::const_iterator paragraphIt = textLayoutInfo.mParagraphsLayoutInfo.begin(), paragraphEndIt = textLayoutInfo.mParagraphsLayoutInfo.end();
59 paragraphIt != paragraphEndIt;
62 const ParagraphLayoutInfo& paragraph( *paragraphIt );
64 // Updates text size with the size of all paragraphs.
65 UpdateSize( textLayoutInfo.mWholeTextSize, paragraph.mSize, GrowHeight );
67 // Updates number of characters.
68 textLayoutInfo.mNumberOfCharacters += paragraph.mNumberOfCharacters;
70 // Updates the max word's width.
71 for( WordLayoutInfoContainer::const_iterator wordIt = paragraph.mWordsLayoutInfo.begin(), wordEndIt = paragraph.mWordsLayoutInfo.end();
75 const WordLayoutInfo& word( *wordIt );
77 textLayoutInfo.mMaxWordWidth = std::max( textLayoutInfo.mMaxWordWidth, word.mSize.width );
84 // Constructors and assignment operators
86 TextInfoIndices::TextInfoIndices()
87 : mParagraphIndex( 0u ),
89 mCharacterIndex( 0u ),
90 mCharacterParagraphIndex( 0u )
94 TextInfoIndices::TextInfoIndices( const std::size_t paragraphIndex,
95 const std::size_t wordIndex,
96 const std::size_t characterIndex )
97 : mParagraphIndex( paragraphIndex ),
98 mWordIndex( wordIndex ),
99 mCharacterIndex( characterIndex ),
100 mCharacterParagraphIndex( 0u )
104 bool TextInfoIndices::operator==( const TextInfoIndices& indices ) const
106 return ( ( mParagraphIndex == indices.mParagraphIndex ) &&
107 ( mWordIndex == indices.mWordIndex ) &&
108 ( mCharacterIndex == indices.mCharacterIndex ) &&
109 ( mCharacterParagraphIndex == indices.mCharacterParagraphIndex ) );
112 /////////////////////
114 /////////////////////
116 TextLayoutInfo::TextLayoutInfo()
118 mMaxWordWidth( 0.f ),
119 mMaxItalicsOffset( 0.f ),
120 mNumberOfCharacters( 0u ),
121 mParagraphsLayoutInfo(),
122 mEllipsizeLayoutInfo(),
123 mEllipsisText( "..." ),
124 mEllipsisTextStyles()
126 // Sets default styles for the default ellipsis text.
127 mEllipsisTextStyles.PushBack( new TextStyle() );
128 mEllipsisTextStyles.PushBack( new TextStyle() );
129 mEllipsisTextStyles.PushBack( new TextStyle() );
132 TextLayoutInfo::~TextLayoutInfo()
137 TextLayoutInfo::TextLayoutInfo( const TextLayoutInfo& text )
138 : mWholeTextSize( text.mWholeTextSize ),
139 mMaxWordWidth( text.mMaxWordWidth ),
140 mMaxItalicsOffset( text.mMaxItalicsOffset ),
141 mNumberOfCharacters( text.mNumberOfCharacters ),
142 mParagraphsLayoutInfo( text.mParagraphsLayoutInfo ),
143 mEllipsizeLayoutInfo( text.mEllipsizeLayoutInfo ),
144 mEllipsisText( text.mEllipsisText ),
145 mEllipsisTextStyles()
147 for( Vector<TextStyle*>::ConstIterator it = text.mEllipsisTextStyles.Begin(), endIt = text.mEllipsisTextStyles.End(); it != endIt; ++it )
149 mEllipsisTextStyles.PushBack( new TextStyle( *(*it) ) );
153 TextLayoutInfo& TextLayoutInfo::operator=( const TextLayoutInfo& text )
157 mWholeTextSize = text.mWholeTextSize;
158 mMaxWordWidth = text.mMaxWordWidth;
159 mMaxItalicsOffset = text.mMaxItalicsOffset;
160 mNumberOfCharacters = text.mNumberOfCharacters;
161 mParagraphsLayoutInfo = text.mParagraphsLayoutInfo;
162 mEllipsizeLayoutInfo = text.mEllipsizeLayoutInfo;
163 mEllipsisText = text.mEllipsisText;
167 for( Vector<TextStyle*>::ConstIterator it = text.mEllipsisTextStyles.Begin(), endIt = text.mEllipsisTextStyles.End(); it != endIt; ++it )
169 mEllipsisTextStyles.PushBack( new TextStyle( *(*it) ) );
175 void TextLayoutInfo::ClearStyles()
177 for( Vector<TextStyle*>::Iterator it = mEllipsisTextStyles.Begin(), endIt = mEllipsisTextStyles.End(); it != endIt; ++it )
181 mEllipsisTextStyles.Clear();
184 /////////////////////////////////////////////////////////////////////////////////////////////
186 void CreateTextInfo( const MarkupProcessor::StyledTextArray& text,
187 const TextView::LayoutParameters& layoutParameters,
188 TextView::RelayoutData& relayoutData )
190 // * Traverse the given text spliting it in paragraphs and each paragraph in words.
191 // * White spaces and new paragraph characters are alone in one word.
192 // * Bidirectional text is processed in each paragraph.
193 // It creates the conversion tables
194 // It does the ligatures if there is arabic glyphs.
195 // It doesn't reorder the text as it must be done for each line not for the paragraph.
196 // * Generates a layout data structure to store layout information (size, position, ascender, text direction, etc) and metrics of all characters.
197 // * Store text for each paragraph and the style for each character.
199 // Collect previously created text-actors.
200 std::vector<TextActor> textActors;
201 CollectTextActorsFromParagraphs( textActors, relayoutData.mTextLayoutInfo, 0u, relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.size() );
203 if( !textActors.empty() )
205 // Add text-actors to the cache.
206 relayoutData.mTextActorCache.InsertTextActors( textActors );
207 relayoutData.mTextActorCache.ClearTexts();
210 // Store the ellipsize layout info before clearing the previous created info.
211 // TODO: fix this. Don't clear the ellipsis layout, store it somewhere else ...
212 const WordLayoutInfo ellipsizeInfo = relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo;
213 Dali::Text ellipsisText = relayoutData.mTextLayoutInfo.mEllipsisText;
214 Vector<TextStyle*> ellipsisTextStyles = relayoutData.mTextLayoutInfo.mEllipsisTextStyles;
215 relayoutData.mTextLayoutInfo.mEllipsisTextStyles.Clear();
217 // clear previously created info.
218 relayoutData.mTextLayoutInfo = TextLayoutInfo();
219 relayoutData.mCharacterLogicalToVisualMap.clear();
220 relayoutData.mCharacterVisualToLogicalMap.clear();
222 // Sets the ellipsize layout info.
223 relayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = ellipsizeInfo;
224 relayoutData.mTextLayoutInfo.mEllipsisText = ellipsisText;
225 relayoutData.mTextLayoutInfo.mEllipsisTextStyles = ellipsisTextStyles;
227 // Split the whole text in paragraphs.
228 // It returns a vector of Text with all the paragraphs.
229 // and for each paragraph a vector of styles per character. TODO: don't create a style per character.
230 std::vector<Text> paragraphs;
231 std::vector< Vector<TextStyle*> > styles;
232 TextProcessor::SplitInParagraphs( text,
236 // Reserve space for the current number of paragraphs.
237 relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.resize( paragraphs.size(), ParagraphLayoutInfo() );
239 // Traverse all paragraphs
240 std::size_t paragraphIndex = 0u;
241 std::vector<Text>::iterator paragraphIt, paragraphEndIt;
242 std::vector< Vector<TextStyle*> >::const_iterator styleIt, styleEndIt;
243 for( paragraphIt = paragraphs.begin(), paragraphEndIt = paragraphs.end(),
244 styleIt = styles.begin(), styleEndIt = styles.end();
245 ( paragraphIt != paragraphEndIt ) && ( styleIt != styleEndIt );
250 // Gets the paragraph and the styles for each character.
251 Text& paragraph( *paragraphIt );
252 const Vector<TextStyle*>& textStyles( *styleIt );
254 // Retrieve the data structure for the current paragraph.
255 ParagraphLayoutInfo& paragraphLayoutInfo = *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + paragraphIndex );
257 // Sets text and styles.
258 paragraphLayoutInfo.mText = paragraph;
259 paragraphLayoutInfo.mTextStyles = textStyles;
261 // Fills the paragraph data structure with the layout info.
263 if( !paragraph.IsEmpty() )
265 CreateParagraphInfo( relayoutData,
266 paragraphLayoutInfo );
268 // do not add the line offset if the paragraph has no characters.
269 paragraphLayoutInfo.mSize.height += layoutParameters.mLineHeightOffset;
270 paragraphLayoutInfo.mLineHeightOffset = layoutParameters.mLineHeightOffset;
274 // This paragraph has no text. i.e. the previous paragraph ends with '\n'.
275 // Even though, line height needs to be set in order to align the whole text correctly.
277 float lineHeight = 0.f;
278 // Get the last character of the last paragraph.
279 if( 0u < paragraphIndex )
281 const ParagraphLayoutInfo& paragraphInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + ( paragraphIndex - 1u ) ) );
283 const CharacterLayoutInfo characterInfo = GetLastCharacterLayoutInfo( paragraphInfo );
285 lineHeight = characterInfo.mSize.height;
288 paragraphLayoutInfo.mSize.height = lineHeight;
291 // Fills the conversion tables used to get the logical or visual position of a character is case there is right to left text.
292 // If there is right to left text, it needs to be updated every time the text is relaid-out.
293 for( std::size_t index = 0u; index < paragraphLayoutInfo.mNumberOfCharacters; ++index )
295 relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
296 relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
299 // Update layout info for the whole text.
300 UpdateSize( relayoutData.mTextLayoutInfo.mWholeTextSize, paragraphLayoutInfo.mSize, GrowHeight );
301 relayoutData.mTextLayoutInfo.mNumberOfCharacters += paragraphLayoutInfo.mNumberOfCharacters;
302 } // end of paragraphs
305 void UpdateTextInfo( std::size_t position,
306 const MarkupProcessor::StyledTextArray& text,
307 const TextView::LayoutParameters& layoutParameters,
308 TextView::RelayoutData& relayoutData )
310 // Update current internal data structure with added text.
312 // * Creates layout info for the given text.
313 // * With the given position, find where to add the text.
314 // * If the new text is not added at the end of current text, a paragraph need to be split.
315 // * Merge the last paragraph of the new text to the last part or the split paragraph.
316 // * Add paragraphs between first and last of the new text.
317 // * Merge the first part of the split paragraph with the first paragraph of the new text.
318 // * Update the layout info for the whole text.
324 // nothing to do if the input text is empty.
328 if( 0u == relayoutData.mTextLayoutInfo.mNumberOfCharacters )
330 // Current text is empty. There is no need to update current data structure,
331 // just create a new one with the new input text.
332 CreateTextInfo( text,
339 if( position > relayoutData.mTextLayoutInfo.mNumberOfCharacters )
341 // Asserts if text is to be added out of bounds.
342 DALI_ASSERT_ALWAYS( !"TextViewProcessor::UpdateTextInfo (insert). Trying to insert text out of bounds." );
347 // If in the insertion is involved right to left text,
348 // check the neighbour characters is needed.
349 // i.e. Arabic characters may have four different glyphs (isolated, end, middle, begin).
350 // So check the neighbours on each side is needed in order to render
351 // the correct ligatures.
354 TextView::RelayoutData relayoutDataForNewText;
356 // Creates layout info for the given text.
357 // It doesn't create text-actors as text could be added to an existing one.
358 CreateTextInfo( text,
360 relayoutDataForNewText );
362 // Fills the conversion tables used to get the logical or visual position of a character is case there is right to left text.
363 // If there is right to left text, it needs to be updated every time the text is relaid-out.
364 std::size_t index = 0u;
365 for( std::size_t i = 0u; i < relayoutDataForNewText.mTextLayoutInfo.mNumberOfCharacters; ++i )
367 relayoutData.mCharacterLogicalToVisualMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
368 relayoutData.mCharacterVisualToLogicalMap.push_back( relayoutData.mTextLayoutInfo.mNumberOfCharacters + index );
372 // If a paragraph is split, it stores the last part of the paragraph.
373 ParagraphLayoutInfo lastParagraphLayoutInfo;
375 // Stores indices to the paragraph, word and character of the given position.
376 TextInfoIndices textInfoIndices;
378 if( position < relayoutData.mTextLayoutInfo.mNumberOfCharacters )
380 // Get paragraph, word and character indices for given position.
381 GetIndicesFromGlobalCharacterIndex( position,
382 relayoutData.mTextLayoutInfo,
385 // 1) Split the paragraph
387 // Split a paragraph in two is needed, then merge the first part of the split paragraph
388 // with the first paragraph of the new text, add subsequent paragraphs and merge the last paragraph
389 // of the new text with the last part of the split paragraph.
391 // Implementation notes!
393 // These references to the first paragraph are declared in this scope because if new paragraphs are inserted in step 2,
394 // they become invalid, making the algorithm to crash if used again.
395 // In the step 3, references to the first paragraph are needed and declared again.
397 // Stores the first part of the split paragraph.
398 ParagraphLayoutInfo& firstParagraphLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndices.mParagraphIndex ) );
400 SplitParagraph( textInfoIndices,
401 PointSize( layoutParameters.mLineHeightOffset ),
402 firstParagraphLayoutInfo,
403 lastParagraphLayoutInfo );
407 // Position is just after the last character.
408 // Calculates indices for that position.
409 if( !relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.empty() )
411 textInfoIndices.mParagraphIndex = relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.size() - 1u;
412 const ParagraphLayoutInfo& paragraphLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.end() - 1u ) );
414 if( !paragraphLayoutInfo.mWordsLayoutInfo.empty() )
416 textInfoIndices.mWordIndex = paragraphLayoutInfo.mWordsLayoutInfo.size() - 1u;
418 const WordLayoutInfo& wordLayoutInfo( *( paragraphLayoutInfo.mWordsLayoutInfo.end() - 1u ) );
419 textInfoIndices.mCharacterIndex = wordLayoutInfo.mCharactersLayoutInfo.size();
421 // Sets the character index within the paragraph.
422 textInfoIndices.mCharacterParagraphIndex = 0u;
423 for( WordLayoutInfoContainer::const_iterator it = paragraphLayoutInfo.mWordsLayoutInfo.begin(), endIt = paragraphLayoutInfo.mWordsLayoutInfo.end(); it != endIt; ++it )
425 textInfoIndices.mCharacterParagraphIndex += wordLayoutInfo.mCharactersLayoutInfo.size();
431 // 2) If the new text has more than 1 paragraph, merge the last paragraph of the input text with the last part of the split paragraph.
432 //TODO check this cases ( num paragraphs ==1, >1, >2 ) if it could be simplified.
433 if( relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.size() > 1u )
435 ParagraphLayoutInfo& lastInputParagraphLayoutInfo( *( relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.end() - 1u ) );
437 MergeParagraph( lastInputParagraphLayoutInfo,
438 lastParagraphLayoutInfo );
440 if( relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.size() > 2u )
442 // Insert all paragraphs except first and last in the text.
443 relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.insert( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndices.mParagraphIndex + 1u,
444 relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + 1u,
445 relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.end() - 1u );
448 // Insert the last paragraph to the text
449 relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.insert( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndices.mParagraphIndex + relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.size() - 1u,
450 lastInputParagraphLayoutInfo );
454 // Merge the new paragraph to the last part of the split paragraph.
455 ParagraphLayoutInfo& inputParagraphLayoutInfo( *relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.begin() );
457 MergeParagraph( inputParagraphLayoutInfo,
458 lastParagraphLayoutInfo );
461 // 3) Merge the first paragraph of the split text with the first paragraph of the input text.
462 ParagraphLayoutInfo& firstParagraphLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndices.mParagraphIndex ) );
463 ParagraphLayoutInfo& firstInputParagraphLayoutInfo( *relayoutDataForNewText.mTextLayoutInfo.mParagraphsLayoutInfo.begin() );
465 MergeParagraph( firstParagraphLayoutInfo,
466 firstInputParagraphLayoutInfo );
468 // 4) Update text info.
470 // Updates the whole text size, maximum word size, etc.
471 UpdateLayoutInfo( relayoutData.mTextLayoutInfo );
474 void UpdateTextInfo( std::size_t position,
475 std::size_t numberOfCharacters,
476 const TextView::LayoutParameters& layoutParameters,
477 TextView::RelayoutData& relayoutData,
478 const TextOperationOnRemove clearText )
480 // Removes 'numberOfCharacters' starting from 'position'.
482 // * It checks if text to be deleted is in the same paragraph or not:
483 // * If it is not, check which paragraphs need to be split/merged or deleted.
484 // * If it is but all characters of the paragraph are going to be deleted, just delete the paragraph (nothing needs to be split/merged)
485 // * If only some characters of the same paragraph are going to be deleted, proceed similarly: check if text to be deleted is in the same word.
486 // * If it is not, split/merge words.
487 // * Check if the whole word needs to be deleted.
488 // * Check if only some characters of the word need to be deleted.
489 // * Updates layout info.
491 // * The algorithm checks if a word separator is deleted (in that case, different words need to be merged) and if a new paragraph separator is deleted (two paragraphs need to be merged).
495 if( 0u == numberOfCharacters )
497 DALI_ASSERT_DEBUG( !"TextViewProcessor::UpdateTextInfo. WARNING: trying to delete 0 characters!" );
499 // nothing to do if no characters are deleted.
503 // Asserts if trying to delete text out of bounds.
504 DALI_ASSERT_ALWAYS( position + numberOfCharacters <= relayoutData.mTextLayoutInfo.mNumberOfCharacters && "TextViewProcessor::UpdateTextInfo. ERROR: trying to delete characters out of boundary" );
506 // Remove characters from character to visual map and vs.
507 // If there is right to left text, it needs to be updated every time the text is relaid-out.
508 relayoutData.mCharacterLogicalToVisualMap.erase( relayoutData.mCharacterLogicalToVisualMap.end() - numberOfCharacters, relayoutData.mCharacterLogicalToVisualMap.end() );
509 relayoutData.mCharacterVisualToLogicalMap.erase( relayoutData.mCharacterVisualToLogicalMap.end() - numberOfCharacters, relayoutData.mCharacterVisualToLogicalMap.end() );
511 // Get paragraph, word and character indices for the given start position.
512 TextInfoIndices textInfoIndicesBegin;
513 GetIndicesFromGlobalCharacterIndex( position,
514 relayoutData.mTextLayoutInfo,
515 textInfoIndicesBegin );
517 // Get paragraph, word and character indices for the given end position (start position + number of characters to be deleted).
518 TextInfoIndices textInfoIndicesEnd;
519 GetIndicesFromGlobalCharacterIndex( position + numberOfCharacters - 1u,
520 relayoutData.mTextLayoutInfo,
521 textInfoIndicesEnd );
523 // Vectors used to temporary store text-actors removed from text.
524 // Three vectors are needed because text-actors are not removed in order
525 // but insert them in order is required to reuse them later.
526 std::vector<TextActor> removedTextActorsFromBegin;
527 std::vector<TextActor> removedTextActorsFromMid;
528 std::vector<TextActor> removedTextActorsFromEnd;
530 // Whether paragraphs and words need to be merged.
531 bool mergeParagraphs = false;
532 bool mergeWords = false;
534 // Indices of the paragraphs and words to be merged.
535 TextInfoIndices textInfoMergeIndicesBegin;
536 TextInfoIndices textInfoMergeIndicesEnd;
538 const ParagraphLayoutInfo& paragraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesBegin.mParagraphIndex ) ); // used to check the number of characters of the paragraph
539 // if all characters to be deleted are in the same paragraph.
540 if( textInfoIndicesBegin.mParagraphIndex < textInfoIndicesEnd.mParagraphIndex )
542 // Deleted text is from different paragraphs. It may need to split two paragraphs, and merge first part of the first one with last part of the last one.
544 // whether first or last paragraph need to be split and merged with the last part.
545 bool mergeFirstParagraph = false;
546 bool mergeLastParagraph = true;
548 textInfoMergeIndicesBegin.mParagraphIndex = textInfoIndicesBegin.mParagraphIndex;
549 textInfoMergeIndicesEnd.mParagraphIndex = textInfoIndicesEnd.mParagraphIndex;
551 if( ( textInfoIndicesBegin.mWordIndex > 0u ) || ( textInfoIndicesBegin.mCharacterIndex > 0u ) )
553 // first character to be deleted is not the first one of the current paragraph.
554 ++textInfoIndicesBegin.mParagraphIndex; // won't delete current paragraph
556 // As some characters remain, this paragraph could be merged with the last one.
557 mergeFirstParagraph = true;
560 // Check if all characters of the last paragraph are going to be deleted.
561 bool wholeParagraphDeleted = false;
562 const ParagraphLayoutInfo& lastParagraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesEnd.mParagraphIndex ) );
563 if( textInfoIndicesEnd.mWordIndex + 1u == lastParagraphLayout.mWordsLayoutInfo.size() )
565 const WordLayoutInfo& lastWordLayout( *( lastParagraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
566 if( textInfoIndicesEnd.mCharacterIndex + 1u == lastWordLayout.mCharactersLayoutInfo.size() )
568 // All characters of the last paragraph are going to be deleted.
569 ++textInfoIndicesEnd.mParagraphIndex; // will delete the last paragraph.
571 // the whole last paragraph is deleted. Need to check if the next paragraph could be merged.
572 mergeLastParagraph = false;
573 wholeParagraphDeleted = true;
577 if( wholeParagraphDeleted )
579 // It means the whole last paragraph is deleted completely.
580 // It's needed to check if there is another paragraph after that could be merged.
581 if( textInfoIndicesEnd.mParagraphIndex < relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.size() )
583 mergeLastParagraph = true;
585 // Point the first characters of the next paragraph.
586 textInfoIndicesEnd.mWordIndex = 0u;
587 textInfoIndicesEnd.mCharacterIndex = 0u;
588 textInfoMergeIndicesEnd.mParagraphIndex = textInfoIndicesEnd.mParagraphIndex;
592 // If some characters remain in the first and last paragraph, they need to be merged.
593 mergeParagraphs = mergeFirstParagraph && mergeLastParagraph;
595 if( mergeParagraphs )
597 // last paragraph is going to be merged with the first one, so is not needed.
598 ++textInfoIndicesEnd.mParagraphIndex; // will delete the last paragraph.
601 if( mergeFirstParagraph )
603 // Remove characters from the first paragraph.
605 // Vectors used to temporary store text-actors removed from the paragraph.
606 // Three vectors are needed because text-actors are not removed in order
607 // but insert them in order is required to reuse them later.
608 std::vector<TextActor> removedTextActorsFromFirstWord;
609 std::vector<TextActor> removedTextActorsFromFirstParagraph;
611 // As paragraphIndexBegin has been increased just to not to remove the paragraph, decrease now is needed to access it.
612 ParagraphLayoutInfo& paragraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesBegin.mParagraphIndex - 1u ) );
614 // Remove the characters from the text and the styles.
615 paragraphLayout.mText.Remove( textInfoIndicesBegin.mCharacterParagraphIndex, paragraphLayout.mNumberOfCharacters - textInfoIndicesBegin.mCharacterParagraphIndex );
617 for( Vector<TextStyle*>::Iterator it = paragraphLayout.mTextStyles.Begin() + textInfoIndicesBegin.mCharacterParagraphIndex,
618 endIt = paragraphLayout.mTextStyles.End();
624 paragraphLayout.mTextStyles.Erase( paragraphLayout.mTextStyles.Begin() + textInfoIndicesBegin.mCharacterParagraphIndex,
625 paragraphLayout.mTextStyles.End() );
627 if( ( textInfoIndicesBegin.mWordIndex + 1u < paragraphLayout.mWordsLayoutInfo.size() ) || ( 0u == textInfoIndicesBegin.mCharacterIndex ) )
629 // Remove extra words within current paragraph. (and current word if whole characters are removed)
630 // 0 == characterIndexBegin means the whole word is deleted.
631 const std::size_t wordIndex = ( ( 0u == textInfoIndicesBegin.mCharacterIndex ) ? textInfoIndicesBegin.mWordIndex : textInfoIndicesBegin.mWordIndex + 1u );
633 // Store text-actors before removing them.
634 CollectTextActorsFromWords( removedTextActorsFromFirstParagraph, paragraphLayout, wordIndex, paragraphLayout.mWordsLayoutInfo.size() );
636 RemoveWordsFromParagraph( wordIndex,
637 paragraphLayout.mWordsLayoutInfo.size() - wordIndex,
638 layoutParameters.mLineHeightOffset,
642 if( ( textInfoIndicesBegin.mWordIndex < paragraphLayout.mWordsLayoutInfo.size() ) && ( textInfoIndicesBegin.mCharacterIndex > 0u ) )
644 // Only some characters of the word need to be removed.
645 WordLayoutInfo& wordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
647 // Store text-actors before removing them.
648 CollectTextActors( removedTextActorsFromFirstWord, wordLayout, textInfoIndicesBegin.mCharacterIndex, wordLayout.mCharactersLayoutInfo.size() );
650 const std::size_t wordNumberCharacters = wordLayout.mCharactersLayoutInfo.size();
651 RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
652 wordLayout.mCharactersLayoutInfo.size() - textInfoIndicesBegin.mCharacterIndex,
655 // discount the removed number of characters.
656 const std::size_t removedNumberOfCharacters = ( wordNumberCharacters - wordLayout.mCharactersLayoutInfo.size() );
657 paragraphLayout.mNumberOfCharacters -= removedNumberOfCharacters;
659 UpdateLayoutInfo( paragraphLayout, layoutParameters.mLineHeightOffset );
661 // Insert the text-actors in order.
662 removedTextActorsFromBegin.insert( removedTextActorsFromBegin.end(), removedTextActorsFromFirstWord.begin(), removedTextActorsFromFirstWord.end() );
663 removedTextActorsFromBegin.insert( removedTextActorsFromBegin.end(), removedTextActorsFromFirstParagraph.begin(), removedTextActorsFromFirstParagraph.end() );
666 if( mergeLastParagraph && !wholeParagraphDeleted )
668 // Some characters from the last paragraph need to be removed.
670 // Vectors used to temporary store text-actors removed from the paragraph.
671 // Three vectors are needed because text-actors are not removed in order
672 // but insert them in order is required to reuse them later.
673 std::vector<TextActor> removedTextActorsFromFirstWord;
674 std::vector<TextActor> removedTextActorsFromFirstParagraph;
676 // paragraphIndexEnd was increased to delete the last paragraph if paragraphs need to be merged.
677 // To access now the last paragraph we need to decrease the index.
678 const std::size_t paragraphIndex = ( mergeParagraphs ? textInfoIndicesEnd.mParagraphIndex - 1u : textInfoIndicesEnd.mParagraphIndex );
680 // Get the last paragraph.
681 ParagraphLayoutInfo& paragraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + paragraphIndex ) );
683 // Remove the characters from the text and the styles.
684 const std::size_t lastCharacterIndex = textInfoIndicesEnd.mCharacterParagraphIndex + 1u;
686 paragraphLayout.mText.Remove( 0u, lastCharacterIndex );
688 for( Vector<TextStyle*>::Iterator it = paragraphLayout.mTextStyles.Begin(),
689 endIt = paragraphLayout.mTextStyles.Begin() + lastCharacterIndex;
695 paragraphLayout.mTextStyles.Erase( paragraphLayout.mTextStyles.Begin(),
696 paragraphLayout.mTextStyles.Begin() + lastCharacterIndex );
698 // Check if is needed remove the whole word. (If the character index is pointing just after the end of the word)
699 const WordLayoutInfo& wordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex ) );
700 bool removeWholeWord = wordLayout.mCharactersLayoutInfo.size() == textInfoIndicesEnd.mCharacterIndex + 1u;
702 if( ( textInfoIndicesEnd.mWordIndex > 0u ) || ( removeWholeWord ) )
704 // Store text-actors before removing them.
705 CollectTextActorsFromWords( removedTextActorsFromFirstParagraph,
708 ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1u : textInfoIndicesEnd.mWordIndex );
710 // Remove extra words. (and current word if whole characters are removed)
711 RemoveWordsFromParagraph( 0u,
712 ( removeWholeWord ) ? textInfoIndicesEnd.mWordIndex + 1u : textInfoIndicesEnd.mWordIndex,
713 layoutParameters.mLineHeightOffset,
717 if( !removeWholeWord )
719 // Only some characters of the word need to be deleted.
721 // After removing all extra words. The word with the characters to be removed is the first one.
722 WordLayoutInfo& wordLayout( *paragraphLayout.mWordsLayoutInfo.begin() );
724 // Store text-actors before removing them.
725 CollectTextActors( removedTextActorsFromFirstWord, wordLayout, 0u, textInfoIndicesEnd.mCharacterIndex + 1u );
727 const std::size_t wordNumberCharacters = wordLayout.mCharactersLayoutInfo.size();
728 RemoveCharactersFromWord( 0u,
729 textInfoIndicesEnd.mCharacterIndex + 1u,
732 // discount the removed number of characters.
733 const std::size_t removedNumberOfCharacters = ( wordNumberCharacters - wordLayout.mCharactersLayoutInfo.size() );
734 paragraphLayout.mNumberOfCharacters -= removedNumberOfCharacters;
736 UpdateLayoutInfo( paragraphLayout, layoutParameters.mLineHeightOffset );
738 // Insert the text-actors in order.
739 removedTextActorsFromEnd.insert( removedTextActorsFromEnd.end(), removedTextActorsFromFirstWord.begin(), removedTextActorsFromFirstWord.end() );
740 removedTextActorsFromEnd.insert( removedTextActorsFromEnd.end(), removedTextActorsFromFirstParagraph.begin(), removedTextActorsFromFirstParagraph.end() );
742 } // end delete text from different paragraphs
743 else if( ( textInfoIndicesBegin.mParagraphIndex == textInfoIndicesEnd.mParagraphIndex ) && ( paragraphLayout.mNumberOfCharacters == numberOfCharacters ) )
745 // the whole paragraph needs to be deleted.
746 ++textInfoIndicesEnd.mParagraphIndex; // will delete current paragraph.
750 // deleted text is within the same paragraph. (merge paragraphs could be needed if the paragraph separator character is deleted)
752 // Paragraph which contains the characters to be deleted.
753 ParagraphLayoutInfo& paragraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesBegin.mParagraphIndex ) );
755 // Remove the characters from the text and the styles.
756 const std::size_t firstCharacterIndex = textInfoIndicesBegin.mCharacterParagraphIndex;
757 const std::size_t lastCharacterIndex = textInfoIndicesEnd.mCharacterParagraphIndex + 1u;
759 paragraphLayout.mText.Remove( firstCharacterIndex, lastCharacterIndex - firstCharacterIndex );
761 for( Vector<TextStyle*>::Iterator it = paragraphLayout.mTextStyles.Begin() + firstCharacterIndex,
762 endIt = paragraphLayout.mTextStyles.Begin() + lastCharacterIndex;
768 paragraphLayout.mTextStyles.Erase( paragraphLayout.mTextStyles.Begin() + firstCharacterIndex,
769 paragraphLayout.mTextStyles.Begin() + lastCharacterIndex );
771 // Remove the characters from the paragraph layout info. It returns whether the current paragraph can be merged with the next one.
772 RemoveCharactersFromParagraphInfo( relayoutData,
776 textInfoIndicesBegin,
778 textInfoMergeIndicesBegin,
779 textInfoMergeIndicesEnd,
781 removedTextActorsFromBegin,
782 removedTextActorsFromEnd );
786 // Merges words pointed by textInfoMergeIndicesBegin.mWordIndex and textInfoMergeIndicesEnd.mWordIndex calculated previously.
787 DALI_ASSERT_DEBUG( ( textInfoMergeIndicesBegin.mWordIndex < paragraphLayout.mWordsLayoutInfo.size() ) && "TextViewProcessor::UpdateTextInfo (delete). Word index (begin) out of bounds." );
788 DALI_ASSERT_DEBUG( ( textInfoMergeIndicesEnd.mWordIndex < paragraphLayout.mWordsLayoutInfo.size() ) && "TextViewProcessor::UpdateTextInfo (delete). Word index (end) out of bounds." );
790 WordLayoutInfo& firstWordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoMergeIndicesBegin.mWordIndex ) );
791 WordLayoutInfo& lastWordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoMergeIndicesEnd.mWordIndex ) );
793 MergeWord( firstWordLayout,
797 // Store text-actors before removing them.
798 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.
799 CollectTextActorsFromWords( removedTextActorsFromMid, paragraphLayout, textInfoIndicesBegin.mWordIndex, endIndex );
801 // Remove unwanted words using previously calculated indices. (including the last part of the merged word)
802 paragraphLayout.mWordsLayoutInfo.erase( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex, paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesEnd.mWordIndex );
804 // Update paragraph info.
805 UpdateLayoutInfo( paragraphLayout, layoutParameters.mLineHeightOffset );
806 }// end delete text from same paragraph.
808 if( mergeParagraphs )
810 // Merges paragraphs pointed by textInfoMergeIndicesBegin.mParagraphIndex and textInfoMergeIndicesEnd.mParagraphIndex calculated previously.
812 ParagraphLayoutInfo& firstParagraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoMergeIndicesBegin.mParagraphIndex ) );
814 const ParagraphLayoutInfo& lastParagraphLayout( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoMergeIndicesEnd.mParagraphIndex ) );
816 MergeParagraph( firstParagraphLayout,
817 lastParagraphLayout );
820 // Store text-actors before removing them.
821 const std::size_t endIndex = ( mergeParagraphs && ( textInfoIndicesEnd.mParagraphIndex > 0u ) ) ? textInfoIndicesEnd.mParagraphIndex - 1u : textInfoIndicesEnd.mParagraphIndex; // text-actors from the last paragraph may have been added in the merge above.
822 CollectTextActorsFromParagraphs( removedTextActorsFromMid,
823 relayoutData.mTextLayoutInfo,
824 textInfoIndicesBegin.mParagraphIndex,
827 // Remove unwanted paragraphs using previously calculated indices. (including the last part of the merged paragraph)
828 relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.erase( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesBegin.mParagraphIndex,
829 relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin() + textInfoIndicesEnd.mParagraphIndex );
832 UpdateLayoutInfo( relayoutData.mTextLayoutInfo );
834 // If the last character of the last paragraph is a new paragraph character, an empty paragraph need to be added.
835 if( !relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.empty() )
837 const WordLayoutInfo lastWordLayout = GetLastWordLayoutInfo( *( relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.end() - 1u ) );
839 if( ParagraphSeparator == lastWordLayout.mType )
841 ParagraphLayoutInfo lastParagraphLayout;
843 const CharacterLayoutInfo layoutInfo = GetLastCharacterLayoutInfo( lastWordLayout );
844 lastParagraphLayout.mSize.height = layoutInfo.mSize.height;
846 relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.push_back( lastParagraphLayout );
848 relayoutData.mTextLayoutInfo.mWholeTextSize.height += layoutInfo.mSize.height;
852 // Clear the text from the text-actors if required.
853 if( CLEAR_TEXT == clearText )
855 ClearText( removedTextActorsFromEnd );
856 ClearText( removedTextActorsFromMid );
857 ClearText( removedTextActorsFromBegin );
860 // Insert text-actors into the cache.
861 // Text-actors are inserted in reverse order to use first the first removed.
862 relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromEnd );
863 relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromMid );
864 relayoutData.mTextActorCache.InsertTextActors( removedTextActorsFromBegin );
867 void UpdateTextInfo( std::size_t position,
868 std::size_t numberOfCharacters,
869 const MarkupProcessor::StyledTextArray& text,
870 const TextView::LayoutParameters& layoutParameters,
871 TextView::RelayoutData& relayoutData )
873 // Replaces 'numberOfCharacters' of text starting from 'position' with the given text.
875 // TODO: Temporary implementation with remove and insert.
878 UpdateTextInfo( position,
882 KEEP_TEXT ); // Do not clear the text from the text-actors.
885 UpdateTextInfo( position,
891 void UpdateTextInfo( float lineHeightOffset,
892 TextLayoutInfo& textLayoutInfo )
894 // Updates the space between lines with the new offset value.
896 float newTextHeight = 0.f;
898 for( ParagraphLayoutInfoContainer::iterator paragraphIt = textLayoutInfo.mParagraphsLayoutInfo.begin(), paragraphEndIt = textLayoutInfo.mParagraphsLayoutInfo.end();
899 paragraphIt != paragraphEndIt;
902 ParagraphLayoutInfo& paragraphLayoutInfo( *paragraphIt );
904 paragraphLayoutInfo.mSize.height += ( lineHeightOffset - paragraphLayoutInfo.mLineHeightOffset );
905 newTextHeight += paragraphLayoutInfo.mSize.height;
907 paragraphLayoutInfo.mLineHeightOffset = lineHeightOffset;
910 textLayoutInfo.mWholeTextSize.height = newTextHeight;
913 void UpdateTextInfo( const TextStyle& style,
914 const TextStyle::Mask mask,
915 TextView::RelayoutData& relayoutData )
917 // Change text style for all text-actors.
919 for( ParagraphLayoutInfoContainer::iterator paragraphIt = relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.begin(), paragraphEndIt = relayoutData.mTextLayoutInfo.mParagraphsLayoutInfo.end();
920 paragraphIt != paragraphEndIt;
923 ParagraphLayoutInfo& paragraph( *paragraphIt );
925 std::size_t index = 0u;
926 for( Vector<TextStyle*>::Iterator it = paragraph.mTextStyles.Begin(), endIt = paragraph.mTextStyles.End(); it != endIt; ++it )
928 (*it)->Copy( style, mask );
930 // Checks if the font family supports all glyphs. If not, chooses a most suitable one.
931 ChooseFontFamilyName( paragraph.mText[index], *(*it) );
936 for( WordLayoutInfoContainer::iterator wordIt = paragraph.mWordsLayoutInfo.begin(), wordEndIt = paragraph.mWordsLayoutInfo.end();
940 WordLayoutInfo& word( *wordIt );
942 for( CharacterLayoutInfoContainer::iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end();
943 characterIt != characterEndIt;
946 CharacterLayoutInfo& characterLayout( *characterIt );
948 // Mark the character to be set the new style into the text-actor.
949 characterLayout.mSetStyle = true;
955 } // namespace TextViewProcessor
957 } // namespace Internal
959 } // namespace Toolkit