TextView - Right to left implementation.
[platform/core/uifw/dali-toolkit.git] / base / dali-toolkit / internal / controls / text-view / text-view-word-processor.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/text-view-word-processor.h>
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/internal/controls/text-view/text-view-processor-helper-functions.h>
23 #include <dali-toolkit/internal/controls/text-view/text-view-processor-dbg.h>
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Internal
32 {
33
34 namespace TextViewProcessor
35 {
36
37 namespace
38 {
39
40 const std::string EMOJI_FONT_NAME( "SamsungEmoji" ); // Emoticons font family name.
41
42 } // namespace
43
44 /////////////////////
45 // Layout info.
46 /////////////////////
47
48 WordLayoutInfo::WordLayoutInfo()
49 : mSize(),
50   mAscender( 0.f ),
51   mType( NoSeparator ),
52   mFirstCharacter( 0u ),
53   mCharactersLayoutInfo()
54 {
55 }
56
57 WordLayoutInfo::~WordLayoutInfo()
58 {
59 }
60
61 WordLayoutInfo::WordLayoutInfo( const WordLayoutInfo& word )
62 : mSize( word.mSize ),
63   mAscender( word.mAscender ),
64   mType( word.mType ),
65   mFirstCharacter( word.mFirstCharacter ),
66   mCharactersLayoutInfo( word.mCharactersLayoutInfo )
67 {
68 }
69
70 WordLayoutInfo& WordLayoutInfo::operator=( const WordLayoutInfo& word )
71 {
72   mSize = word.mSize;
73   mAscender = word.mAscender;
74   mType = word.mType;
75   mFirstCharacter = word.mFirstCharacter;
76   mCharactersLayoutInfo = word.mCharactersLayoutInfo;
77
78   return *this;
79 }
80
81 void CreateWordTextInfo( const Text& paragraph,
82                          Vector<TextStyle*>& textStyles,
83                          WordLayoutInfo& wordLayoutInfo )
84 {
85   DALI_LOG_INFO( gTextViewProcessorLogFilter, Debug::General, "-->TextViewProcessor::CreateWordTextInfo\n" );
86   // Split in characters.
87   std::size_t characterIndex = wordLayoutInfo.mFirstCharacter;
88   for( CharacterLayoutInfoContainer::iterator it = wordLayoutInfo.mCharactersLayoutInfo.begin(),
89          endIt =  wordLayoutInfo.mCharactersLayoutInfo.end();
90        it != endIt;
91        ++it, ++characterIndex )
92   {
93     // Gets a reference of the character's layout info.
94     CharacterLayoutInfo& characterLayoutInfo( *it );
95
96     // Gets the character and the style for that character from the paragraph.
97     Character character = paragraph[characterIndex];
98     TextStyle* textStyle = *( textStyles.Begin() + characterIndex );
99
100     // Checks whether the character is an emoticon.
101     characterLayoutInfo.mIsColorGlyph = GlyphImage::IsColorGlyph( character );
102     DALI_LOG_INFO( gTextViewProcessorLogFilter, Debug::General, "  Is color glyph: %s\n", ( characterLayoutInfo.mIsColorGlyph ? "True" : "False" ) );
103
104     if( characterLayoutInfo.mIsColorGlyph )
105     {
106       // If the character is an emoticon a predefined font is set.
107       textStyle->SetFontName( EMOJI_FONT_NAME );
108     }
109     else
110     {
111       // Checks if the font family and the font style set in the text style supports the character.
112       // If not, it chooses the right font for the given character and style.
113       ChooseFontFamilyName( character, *textStyle );
114     }
115
116     // Checks whether the charcter is right to left.
117     const Character::CharacterDirection direction = character.GetCharacterDirection();
118     characterLayoutInfo.mIsRightToLeft = ( ( direction == Character::RightToLeft ) ||
119                                            ( direction == Character::RightToLeftWeak ) );
120
121     // Gets the metrics of the font.
122     const Font font = Font::New( FontParameters( textStyle->GetFontName(), textStyle->GetFontStyle(), textStyle->GetFontPointSize() ) );
123     const Font::Metrics metrics = font.GetMetrics( character );
124     const float ascender = font.GetAscender();
125
126     // Fill Natural size info for current character.
127
128     // The font line's height is used as character's height.
129     characterLayoutInfo.mSize.height = font.GetLineHeight();
130
131     // The character's advance is used as charcter's width.
132     characterLayoutInfo.mSize.width = metrics.GetAdvance();
133
134     // The ascender and bearing are used to position correctly glyphs of different font sizes.
135     characterLayoutInfo.mAscender = ascender;
136     characterLayoutInfo.mBearing = metrics.GetBearing();
137
138     if( character.IsNewLine() && !characterLayoutInfo.mIsColorGlyph )
139     {
140       // A new paragraph character '\n'  doesn't have any width.
141       characterLayoutInfo.mSize.width = 0.f;
142     }
143
144     // Set's the underline thickness and position.
145     // Both thickness and position includes the vertical pad adjust used in effects like glow or shadow.
146     if( textStyle->IsUnderlineEnabled() )
147     {
148       characterLayoutInfo.mUnderlineThickness = font.GetUnderlineThickness();
149       characterLayoutInfo.mUnderlinePosition = font.GetUnderlinePosition();
150     }
151
152     // Updates the word size and ascender.
153     UpdateSize( wordLayoutInfo.mSize, characterLayoutInfo.mSize );
154     wordLayoutInfo.mAscender = std::max( wordLayoutInfo.mAscender, characterLayoutInfo.mAscender );
155   } // end of characters in the word.
156   DALI_LOG_INFO( gTextViewProcessorLogFilter, Debug::General, "<--TextViewProcessor::CreateWordTextInfo\n" );
157 }
158
159 void UpdateLayoutInfo( WordLayoutInfo& wordLayout )
160 {
161   // Initialize layout info for the whole word.
162   wordLayout.mSize = Size::ZERO;
163   wordLayout.mAscender = 0.f;
164
165   // Traverse the character layout info to update the word layout.
166   for( CharacterLayoutInfoContainer::iterator layoutIt = wordLayout.mCharactersLayoutInfo.begin(), layoutEndIt = wordLayout.mCharactersLayoutInfo.end();
167        layoutIt != layoutEndIt;
168        ++layoutIt )
169   {
170     // Layout info for the current character.
171     CharacterLayoutInfo& layoutInfo( *layoutIt );
172
173     // Update layout info for the current word.
174     UpdateSize( wordLayout.mSize, layoutInfo.mSize );
175     wordLayout.mAscender = std::max( wordLayout.mAscender, layoutInfo.mAscender );
176   }
177 }
178
179 void RemoveCharactersFromWordInfo( TextView::RelayoutData& relayoutData,
180                                    const std::size_t numberOfCharacters,
181                                    bool& mergeWords,
182                                    bool& mergeParagraphs,
183                                    TextInfoIndices& textInfoIndicesBegin,
184                                    TextInfoIndices& textInfoIndicesEnd,
185                                    TextInfoIndices& textInfoMergeIndicesBegin,
186                                    TextInfoIndices& textInfoMergeIndicesEnd,
187                                    ParagraphLayoutInfo& paragraphLayout,
188                                    std::vector<TextActor>& removedTextActors )
189 {
190   const TextLayoutInfo& textLayoutInfo = relayoutData.mTextLayoutInfo;
191
192   // Get the word.
193   WordLayoutInfo& wordLayout( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex ) );
194
195   if( ParagraphSeparator == wordLayout.mType )
196   {
197     // If the word is a paragraph separator and there is more paragraphs, then current paragraph and the paragraph after need to be merged.
198     if( textInfoIndicesBegin.mParagraphIndex + 1u < textLayoutInfo.mParagraphsLayoutInfo.size() )
199     {
200       // current paragraph is not the last one.
201
202       // Update indices to merge paragraphs.
203       textInfoMergeIndicesBegin.mParagraphIndex = textInfoIndicesBegin.mParagraphIndex;
204       textInfoMergeIndicesEnd.mParagraphIndex = textInfoIndicesBegin.mParagraphIndex + 1u;
205
206       mergeParagraphs = true;
207
208       ++textInfoIndicesBegin.mParagraphIndex; // increase both indices,
209       textInfoIndicesEnd.mParagraphIndex += 2u; // will delete last paragraph.
210     }
211
212     ++textInfoIndicesEnd.mWordIndex; //will delete the paragraph separator;
213   }
214   else if( WordSeparator == wordLayout.mType )
215   {
216     // If the word is a word separator. Check if the word before and the word after can be merged.
217
218     if( ( 0u < textInfoIndicesBegin.mWordIndex ) && ( paragraphLayout.mWordsLayoutInfo.size() > textInfoIndicesBegin.mWordIndex + 1u ) )
219     {
220       const WordLayoutInfo& wordLayoutBefore( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex - 1u ) );
221       const WordLayoutInfo& wordLayoutAfter( *( paragraphLayout.mWordsLayoutInfo.begin() + textInfoIndicesBegin.mWordIndex + 1u ) );
222
223       if( ( NoSeparator == wordLayoutBefore.mType ) && ( NoSeparator == wordLayoutAfter.mType ) )
224       {
225         // This word is a word separator (white space) and is not the first word of the paragraph nor the last one.
226         mergeWords = true;
227
228         // Set indices to merge the words.
229         textInfoMergeIndicesBegin.mWordIndex = textInfoIndicesBegin.mWordIndex - 1u; // word before word separator.
230         textInfoMergeIndicesEnd.mWordIndex = textInfoIndicesBegin.mWordIndex + 1u; // word after word separator.
231
232         textInfoIndicesEnd.mWordIndex += 2u; // will delete the word separator and the merged word.
233       }
234       else
235       {
236         ++textInfoIndicesEnd.mWordIndex; // will delete the word separator;
237       }
238     }
239     else
240     {
241       ++textInfoIndicesEnd.mWordIndex; // will delete the word separator;
242     }
243   }
244   else if( numberOfCharacters == wordLayout.mCharactersLayoutInfo.size() )
245   {
246     // The whole word needs to be removed.
247     ++textInfoIndicesEnd.mWordIndex; // will delete the current word.
248   }
249   else
250   {
251     // Store text-actors before removing them.
252     CollectTextActors( removedTextActors, wordLayout, textInfoIndicesBegin.mCharacterIndex, textInfoIndicesBegin.mCharacterIndex + numberOfCharacters );
253
254     // just remove some characters from current word.
255     RemoveCharactersFromWord( textInfoIndicesBegin.mCharacterIndex,
256                               numberOfCharacters,
257                               wordLayout );
258   }
259 }
260
261 void RemoveCharactersFromWord( const std::size_t position,
262                                const std::size_t numberOfCharacters,
263                                WordLayoutInfo& wordLayout )
264 {
265   // Removes a given number of characters from the given word starting from the 'position' index.
266
267   // Early return.
268   if( 0u == numberOfCharacters )
269   {
270     // nothing to do if the number of characters is zero.
271
272     return;
273   }
274
275   // Remove characters from layout and text-actor info.
276   wordLayout.mCharactersLayoutInfo.erase( wordLayout.mCharactersLayoutInfo.begin() + position, wordLayout.mCharactersLayoutInfo.begin() + position + numberOfCharacters );
277
278   // Some characters have been removed from the word. Update the layout info is needed.
279   UpdateLayoutInfo( wordLayout );
280 }
281
282 void SplitWord( const std::size_t position,
283                 WordLayoutInfo& firstWordLayoutInfo,
284                 WordLayoutInfo& lastWordLayoutInfo )
285 {
286   // Splits a word in two.
287   // It moves characters from the first part of the word to the last one.
288
289   // early returns
290   if( 0u == position )
291   {
292     // the whole word goes to the last part of the word.
293     lastWordLayoutInfo = firstWordLayoutInfo;
294
295     firstWordLayoutInfo = WordLayoutInfo();
296
297     return;
298   }
299
300   if( position == firstWordLayoutInfo.mCharactersLayoutInfo.size() )
301   {
302     // the whole word goes to the first part of the word.
303
304     // Just delete whatever there is in the last part of the word.
305     lastWordLayoutInfo = WordLayoutInfo();
306
307     return;
308   }
309
310   // Initialize output data structures.
311
312   // Layout info
313   lastWordLayoutInfo = WordLayoutInfo();
314
315   // Split layout info.
316
317   // Insert characters from the given index 'position' to the end.
318   lastWordLayoutInfo.mCharactersLayoutInfo.insert( lastWordLayoutInfo.mCharactersLayoutInfo.end(),
319                                                    firstWordLayoutInfo.mCharactersLayoutInfo.begin() + position, firstWordLayoutInfo.mCharactersLayoutInfo.end() );
320
321   // Delete characters from the first part of the word.
322   firstWordLayoutInfo.mCharactersLayoutInfo.erase( firstWordLayoutInfo.mCharactersLayoutInfo.begin() + position, firstWordLayoutInfo.mCharactersLayoutInfo.end() );
323
324   // Update the layout info of both new words.
325   UpdateLayoutInfo( firstWordLayoutInfo );
326   UpdateLayoutInfo( lastWordLayoutInfo );
327 }
328
329 void MergeWord( WordLayoutInfo& firstWordLayoutInfo,
330                 const WordLayoutInfo& lastWordLayoutInfo )
331 {
332   // Merges two given words.
333
334   // Early returns.
335   if( lastWordLayoutInfo.mCharactersLayoutInfo.empty() )
336   {
337     // nothing to do
338     return;
339   }
340
341   if( firstWordLayoutInfo.mCharactersLayoutInfo.empty() )
342   {
343     // copy last to first
344
345     firstWordLayoutInfo = lastWordLayoutInfo;
346
347     return;
348   }
349
350   if( ( NoSeparator != firstWordLayoutInfo.mType ) || ( NoSeparator != lastWordLayoutInfo.mType ) )
351   {
352     // Do not merge white spaces or new paragraph characters.
353     DALI_ASSERT_ALWAYS( !"TextViewProcessor::MergeWord(). ERROR: White spaces or new paragraph characters can't be merged with other words." );
354   }
355
356   // Merge layout info
357   firstWordLayoutInfo.mCharactersLayoutInfo.insert( firstWordLayoutInfo.mCharactersLayoutInfo.end(),
358                                                     lastWordLayoutInfo.mCharactersLayoutInfo.begin(),
359                                                     lastWordLayoutInfo.mCharactersLayoutInfo.end() );
360
361   // Update the word layout info.
362   UpdateSize( firstWordLayoutInfo.mSize, lastWordLayoutInfo.mSize );
363   firstWordLayoutInfo.mAscender = std::max( firstWordLayoutInfo.mAscender, lastWordLayoutInfo.mAscender );
364 }
365
366 CharacterLayoutInfo GetFirstCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo )
367 {
368   CharacterLayoutInfo layoutInfo;
369
370   if( !wordLayoutInfo.mCharactersLayoutInfo.empty() )
371   {
372     layoutInfo = *wordLayoutInfo.mCharactersLayoutInfo.begin();
373   }
374
375   return layoutInfo;
376 }
377
378 CharacterLayoutInfo GetLastCharacterLayoutInfo( const WordLayoutInfo& wordLayoutInfo )
379 {
380   CharacterLayoutInfo layoutInfo;
381
382   if( !wordLayoutInfo.mCharactersLayoutInfo.empty() )
383   {
384     layoutInfo = *( wordLayoutInfo.mCharactersLayoutInfo.end() - 1u );
385   }
386
387   return layoutInfo;
388 }
389
390 void CollectTextActors( std::vector<TextActor>& textActors, const WordLayoutInfo& word, const std::size_t characterIndexBegin, const std::size_t characterIndexEnd )
391 {
392   for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin() + characterIndexBegin,
393          characterEndIt = word.mCharactersLayoutInfo.begin() + characterIndexEnd;
394        characterIt != characterEndIt;
395        ++characterIt )
396   {
397     const CharacterLayoutInfo& characterLayout( *characterIt );
398
399     if( !characterLayout.mIsColorGlyph )
400     {
401       TextActor textActor = TextActor::DownCast( characterLayout.mGlyphActor );
402       if( textActor )
403       {
404         textActors.push_back( textActor );
405       }
406     }
407   }
408 }
409
410 void CollectTextActorsFromWords( std::vector<TextActor>& textActors, const ParagraphLayoutInfo& paragraph, const std::size_t wordIndexBegin, const std::size_t wordIndexEnd )
411 {
412   for( WordLayoutInfoContainer::const_iterator wordIt = paragraph.mWordsLayoutInfo.begin() + wordIndexBegin, wordEndIt = paragraph.mWordsLayoutInfo.begin() + wordIndexEnd;
413        wordIt != wordEndIt;
414        ++wordIt )
415   {
416     const WordLayoutInfo& word( *wordIt );
417
418     for( CharacterLayoutInfoContainer::const_iterator characterIt = word.mCharactersLayoutInfo.begin(), characterEndIt = word.mCharactersLayoutInfo.end();
419          characterIt != characterEndIt;
420          ++characterIt )
421     {
422       const CharacterLayoutInfo& characterLayout( *characterIt );
423
424       if( !characterLayout.mIsColorGlyph )
425       {
426         TextActor textActor = TextActor::DownCast( characterLayout.mGlyphActor );
427         if( textActor )
428         {
429           textActors.push_back( textActor );
430         }
431       }
432     }
433   }
434 }
435
436 } //namespace TextViewProcessor
437
438 } //namespace Internal
439
440 } //namespace Toolkit
441
442 } //namespace Dali