8082ce8efa716e83222b042d2b50fce5436fe2a4
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-view / text-view-impl.cpp
1 //
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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 // CLASS HEADER
18 #include "text-view-impl.h"
19
20 // INTERNAL INCLUDES
21 #include "split-by-new-line-char-policies.h"
22 #include "split-by-word-policies.h"
23 #include "split-by-char-policies.h"
24 #include "text-view-processor.h"
25 #include "text-view-word-processor.h"
26 #include "relayout-utilities.h"
27 #include "text-view-processor-dbg.h"
28
29 namespace Dali
30 {
31
32 namespace Toolkit
33 {
34
35 namespace Internal
36 {
37
38 namespace
39 {
40
41 const char* const PROPERTY_TEXT = "text";
42 const char* const PROPERTY_MULTILINE_POLICY = "multiline-policy";
43 const char* const PROPERTY_WIDTH_EXCEED_POLICY = "width-exceed-policy";
44 const char* const PROPERTY_HEIGHT_EXCEED_POLICY = "height-exceed-policy";
45 const char* const PROPERTY_LINE_JUSTIFICATION = "line-justification";
46 const char* const PROPERTY_FADE_BOUNDARY_LEFT = "fade-boundary-left";
47 const char* const PROPERTY_FADE_BOUNDARY_RIGHT = "fade-boundary-right";
48 const char* const PROPERTY_FADE_BOUNDARY_TOP = "fade-boundary-top";
49 const char* const PROPERTY_FADE_BOUNDARY_BOTTOM = "fade-boundary-bottom";
50 const char* const PROPERTY_LINE_HEIGHT_OFFSET = "line-height-offset";
51 const char* const PROPERTY_HORIZONTAL_ALIGNMENT = "horizontal-alignment";
52 const char* const PROPERTY_VERTICAL_ALIGNMENT = "vertical-alignment";
53
54 // Currently on desktop machines 2k x 2k is the maximum frame buffer size, on target is 4k x 4k.
55 const float MAX_OFFSCREEN_RENDERING_SIZE = 2048.f;
56
57 //Type Registration
58 BaseHandle Create()
59 {
60   return Toolkit::TextView::New();
61 }
62
63 TypeRegistration typeRegistration( typeid(Toolkit::TextView), typeid(Toolkit::Control), Create );
64
65 SignalConnectorType signalConnector1( typeRegistration, Toolkit::TextView::SIGNAL_TEXT_SCROLLED , &TextView::DoConnectSignal );
66
67 /**
68  * Whether the text-view-processor operation sets, inserts, replaces, removes text.
69  *
70  * @param[in] metadata The text-view-processor operation.
71  *
72  * @return \e true if the given text-view-processor operation is modifying the text.
73  */
74 bool IsTextViewProcessorRelayoutOperation( const TextView::TextViewProcessorMetadata& metadata )
75 {
76   return ( ( metadata.mType == TextView::TextSet ) ||
77            ( metadata.mType == TextView::TextInserted ) ||
78            ( metadata.mType == TextView::TextReplaced ) ||
79            ( metadata.mType == TextView::TextRemoved ) ||
80            ( metadata.mType == TextView::NewStyle ));
81 }
82
83 /**
84  * Whether the text-view-processor operation sets a new line height offset.
85  *
86  * @param[in] metadata The text-view-processor operation.
87  *
88  * @return \e true if the given text-view-processor operation sets a new line height offset.
89  */
90 bool IsTextViewProcessorLineHeightOffsetOperation( const TextView::TextViewProcessorMetadata& metadata )
91 {
92   return ( metadata.mType == TextView::NewLineHeight );
93 }
94
95 /**
96  * Whether the text-view-processor operation sets a new style.
97  *
98  * @param[in] metadata The text-view-processor operation.
99  *
100  * @return \e true if the given text-view-processor operation sets a new style.
101  */
102 bool IsTextViewProcessorNewStyleOperation( const TextView::TextViewProcessorMetadata& metadata )
103 {
104   return ( metadata.mType == TextView::NewStyle );
105 }
106
107 } // namespace
108
109 TextView::TextViewProcessorMetadata::TextViewProcessorMetadata()
110 : mType( TextView::TextSet ),
111   mPosition( 0 ),
112   mNumberOfCharacters( 0 ),
113   mText()
114 {
115 }
116
117 Toolkit::TextView TextView::New()
118 {
119   // Create the implementation, temporarily owned on stack
120   IntrusivePtr<TextView> textView = new TextView();
121
122   // Pass ownership to CustomActor
123   Toolkit::TextView handle( *textView );
124
125   // Second-phase init of the implementation
126   // This can only be done after the CustomActor connection has been made...
127   textView->Initialize();
128
129   // Enables by default the offscreen rendering.
130   textView->SetSnapshotModeEnabled( false ); /// @note Temporary disabled due to some issues with text quality and glyph loading.
131
132   return handle;
133 }
134
135 void TextView::SetText( const std::string& text )
136 {
137   // Creates a styled text with the markup or plain string.
138   MarkupProcessor::StyledTextArray styledText;
139   MarkupProcessor::GetStyledTextArray( text, styledText, IsMarkupProcessingEnabled() );
140
141   // Calls SetText() with the styled text array.
142   SetText( styledText );
143 }
144
145 void TextView::SetText( const MarkupProcessor::StyledTextArray& text )
146 {
147   // mTextViewProcessorOperations stores the InsertTextAt and RemoveTextFrom operations to transform the initial text to mCurrentStyledText.
148   // Once again, if a new text is set, any previous call to InsertTextAt or RemoveTextFrom can be discarted.
149
150   std::vector<TextViewProcessorMetadata>::iterator it = std::remove_if( mTextViewProcessorOperations.begin(), mTextViewProcessorOperations.end(), IsTextViewProcessorRelayoutOperation );
151   mTextViewProcessorOperations.erase( it, mTextViewProcessorOperations.end() );
152
153   // Creates metadata with the Set operation.
154   TextViewProcessorMetadata metadata;
155   metadata.mType = TextView::TextSet;
156   metadata.mText = text;
157
158   // Store metadata.
159   mTextViewProcessorOperations.push_back( metadata );
160
161   // Updates current styled text.
162   mCurrentStyledText = text;
163
164   // Request to be relaid out
165   RelayoutRequest();
166
167   // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
168   mRelayoutOperations = RELAYOUT_ALL;
169 }
170
171 void TextView::InsertTextAt( std::size_t position, const std::string& text )
172 {
173   // Creates a styled text with the markup or plain string.
174   MarkupProcessor::StyledTextArray styledText;
175   MarkupProcessor::GetStyledTextArray( text, styledText, IsMarkupProcessingEnabled() );
176
177   // Calls InsertTextAt() with the styled text array.
178   InsertTextAt( position, styledText );
179 }
180
181 void TextView::InsertTextAt( const std::size_t position, const MarkupProcessor::StyledTextArray& text )
182 {
183   // Creates metadata with the Insert operation.
184   TextViewProcessorMetadata metadata;
185   metadata.mType = TextView::TextInserted;
186   metadata.mPosition = position;
187   metadata.mText = text;
188
189   // Store metadata.
190   mTextViewProcessorOperations.push_back( metadata );
191
192   // Updates current styled text.
193   mCurrentStyledText.insert( mCurrentStyledText.begin() + position, text.begin(), text.end() );
194
195   // Request to be relaid out
196   RelayoutRequest();
197
198   // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
199   mRelayoutOperations = RELAYOUT_ALL;
200 }
201
202 void TextView::ReplaceTextFromTo( const std::size_t position, const std::size_t numberOfCharacters, const std::string& text )
203 {
204   // Creates a styled text with the markup or plain string.
205   MarkupProcessor::StyledTextArray styledText;
206   MarkupProcessor::GetStyledTextArray( text, styledText, IsMarkupProcessingEnabled() );
207
208   // Calls ReplaceTextFromTo() with the styled text array.
209   ReplaceTextFromTo( position, numberOfCharacters, styledText );
210 }
211
212 void TextView::ReplaceTextFromTo( const std::size_t position, const std::size_t numberOfCharacters, const MarkupProcessor::StyledTextArray& text )
213 {
214   // Creates metadata with the Insert operation.
215   TextViewProcessorMetadata metadata;
216   metadata.mType = TextView::TextReplaced;
217   metadata.mPosition = position;
218   metadata.mNumberOfCharacters = numberOfCharacters;
219   metadata.mText = text;
220
221   // Store metadata.
222   mTextViewProcessorOperations.push_back( metadata );
223
224   // Updates current styled text.
225   MarkupProcessor::StyledTextArray::iterator it = mCurrentStyledText.begin() + position;
226   mCurrentStyledText.erase( it, it + numberOfCharacters );
227   it = mCurrentStyledText.begin() + position;
228   mCurrentStyledText.insert( it, text.begin(), text.end() );
229
230   // Request to be relaid out
231   RelayoutRequest();
232
233   // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
234   mRelayoutOperations = RELAYOUT_ALL;
235 }
236
237 void TextView::RemoveTextFrom( const std::size_t position, const std::size_t numberOfCharacters )
238 {
239   // Creates metadata with the Remove operation.
240   TextViewProcessorMetadata metadata;
241   metadata.mType = TextView::TextRemoved;
242   metadata.mPosition = position;
243   metadata.mNumberOfCharacters = numberOfCharacters;
244
245   // Store metadata.
246   mTextViewProcessorOperations.push_back( metadata );
247
248   // Updates current styled text.
249   MarkupProcessor::StyledTextArray::iterator it = mCurrentStyledText.begin() + position;
250   mCurrentStyledText.erase( it, it + numberOfCharacters );
251
252   // Request to be relaid out
253   RelayoutRequest();
254
255   // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
256   mRelayoutOperations = RELAYOUT_ALL;
257 }
258
259 std::string TextView::GetText() const
260 {
261   // Traverses the styled text array getting only the text.
262   //  Note that for some languages a 'character' could be represented by more than one 'char'
263
264   std::string text;
265   for( MarkupProcessor::StyledTextArray::const_iterator it = mCurrentStyledText.begin(), endIt = mCurrentStyledText.end(); it != endIt; ++it )
266   {
267     text.append( (*it).mText.GetText() );
268   }
269
270   return text;
271 }
272
273 void TextView::SetLineHeightOffset( const PointSize offset )
274 {
275   if( fabsf( mLayoutParameters.mLineHeightOffset - offset ) > Math::MACHINE_EPSILON_1000 )
276   {
277     // Removes any previous operation which modifies the line height offset.
278     std::vector<TextViewProcessorMetadata>::iterator it = std::remove_if( mTextViewProcessorOperations.begin(), mTextViewProcessorOperations.end(), IsTextViewProcessorLineHeightOffsetOperation );
279     mTextViewProcessorOperations.erase( it, mTextViewProcessorOperations.end() );
280
281     // Creates metadata with the new line height operation.
282     TextViewProcessorMetadata metadata;
283     metadata.mType = TextView::NewLineHeight;
284
285     mTextViewProcessorOperations.push_back( metadata );
286
287     // Updates line height offset.
288     mLayoutParameters.mLineHeightOffset = offset;
289
290     RelayoutRequest();
291
292     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
293     if( RELAYOUT_ALL != mRelayoutOperations )
294     {
295       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
296                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
297                                                                 RELAYOUT_SIZE_POSITION |
298                                                                 RELAYOUT_ALIGNMENT |
299                                                                 RELAYOUT_VISIBILITY |
300                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
301                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
302                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
303     }
304   }
305 }
306
307 PointSize TextView::GetLineHeightOffset() const
308 {
309   return PointSize( mLayoutParameters.mLineHeightOffset );
310 }
311
312 void TextView::SetStyleToCurrentText( const TextStyle& style, const TextStyle::Mask mask )
313 {
314   if( !mCurrentStyledText.empty() )
315   {
316     const bool checkFontName = mask & TextStyle::FONT;
317     const bool checkFontSize = mask & TextStyle::SIZE;
318     const bool checkFontStyle = mask & TextStyle::STYLE;
319
320     // Check first if metrics have changed.
321     bool metricsChanged = false;
322     for( MarkupProcessor::StyledTextArray::const_iterator it = mCurrentStyledText.begin(), endIt = mCurrentStyledText.end(); ( it != endIt ) && !metricsChanged; ++it )
323     {
324       const MarkupProcessor::StyledText& styledText( *it );
325
326       metricsChanged = ( checkFontName && ( styledText.mStyle.GetFontName() != style.GetFontName() ) ) ||
327                        ( checkFontStyle && ( styledText.mStyle.GetFontStyle() != style.GetFontStyle() ) ) ||
328                        ( checkFontSize && ( fabsf( styledText.mStyle.GetFontPointSize() - style.GetFontPointSize() ) > Math::MACHINE_EPSILON_1000 ) );
329     }
330
331     if( metricsChanged )
332     {
333       MarkupProcessor::SetTextStyle( mCurrentStyledText, style, mask );
334
335       // If metrics change, new text measurements are needed.
336       SetText( mCurrentStyledText );
337     }
338     else
339     {
340       // Deletes any previous operation which sets a new style.
341       std::vector<TextViewProcessorMetadata>::iterator it = std::remove_if( mTextViewProcessorOperations.begin(), mTextViewProcessorOperations.end(), IsTextViewProcessorNewStyleOperation );
342       mTextViewProcessorOperations.erase( it, mTextViewProcessorOperations.end() );
343
344       // Creates metadata with the new style operation.
345       TextViewProcessorMetadata metadata;
346       metadata.mType = TextView::NewStyle;
347
348       MarkupProcessor::StyledText text;
349       text.mStyle = style;
350       metadata.mText.push_back( text );
351       metadata.mStyleMask = mask;
352
353       mTextViewProcessorOperations.push_back( metadata );
354
355       MarkupProcessor::SetTextStyle( mCurrentStyledText, style, mask );
356
357       RelayoutRequest();
358
359       if( RELAYOUT_ALL != mRelayoutOperations )
360       {
361         mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
362                                                                   RELAYOUT_TEXT_ACTOR_UPDATE );
363       }
364     }
365   }
366
367   // Sets the new style to the ellipsize text
368   if( !mLayoutParameters.mEllipsizeText.empty() )
369   {
370     for( MarkupProcessor::StyledTextArray::iterator it = mLayoutParameters.mEllipsizeText.begin(), endIt = mLayoutParameters.mEllipsizeText.end(); it != endIt; ++it )
371     {
372       (*it).mStyle.Copy( style, mask );
373     }
374
375     SetEllipsizeText( mLayoutParameters.mEllipsizeText );
376   }
377 }
378
379 void TextView::SetTextAlignment( Toolkit::Alignment::Type align )
380 {
381   if( align != ( mLayoutParameters.mHorizontalAlignment | mLayoutParameters.mVerticalAlignment ) )
382   {
383     Toolkit::Alignment::Type horizontalAlignment( ( align & Toolkit::Alignment::HorizontalLeft ? Toolkit::Alignment::HorizontalLeft :
384                                                     ( align & Toolkit::Alignment::HorizontalCenter ? Toolkit::Alignment::HorizontalCenter :
385                                                       ( align & Toolkit::Alignment::HorizontalRight ? Toolkit::Alignment::HorizontalRight : Toolkit::Alignment::HorizontalCenter ) ) ) );
386     Toolkit::Alignment::Type verticalAlignment( ( align & Toolkit::Alignment::VerticalTop ? Toolkit::Alignment::VerticalTop :
387                                                   ( align & Toolkit::Alignment::VerticalCenter ? Toolkit::Alignment::VerticalCenter :
388                                                     ( align & Toolkit::Alignment::VerticalBottom ? Toolkit::Alignment::VerticalBottom : Toolkit::Alignment::VerticalCenter ) ) ) );
389
390     mLayoutParameters.mHorizontalAlignment = horizontalAlignment;
391     mLayoutParameters.mVerticalAlignment = verticalAlignment;
392
393     RelayoutRequest();
394
395     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
396     if( RELAYOUT_ALL != mRelayoutOperations )
397     {
398       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
399                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
400                                                                 RELAYOUT_ALIGNMENT |
401                                                                 RELAYOUT_VISIBILITY );
402     }
403   }
404 }
405
406 Toolkit::Alignment::Type TextView::GetTextAlignment() const
407 {
408   return static_cast<Toolkit::Alignment::Type>( mLayoutParameters.mHorizontalAlignment | mLayoutParameters.mVerticalAlignment );
409 }
410
411 void TextView::SetMultilinePolicy( Toolkit::TextView::MultilinePolicy policy )
412 {
413   if( policy != mLayoutParameters.mMultilinePolicy )
414   {
415     mLayoutParameters.mMultilinePolicy = policy;
416
417     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed on order to retrieve the right values.
418     mRelayoutOperations = RELAYOUT_ALL;
419
420     RelayoutRequest();
421   }
422 }
423
424 Toolkit::TextView::MultilinePolicy TextView::GetMultilinePolicy() const
425 {
426   return mLayoutParameters.mMultilinePolicy;
427 }
428
429 void TextView::SetWidthExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
430 {
431   // The layout info could be invalid depending on the current exceed policy and the new one.
432   // i.e. if the current policy is Split and the new one is ShrinkToFit then
433   // the layout info generated for each char is not needed.
434   if( policy != mLayoutParameters.mWidthExceedPolicy )
435   {
436     mLayoutParameters.mWidthExceedPolicy = policy;
437
438     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
439     mRelayoutOperations = RELAYOUT_ALL;
440
441     RelayoutRequest();
442   }
443 }
444
445 Toolkit::TextView::ExceedPolicy TextView::GetWidthExceedPolicy() const
446 {
447   return mLayoutParameters.mWidthExceedPolicy;
448 }
449
450 void TextView::SetHeightExceedPolicy( Toolkit::TextView::ExceedPolicy policy )
451 {
452   if( policy != mLayoutParameters.mHeightExceedPolicy )
453   {
454     mLayoutParameters.mHeightExceedPolicy = policy;
455
456     RelayoutRequest();
457
458     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
459     if( RELAYOUT_ALL != mRelayoutOperations )
460     {
461       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
462                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
463                                                                 RELAYOUT_SIZE_POSITION |
464                                                                 RELAYOUT_ALIGNMENT |
465                                                                 RELAYOUT_VISIBILITY |
466                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
467                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
468                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
469     }
470   }
471 }
472
473 Toolkit::TextView::ExceedPolicy TextView::GetHeightExceedPolicy() const
474 {
475   return mLayoutParameters.mHeightExceedPolicy;
476 }
477
478 void TextView::SetLineJustification( Toolkit::TextView::LineJustification justification )
479 {
480   if( justification != mLayoutParameters.mLineJustification )
481   {
482     mLayoutParameters.mLineJustification = justification;
483
484     RelayoutRequest();
485
486     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
487     if( RELAYOUT_ALL != mRelayoutOperations )
488     {
489       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
490                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
491                                                                 RELAYOUT_SIZE_POSITION |
492                                                                 RELAYOUT_ALIGNMENT |
493                                                                 RELAYOUT_VISIBILITY |
494                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
495                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
496                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
497     }
498   }
499 }
500
501 Toolkit::TextView::LineJustification TextView::GetLineJustification() const
502 {
503   return mLayoutParameters.mLineJustification;
504 }
505
506 void TextView::SetFadeBoundary( const Toolkit::TextView::FadeBoundary& fadeBoundary )
507 {
508   if( ( fadeBoundary.mLeft != mVisualParameters.mFadeBoundary.mLeft )   ||
509       ( fadeBoundary.mRight != mVisualParameters.mFadeBoundary.mRight ) ||
510       ( fadeBoundary.mTop != mVisualParameters.mFadeBoundary.mTop )     ||
511       ( fadeBoundary.mBottom != mVisualParameters.mFadeBoundary.mBottom ) )
512   {
513     mVisualParameters.mFadeBoundary = fadeBoundary;
514
515     RelayoutRequest();
516
517     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
518     if( RELAYOUT_ALL != mRelayoutOperations )
519     {
520       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
521                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
522                                                                 RELAYOUT_VISIBILITY |
523                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
524                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
525                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
526     }
527   }
528 }
529
530 const Toolkit::TextView::FadeBoundary& TextView::GetFadeBoundary() const
531 {
532   return mVisualParameters.mFadeBoundary;
533 }
534
535 void TextView::SetEllipsizeText( const std::string& ellipsizeText )
536 {
537   // Creates a styled text with the markup or plain string.
538   MarkupProcessor::StyledTextArray styledText;
539   MarkupProcessor::GetStyledTextArray( ellipsizeText, styledText, IsMarkupProcessingEnabled() );
540
541   SetEllipsizeText( styledText );
542 }
543
544 void TextView::SetEllipsizeText( const MarkupProcessor::StyledTextArray& ellipsizeText )
545 {
546   mLayoutParameters.mEllipsizeText = ellipsizeText;
547
548   mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = TextViewProcessor::WordLayoutInfo();
549
550   TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText,
551                                          mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo );
552
553   // Request to be relaid out
554   RelayoutRequest();
555
556   mRelayoutOperations = RELAYOUT_ALL;
557 }
558
559 std::string TextView::GetEllipsizeText() const
560 {
561   std::string text;
562   for( MarkupProcessor::StyledTextArray::const_iterator it = mLayoutParameters.mEllipsizeText.begin(), endIt = mLayoutParameters.mEllipsizeText.end(); it != endIt; ++it )
563   {
564     text.append( (*it).mText.GetText() );
565   }
566
567   return text;
568 }
569
570 void TextView::GetTextLayoutInfo()
571 {
572   const bool relayoutSizeAndPositionNeeded = mRelayoutOperations & RELAYOUT_SIZE_POSITION;
573   const bool relayoutAlignmentNeeded = mRelayoutOperations & RELAYOUT_ALIGNMENT;
574   const bool relayoutVisibilityNeeded = mRelayoutOperations & RELAYOUT_VISIBILITY;
575
576   if( relayoutSizeAndPositionNeeded || relayoutAlignmentNeeded || relayoutVisibilityNeeded )
577   {
578     Vector3 textViewSize = GetControlSize();
579
580     if( ( ( textViewSize.width < Math::MACHINE_EPSILON_1000 ) ||
581           ( textViewSize.height < Math::MACHINE_EPSILON_1000 ) ) &&
582         ( ( Toolkit::TextView::SplitByNewLineChar == mLayoutParameters.mMultilinePolicy ) &&
583           ( Toolkit::TextView::Original == mLayoutParameters.mWidthExceedPolicy ) &&
584           ( Toolkit::TextView::Original == mLayoutParameters.mHeightExceedPolicy ) ) )
585     {
586       // In case the control size is not set but the layout settings are the default (split by new line character and original exceed policies)
587       // the text natural size can be used.
588       textViewSize = GetNaturalSize();
589     }
590
591     if( ( textViewSize.width > Math::MACHINE_EPSILON_1000 ) &&
592         ( textViewSize.height > Math::MACHINE_EPSILON_1000 ) )
593     {
594       // Check if the text-view has glyph-actors.
595       const bool hasGlyphActors = !mRelayoutData.mGlyphActors.empty();
596
597       RelayoutOperationMask mask = NO_RELAYOUT;
598       if( relayoutSizeAndPositionNeeded )
599       {
600         mask = static_cast<RelayoutOperationMask>( mask | RELAYOUT_SIZE_POSITION );
601       }
602       if( relayoutAlignmentNeeded )
603       {
604         mask = static_cast<RelayoutOperationMask>( mask | RELAYOUT_ALIGNMENT );
605       }
606       if( relayoutVisibilityNeeded )
607       {
608         mask = static_cast<RelayoutOperationMask>( mask | RELAYOUT_VISIBILITY );
609       }
610
611       if( hasGlyphActors )
612       {
613         // Remove glyph-actors from the text-view as some text-operation like CreateTextInfo()
614         // add them to the text-actor cache.
615         TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
616         mRelayoutData.mGlyphActors.clear();
617       }
618
619       // Relays-out but doesn't add glyph-actors to the text-view.
620       DoRelayOut( textViewSize.GetVectorXY(), mask );
621
622       if( hasGlyphActors )
623       {
624         mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW );
625         mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
626       }
627
628     }
629   }
630 }
631
632 void TextView::GetTextLayoutInfo( Toolkit::TextView::TextLayoutInfo& textLayoutInfo )
633 {
634   GetTextLayoutInfo();
635
636   textLayoutInfo.mCharacterLayoutInfoTable = mRelayoutData.mCharacterLayoutInfoTable;
637   textLayoutInfo.mLines = mRelayoutData.mLines;
638
639   textLayoutInfo.mCharacterLogicalToVisualMap = mRelayoutData.mCharacterLogicalToVisualMap;
640   textLayoutInfo.mCharacterVisualToLogicalMap = mRelayoutData.mCharacterVisualToLogicalMap;
641
642   textLayoutInfo.mTextSize = mRelayoutData.mTextSizeForRelayoutOption;
643
644   textLayoutInfo.mScrollOffset = mVisualParameters.mCameraScrollPosition;
645 }
646
647 void TextView::SetSortModifier( float depthOffset )
648 {
649   mVisualParameters.mSortModifier = depthOffset;
650
651   for( std::vector<RenderableActor>::iterator it = mRelayoutData.mGlyphActors.begin(), endIt = mRelayoutData.mGlyphActors.end();
652        it != endIt;
653        ++it )
654   {
655     ( *it ).SetSortModifier( depthOffset );
656   }
657
658   if( mOffscreenImageActor )
659   {
660     mOffscreenImageActor.SetSortModifier( depthOffset );
661   }
662 }
663
664 void TextView::SetSnapshotModeEnabled( bool enable )
665 {
666   if( enable != mVisualParameters.mSnapshotModeEnabled )
667   {
668     // Remove first all glyph-actors
669     if( !mRelayoutData.mGlyphActors.empty() )
670     {
671       TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
672     }
673
674     mVisualParameters.mSnapshotModeEnabled = enable;
675     if( !mLockPreviousSnapshotMode )
676     {
677       // mPreviousSnapshotModeEnabled stores the snapshot mode value before SetScrollEnabled( true ) is
678       // called. However, if SetSnapshotModeEnabled() is called after SetScrollEnabled() then the stored value
679       // is updated.
680       // As SetSnapshotModeEnabled() is also called from SetScrollEnabled(), the mLockPreviousSnapshotMode prevents
681       // to smash the stored value.
682       mPreviousSnapshotModeEnabled = enable;
683     }
684
685     if( mVisualParameters.mSnapshotModeEnabled )
686     {
687       // Create a root actor and an image actor for offscreen rendering.
688       mOffscreenRootActor = Layer::New();
689       mOffscreenImageActor = ImageActor::New();
690
691       mOffscreenRootActor.SetColorMode( USE_OWN_COLOR );
692       mOffscreenRootActor.SetPositionInheritanceMode( DONT_INHERIT_POSITION );
693       mOffscreenRootActor.SetInheritRotation( false );
694       mOffscreenRootActor.SetInheritScale( false );
695       mOffscreenRootActor.SetDepthTestDisabled( true );
696
697       mOffscreenRootActor.SetPosition( 0.f, 0.f, 0.f );
698
699       mOffscreenImageActor.SetAnchorPoint( ParentOrigin::CENTER );
700       mOffscreenImageActor.SetParentOrigin( ParentOrigin::CENTER );
701
702       Actor self = Self();
703       self.Add( mOffscreenRootActor );
704       self.Add( mOffscreenImageActor );
705       mOffscreenImageActor.SetScale(Vector3(1.f, -1.f, 1.f));
706     }
707     else
708     {
709       Actor self = Self();
710
711       if( mOffscreenRootActor )
712       {
713         self.Remove( mOffscreenRootActor );
714       }
715
716       if( mOffscreenImageActor )
717       {
718         self.Remove( mOffscreenImageActor );
719       }
720
721       DestroyOffscreenRenderingResources();
722     }
723
724     if( RELAYOUT_ALL != mRelayoutOperations )
725     {
726       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
727                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
728                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
729                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
730                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
731     }
732     RelayoutRequest();
733   }
734 }
735
736 bool TextView::IsSnapshotModeEnabled() const
737 {
738   return mVisualParameters.mSnapshotModeEnabled;
739 }
740
741 void TextView::SetMarkupProcessingEnabled( bool enable )
742 {
743   mMarkUpEnabled = enable;
744 }
745
746 bool TextView::IsMarkupProcessingEnabled() const
747 {
748   return mMarkUpEnabled;
749 }
750
751 void TextView::SetScrollEnabled( const bool enable )
752 {
753   if( enable != mVisualParameters.mScrollEnabled )
754   {
755     mVisualParameters.mScrollEnabled = enable;
756
757     if( mVisualParameters.mScrollEnabled )
758     {
759       // Offscreen rendering is needed to enable text scroll.
760
761       // Stores previous value of the snapshot mode.
762       mPreviousSnapshotModeEnabled = IsSnapshotModeEnabled();
763
764       {
765         // SetSnapshotModeEnabled() modifies the mPreviousSnapshotModeEnabled just in case it's called after SetScrollEnabled(),
766         // this lock prevents to modify the mPreviousSnapshotModeEnabled when SetSnapshotModeEnabled() from this method.
767         Lock lock( mLockPreviousSnapshotMode );
768         SetSnapshotModeEnabled( true );
769       }
770
771       // Creates the pan gesture detector and attach the text-view.
772       mPanGestureDetector = PanGestureDetector::New();
773       mPanGestureDetector.DetectedSignal().Connect( this, &TextView::OnTextPan );
774       mPanGestureDetector.Attach( Self() );
775     }
776     else
777     {
778       // Removes the pan gesture detector.
779       if( mPanGestureDetector )
780       {
781         mPanGestureDetector.Detach( Self() );
782         mPanGestureDetector.DetectedSignal().Disconnect( this, &TextView::OnTextPan );
783         mPanGestureDetector.Reset();
784       }
785
786       // Restores the previous state for snapshot mode.
787       SetSnapshotModeEnabled( mPreviousSnapshotModeEnabled );
788     }
789   }
790 }
791
792 bool TextView::IsScrollEnabled() const
793 {
794   return mVisualParameters.mScrollEnabled;
795 }
796
797 void TextView::SetScrollPosition( const Vector2& position )
798 {
799   if( position != mVisualParameters.mCameraScrollPosition )
800   {
801     // Guard against destruction during signal emission
802     // Note that Emit() methods are called indirectly from within DoSetScrollPosition()
803     Toolkit::TextView handle( GetOwner() );
804
805     DoSetScrollPosition( position );
806
807     // Check if the new scroll position has been trimmed.
808     mVisualParameters.mScrollPositionTrimmed = ( position != mVisualParameters.mCameraScrollPosition );
809   }
810 }
811
812 const Vector2& TextView::GetScrollPosition() const
813 {
814   return mVisualParameters.mCameraScrollPosition;
815 }
816
817 bool TextView::IsScrollPositionTrimmed() const
818 {
819   return mVisualParameters.mScrollPositionTrimmed;
820 }
821
822 Toolkit::TextView::ScrolledSignalV2& TextView::ScrolledSignal()
823 {
824   return mScrolledSignalV2;
825 }
826
827 bool TextView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
828 {
829   Dali::BaseHandle handle( object );
830
831   bool connected( true );
832   Toolkit::TextView textView = Toolkit::TextView::DownCast(handle);
833
834   if( Dali::Toolkit::TextView::SIGNAL_TEXT_SCROLLED == signalName )
835   {
836     textView.ScrolledSignal().Connect( tracker, functor );
837   }
838   else
839   {
840     // signalName does not match any signal
841     connected = false;
842   }
843
844   return connected;
845 }
846
847 TextView::LayoutParameters::LayoutParameters()
848 : mMultilinePolicy( Toolkit::TextView::SplitByNewLineChar ),
849   mWidthExceedPolicy( Toolkit::TextView::Original ),
850   mHeightExceedPolicy( Toolkit::TextView::Original ),
851   mHorizontalAlignment( Toolkit::Alignment::HorizontalCenter ),
852   mVerticalAlignment( Toolkit::Alignment::VerticalCenter ),
853   mLineJustification( Toolkit::TextView::Left ),
854   mLineHeightOffset( 0.f ),
855   mEllipsizeText(),
856   mMarkUpEnabled( false )
857 {
858   // Sets ellipsize text
859   MarkupProcessor::StyledTextArray styledEllipsize;
860   MarkupProcessor::GetStyledTextArray( std::string( "..." ), mEllipsizeText, false );
861 }
862
863 TextView::LayoutParameters::LayoutParameters( const Toolkit::TextView::MultilinePolicy   multilinePolicy,
864                                               const Toolkit::TextView::ExceedPolicy      widthExceedPolicy,
865                                               const Toolkit::TextView::ExceedPolicy      heightExceedPolicy,
866                                               const Toolkit::Alignment::Type             alignmentType,
867                                               const Toolkit::TextView::LineJustification lineJustification,
868                                               const float                                lineHeightOffset,
869                                               const std::string&                         ellipsizeText,
870                                               const bool                                 markUpEnabled )
871 : mMultilinePolicy( multilinePolicy ),
872   mWidthExceedPolicy( widthExceedPolicy ),
873   mHeightExceedPolicy( heightExceedPolicy ),
874   mHorizontalAlignment(),
875   mVerticalAlignment(),
876   mLineJustification( lineJustification ),
877   mLineHeightOffset( lineHeightOffset ),
878   mEllipsizeText(),
879   mMarkUpEnabled( markUpEnabled )
880 {
881   // Sets alignment
882   Toolkit::Alignment::Type horizontalAlignment( ( alignmentType & Toolkit::Alignment::HorizontalLeft ? Toolkit::Alignment::HorizontalLeft :
883                                                   ( alignmentType & Toolkit::Alignment::HorizontalCenter ? Toolkit::Alignment::HorizontalCenter :
884                                                     ( alignmentType & Toolkit::Alignment::HorizontalRight ? Toolkit::Alignment::HorizontalRight : Toolkit::Alignment::HorizontalCenter ) ) ) );
885   Toolkit::Alignment::Type verticalAlignment( ( alignmentType & Toolkit::Alignment::VerticalTop ? Toolkit::Alignment::VerticalTop :
886                                                 ( alignmentType & Toolkit::Alignment::VerticalCenter ? Toolkit::Alignment::VerticalCenter :
887                                                   ( alignmentType & Toolkit::Alignment::VerticalBottom ? Toolkit::Alignment::VerticalBottom : Toolkit::Alignment::VerticalCenter ) ) ) );
888
889   mHorizontalAlignment = horizontalAlignment;
890   mVerticalAlignment = verticalAlignment;
891
892   // Sets ellipsize text
893   MarkupProcessor::StyledTextArray styledEllipsize;
894   MarkupProcessor::GetStyledTextArray( ellipsizeText, mEllipsizeText, mMarkUpEnabled );
895 }
896
897 TextView::LayoutParameters::LayoutParameters( const TextView::LayoutParameters& layoutParameters )
898 : mMultilinePolicy( layoutParameters.mMultilinePolicy ),
899   mWidthExceedPolicy( layoutParameters.mWidthExceedPolicy ),
900   mHeightExceedPolicy( layoutParameters.mHeightExceedPolicy ),
901   mHorizontalAlignment( layoutParameters.mHorizontalAlignment ),
902   mVerticalAlignment( layoutParameters.mVerticalAlignment ),
903   mLineJustification( layoutParameters.mLineJustification ),
904   mLineHeightOffset( layoutParameters.mLineHeightOffset ),
905   mEllipsizeText( layoutParameters.mEllipsizeText ),
906   mMarkUpEnabled( layoutParameters.mMarkUpEnabled )
907 {
908 }
909
910 TextView::LayoutParameters& TextView::LayoutParameters::operator=( const TextView::LayoutParameters& layoutParameters )
911 {
912   mMultilinePolicy = layoutParameters.mMultilinePolicy;
913   mWidthExceedPolicy = layoutParameters.mWidthExceedPolicy;
914   mHeightExceedPolicy = layoutParameters.mHeightExceedPolicy;
915   mHorizontalAlignment = layoutParameters.mHorizontalAlignment;
916   mVerticalAlignment = layoutParameters.mVerticalAlignment;
917   mLineJustification = layoutParameters.mLineJustification;
918   mLineHeightOffset = layoutParameters.mLineHeightOffset;
919   mEllipsizeText = layoutParameters.mEllipsizeText;
920   mMarkUpEnabled = layoutParameters.mMarkUpEnabled;
921
922   return *this;
923 }
924
925 TextView::VisualParameters::VisualParameters()
926 : mFadeBoundary(),
927   mSortModifier( 0.f ),
928   mCameraScrollPosition( 0.f, 0.f ),
929   mSnapshotModeEnabled( false ),
930   mScrollEnabled( false ),
931   mScrollPositionTrimmed( false )
932 {
933 }
934
935 TextView::VisualParameters::VisualParameters( const VisualParameters& visualParameters )
936 : mFadeBoundary( visualParameters.mFadeBoundary ),
937   mSortModifier( visualParameters.mSortModifier ),
938   mCameraScrollPosition( visualParameters.mCameraScrollPosition ),
939   mSnapshotModeEnabled( visualParameters.mSnapshotModeEnabled ),
940   mScrollEnabled( visualParameters.mScrollEnabled ),
941   mScrollPositionTrimmed( visualParameters.mScrollPositionTrimmed )
942 {
943 }
944
945 TextView::VisualParameters& TextView::VisualParameters::operator=( const TextView::VisualParameters& visualParameters )
946 {
947   mFadeBoundary = visualParameters.mFadeBoundary;
948   mSortModifier = visualParameters.mSortModifier;
949   mCameraScrollPosition = visualParameters.mCameraScrollPosition;
950   mSnapshotModeEnabled = visualParameters.mSnapshotModeEnabled;
951   mScrollEnabled = visualParameters.mScrollEnabled;
952   mScrollPositionTrimmed = visualParameters.mScrollPositionTrimmed;
953
954   return *this;
955 }
956
957 TextView::RelayoutData::RelayoutData()
958 : mTextViewSize(),
959   mShrinkFactor( 1.f ),
960   mTextLayoutInfo(),
961   mCharacterLogicalToVisualMap(),
962   mCharacterVisualToLogicalMap(),
963   mGlyphActors(),
964   mCharacterLayoutInfoTable(),
965   mLines(),
966   mTextSizeForRelayoutOption()
967 {
968 }
969
970 TextView::RelayoutData::RelayoutData( const TextView::RelayoutData& relayoutData )
971 : mTextViewSize( relayoutData.mTextViewSize ),
972   mShrinkFactor( relayoutData.mShrinkFactor ),
973   mTextLayoutInfo( relayoutData.mTextLayoutInfo ),
974   mCharacterLogicalToVisualMap( relayoutData.mCharacterLogicalToVisualMap ),
975   mCharacterVisualToLogicalMap( relayoutData.mCharacterVisualToLogicalMap ),
976   mGlyphActors( relayoutData.mGlyphActors ),
977   mCharacterLayoutInfoTable( relayoutData.mCharacterLayoutInfoTable ),
978   mLines( relayoutData.mLines ),
979   mTextSizeForRelayoutOption( relayoutData.mTextSizeForRelayoutOption )
980 {
981 }
982
983 TextView::RelayoutData& TextView::RelayoutData::operator=( const TextView::RelayoutData& relayoutData )
984 {
985   mTextViewSize = relayoutData.mTextViewSize;
986   mShrinkFactor = relayoutData.mShrinkFactor;
987   mTextLayoutInfo = relayoutData.mTextLayoutInfo;
988   mCharacterLogicalToVisualMap = relayoutData.mCharacterLogicalToVisualMap;
989   mCharacterVisualToLogicalMap = relayoutData.mCharacterVisualToLogicalMap;
990   mGlyphActors = relayoutData.mGlyphActors;
991   mCharacterLayoutInfoTable = relayoutData.mCharacterLayoutInfoTable;
992   mLines = relayoutData.mLines;
993   mTextSizeForRelayoutOption = relayoutData.mTextSizeForRelayoutOption;
994
995   return *this;
996 }
997
998 TextView::TextView()
999 : ControlImpl( false ),  // doesn't require touch events
1000   mCurrentStyledText(),
1001   mTextViewProcessorOperations(),
1002   mLayoutParameters( Toolkit::TextView::SplitByNewLineChar,
1003                      Toolkit::TextView::Original,
1004                      Toolkit::TextView::Original,
1005                      static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ),
1006                      Toolkit::TextView::Left,
1007                      PointSize( 0.f ),
1008                      std::string( "..." ),
1009                      false ),
1010   mVisualParameters(),
1011   mRelayoutData(),
1012   mRelayoutOperations( NO_RELAYOUT ),
1013   mOffscreenRootActor(),
1014   mOffscreenImageActor(),
1015   mOffscreenCameraActor(),
1016   mCurrentOffscreenSize(),
1017   mFrameBufferImage(),
1018   mRenderTask(),
1019   mPanGestureDetector(),
1020   mLockPreviousSnapshotMode( false ),
1021   mPreviousSnapshotModeEnabled( false ),
1022   mMarkUpEnabled( false )
1023 {
1024   TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText,
1025                                          mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo );
1026 }
1027
1028 TextView::~TextView()
1029 {
1030   // Destroys offscreen rendering resources.
1031   DestroyOffscreenRenderingResources();
1032
1033   // Destroys scroll pan gesture detector.
1034   if( mPanGestureDetector )
1035   {
1036     mPanGestureDetector.Reset();
1037   }
1038 }
1039
1040 Vector3 TextView::GetNaturalSize()
1041 {
1042   if( !mTextViewProcessorOperations.empty() )
1043   {
1044     // There are SetText, Inserts or Removes to do. It means the current layout info is not updated.
1045
1046     if( !mRelayoutData.mGlyphActors.empty() )
1047     {
1048       // Remove glyph-actors from the text-view as some text-operation like CreateTextInfo()
1049       // add them to the text-actor cache.
1050       TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
1051       mRelayoutData.mGlyphActors.clear();
1052
1053       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW );
1054       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
1055     }
1056
1057     PerformTextViewProcessorOperations();
1058   }
1059
1060   return Vector3( mRelayoutData.mTextLayoutInfo.mWholeTextSize.width, mRelayoutData.mTextLayoutInfo.mWholeTextSize.height, 0.f );
1061 }
1062
1063 float TextView::GetHeightForWidth( float width )
1064 {
1065   float height = 0.f;
1066
1067   if( ( Toolkit::TextView::SplitByNewLineChar == mLayoutParameters.mMultilinePolicy ) &&
1068       ( Toolkit::TextView::Original == mLayoutParameters.mWidthExceedPolicy ) &&
1069       ( Toolkit::TextView::Original == mLayoutParameters.mHeightExceedPolicy ) )
1070   {
1071     // If multiline and exceed policies are 'SplitByNewLineChar' and 'Original' is better get the height from the
1072     // natural size. GetNaturalSize() for this configuration is faster than DoRelayOut().
1073     height = GetNaturalSize().height;
1074   }
1075   else
1076   {
1077     // Check if the given width is different than the current one.
1078     const bool differentWidth = ( fabsf( width - mRelayoutData.mTextViewSize.width ) > Math::MACHINE_EPSILON_1000 );
1079
1080     // Check if the text-view has glyph-actors.
1081     const bool hasGlyphActors = !mRelayoutData.mGlyphActors.empty();
1082
1083     // Check which layout operations need to be done.
1084     const bool relayoutSizeAndPositionNeeded = ( mRelayoutOperations & RELAYOUT_SIZE_POSITION ) || differentWidth;
1085
1086     if( relayoutSizeAndPositionNeeded )
1087     {
1088       if( hasGlyphActors )
1089       {
1090         // Remove glyph-actors from the text-view as some text-operation like CreateTextInfo()
1091         // add them to the text-actor cache.
1092         TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
1093         mRelayoutData.mGlyphActors.clear();
1094       }
1095
1096       // Use the given width.
1097       const Vector2 textViewSize( width, GetControlSize().height );
1098
1099       // Relays-out but doesn't add glyph-actors to the text-view.
1100       DoRelayOut( textViewSize, RELAYOUT_SIZE_POSITION );
1101     }
1102
1103     // Retrieve the text height after relayout the text.
1104     height = mRelayoutData.mTextSizeForRelayoutOption.height;
1105
1106     if( differentWidth )
1107     {
1108       // Revert the relayout operation mask
1109       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_SIZE_POSITION );
1110     }
1111
1112     if( hasGlyphActors )
1113     {
1114       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW );
1115       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
1116     }
1117
1118     if( differentWidth || hasGlyphActors )
1119     {
1120       RelayoutRequest();
1121     }
1122   }
1123
1124   return height;
1125 }
1126
1127 float TextView::GetWidthForHeight( float height )
1128 {
1129   // TODO: Needs implementing properly, for now just return the natural width.
1130   return GetNaturalSize().width;
1131 }
1132
1133 void TextView::OnPropertySet( Property::Index index, Property::Value propertyValue )
1134 {
1135   if( index == mPropertyText )
1136   {
1137     SetText(propertyValue.Get<std::string>());
1138   }
1139   else if( index == mPropertyMultilinePolicy )
1140   {
1141     OnMultilinePolicyPropertySet(propertyValue);
1142   }
1143   else if( index == mPropertyWidthExceedPolicy )
1144   {
1145     OnWidthExceedPolicyPropertySet(propertyValue);
1146   }
1147   else if( index == mPropertyHeightExceedPolicy )
1148   {
1149     OnHeightExceedPolicyPropertySet(propertyValue);
1150   }
1151   else if( index == mPropertyLineJustification )
1152   {
1153     OnLineJustificationPropertySet(propertyValue);
1154   }
1155   else if( ( index == mPropertyFadeBoundaryLeft )  ||
1156            ( index == mPropertyFadeBoundaryRight ) ||
1157            ( index == mPropertyFadeBoundaryTop )   ||
1158            ( index == mPropertyFadeBoundaryBottom ) )
1159   {
1160     OnFadeBoundaryPropertySet( index, propertyValue );
1161   }
1162   else if( index == mPropertyLineHeightOffset )
1163   {
1164     Dali::PointSize pointSize( propertyValue.Get<float>() );
1165     SetLineHeightOffset(pointSize);
1166   }
1167   else if ( ( index == mPropertyHorizontalAlignment ) ||
1168             ( index == mPropertyVerticalAlignment ) )
1169   {
1170     OnAlignmentPropertySet( index, propertyValue );
1171   }
1172 }
1173
1174 void TextView::OnInitialize()
1175 {
1176   Actor self = Self();
1177
1178   mPropertyText = self.RegisterProperty( PROPERTY_TEXT, "", Property::READ_WRITE );
1179
1180   mPropertyMultilinePolicy = self.RegisterProperty( PROPERTY_MULTILINE_POLICY, "SplitByNewLineChar", Property::READ_WRITE );
1181
1182   mPropertyWidthExceedPolicy = self.RegisterProperty( PROPERTY_WIDTH_EXCEED_POLICY, "Original", Property::READ_WRITE );
1183
1184   mPropertyHeightExceedPolicy = self.RegisterProperty( PROPERTY_HEIGHT_EXCEED_POLICY, "Original", Property::READ_WRITE );
1185
1186   mPropertyLineJustification = self.RegisterProperty( PROPERTY_LINE_JUSTIFICATION, "Left", Property::READ_WRITE );
1187
1188   mPropertyFadeBoundaryLeft = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_LEFT, static_cast< int >( 0 ), Property::READ_WRITE );
1189
1190   mPropertyFadeBoundaryRight = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_RIGHT, static_cast< int >( 0 ), Property::READ_WRITE );
1191
1192   mPropertyFadeBoundaryTop = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_TOP, static_cast< int >( 0 ), Property::READ_WRITE );
1193
1194   mPropertyFadeBoundaryBottom = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_BOTTOM, static_cast< int >( 0 ), Property::READ_WRITE );
1195
1196   mPropertyLineHeightOffset = self.RegisterProperty( PROPERTY_LINE_HEIGHT_OFFSET, 0.0f, Property::READ_WRITE );
1197
1198   mPropertyHorizontalAlignment = self.RegisterProperty( PROPERTY_HORIZONTAL_ALIGNMENT, "HorizontalCenter", Property::READ_WRITE );
1199
1200   mPropertyVerticalAlignment = self.RegisterProperty( PROPERTY_VERTICAL_ALIGNMENT, "VerticalCenter", Property::READ_WRITE );
1201
1202 }
1203
1204
1205 void TextView::OnStyleChange( StyleChange change )
1206 {
1207   mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = TextViewProcessor::WordLayoutInfo();
1208   TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText,
1209                                          mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo );
1210
1211   SetText( mCurrentStyledText );
1212 }
1213
1214 void TextView::OnControlSizeSet( const Vector3& size )
1215 {
1216   if( size.GetVectorXY() != mRelayoutData.mTextViewSize )
1217   {
1218     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
1219     mRelayoutOperations = RELAYOUT_ALL;
1220
1221     // Request to be relaid out
1222     RelayoutRequest();
1223   }
1224 }
1225
1226 void TextView::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1227 {
1228   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1229   {
1230     // Not worth to relayout if width or height is equal to zero.
1231     return;
1232   }
1233
1234   if( size != mRelayoutData.mTextViewSize )
1235   {
1236     // if new size is different than the prevoius one, set positions and maybe sizes of all glyph-actor is needed.
1237     if( RELAYOUT_ALL != mRelayoutOperations )
1238     {
1239       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
1240                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
1241                                                                 RELAYOUT_SIZE_POSITION |
1242                                                                 RELAYOUT_ALIGNMENT |
1243                                                                 RELAYOUT_VISIBILITY |
1244                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
1245                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
1246                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
1247     }
1248   }
1249
1250   // Remove glyph-actors from text-view
1251   if( !mRelayoutData.mGlyphActors.empty() && ( mRelayoutOperations & RELAYOUT_REMOVE_TEXT_ACTORS ) )
1252   {
1253     TextViewRelayout::RemoveGlyphActors( GetRootActor(), mRelayoutData.mGlyphActors );
1254     mRelayoutData.mGlyphActors.clear();
1255   }
1256
1257   if( NO_RELAYOUT != mRelayoutOperations )
1258   {
1259     // Relays-out and add glyph-actors to the text-view.
1260     DoRelayOut( size, mRelayoutOperations );
1261     ProcessSnapshot( size );
1262   }
1263
1264   // Quite likely the texts of the text-actors are not going to be reused, so clear them.
1265   mRelayoutData.mTextActorCache.ClearTexts();
1266 }
1267
1268 void TextView::PerformTextViewProcessorOperations()
1269 {
1270   // Traverse the relayout operation vector ...
1271
1272   // Optimizes some operations.
1273   OptimizeTextViewProcessorOperations();
1274
1275   for( std::vector<TextViewProcessorMetadata>::const_iterator it = mTextViewProcessorOperations.begin(), endIt = mTextViewProcessorOperations.end(); it != endIt; ++it )
1276   {
1277     const TextViewProcessorMetadata& relayoutMetadata( *it );
1278
1279     switch( relayoutMetadata.mType )
1280     {
1281       case TextView::TextSet:
1282       {
1283         TextViewProcessor::CreateTextInfo( relayoutMetadata.mText,
1284                                            mLayoutParameters,
1285                                            mRelayoutData );
1286         break;
1287       }
1288       case TextView::TextInserted:
1289       {
1290         TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition,
1291                                            relayoutMetadata.mText,
1292                                            mLayoutParameters,
1293                                            mRelayoutData );
1294         break;
1295       }
1296       case TextView::TextReplaced:
1297       {
1298         TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition,
1299                                            relayoutMetadata.mNumberOfCharacters,
1300                                            relayoutMetadata.mText,
1301                                            mLayoutParameters,
1302                                            mRelayoutData );
1303         break;
1304       }
1305       case TextView::TextRemoved:
1306       {
1307         TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition,
1308                                            relayoutMetadata.mNumberOfCharacters,
1309                                            mLayoutParameters,
1310                                            mRelayoutData,
1311                                            TextViewProcessor::CLEAR_TEXT ); // clears the text of the text-actors.
1312         break;
1313       }
1314       case TextView::NewLineHeight:
1315       {
1316         TextViewProcessor::UpdateTextInfo( mLayoutParameters.mLineHeightOffset,
1317                                            mRelayoutData.mTextLayoutInfo );
1318         break;
1319       }
1320       case TextView::NewStyle:
1321       {
1322         TextViewProcessor::UpdateTextInfo( ( *relayoutMetadata.mText.begin() ).mStyle,
1323                                            relayoutMetadata.mStyleMask,
1324                                            mRelayoutData );
1325         break;
1326       }
1327     }
1328   }
1329
1330   // Clear all operations when they are done.
1331   mTextViewProcessorOperations.clear();
1332 }
1333
1334 void TextView::OptimizeTextViewProcessorOperations()
1335 {
1336   // TODO: check if some operation can be discarted. i.e. InsertTextAt( 0, "a" ); RemoveTextFrom( 0, 1 );
1337
1338   // At the moment it only replaces a 'remove 1 character' followed by 'insert 1 character' in the same position by a 'replace' operation.
1339   // This sequence is used by text-input with predictive text. Change this two operations by a replace allows the text-view processor to
1340   // use the cache without clearing the text-actors.
1341
1342   std::vector<TextViewProcessorMetadata> textViewProcessorOperations;
1343
1344   for( std::vector<TextViewProcessorMetadata>::const_iterator it = mTextViewProcessorOperations.begin(), endIt = mTextViewProcessorOperations.end(); it != endIt; ++it )
1345   {
1346     const TextViewProcessorMetadata& relayoutMetadata( *it );
1347
1348     switch( relayoutMetadata.mType )
1349     {
1350       case TextView::TextRemoved:
1351       {
1352         bool optimizationDone = false;
1353
1354         if( it + 1 != endIt )
1355         {
1356           const TextViewProcessorMetadata& nextRelayoutMetadata( *( it + 1 ) );
1357           if( TextView::TextInserted == nextRelayoutMetadata.mType )
1358           {
1359             if( relayoutMetadata.mPosition == nextRelayoutMetadata.mPosition )
1360             {
1361               optimizationDone = true;
1362               TextViewProcessorMetadata newRelayoutMetadata;
1363               newRelayoutMetadata.mType = TextView::TextReplaced;
1364               newRelayoutMetadata.mPosition = relayoutMetadata.mPosition;
1365               newRelayoutMetadata.mNumberOfCharacters = relayoutMetadata.mNumberOfCharacters;
1366               newRelayoutMetadata.mText = nextRelayoutMetadata.mText;
1367               textViewProcessorOperations.push_back( newRelayoutMetadata );
1368
1369               // do not access the TextInserted operation in next iteration.
1370               ++it;
1371             }
1372           }
1373         }
1374
1375         if( !optimizationDone )
1376         {
1377           textViewProcessorOperations.push_back( relayoutMetadata );
1378         }
1379         break;
1380       }
1381       default:
1382       {
1383         textViewProcessorOperations.push_back( relayoutMetadata );
1384       }
1385     }
1386   }
1387
1388   mTextViewProcessorOperations = textViewProcessorOperations;
1389 }
1390
1391 void TextView::DoRelayOut( const Size& textViewSize, const RelayoutOperationMask relayoutOperationMask )
1392 {
1393   // Traverse the relayout operation vector. It fills the natural size, layout and glyph-actor data structures.
1394   if( !mTextViewProcessorOperations.empty() )
1395   {
1396     PerformTextViewProcessorOperations();
1397   }
1398
1399   CombineExceedPolicies();
1400
1401   Actor rootActor;
1402   if( mVisualParameters.mSnapshotModeEnabled )
1403   {
1404     rootActor = mOffscreenRootActor;
1405   }
1406   else
1407   {
1408     rootActor = Self();
1409   }
1410
1411   mRelayoutData.mTextViewSize = textViewSize;
1412   switch( mLayoutParameters.mMultilinePolicy )
1413   {
1414     case Toolkit::TextView::SplitByNewLineChar:              // multiline policy == SplitByNewLineChar.
1415     {
1416       SplitByNewLineChar::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData );
1417       break;
1418     } // SplitByNewLineChar
1419
1420     case Toolkit::TextView::SplitByWord:                     // multiline policy == SplitByWord.
1421     {
1422       SplitByWord::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData );
1423       break;
1424     } // SplitByWord
1425
1426     case Toolkit::TextView::SplitByChar:                     // multiline policy == SplitByChar.
1427     {
1428       SplitByChar::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData );
1429       break;
1430     } // SplitByChar
1431   } // switch( mMultilinePolicy )
1432
1433   // Remove done operations from the mask.
1434   mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations & ~relayoutOperationMask );
1435 }
1436
1437 void TextView::ProcessSnapshot( const Size& textViewSize )
1438 {
1439   if( mVisualParameters.mSnapshotModeEnabled )
1440   {
1441     // If layout options change, it's needed generate a new image.
1442
1443     if( mOffscreenRootActor )
1444     {
1445       // Set the root actor visible.
1446       // The root actor is set to non visible after the render task is processed.
1447       mOffscreenRootActor.SetVisible( true );
1448
1449       // The offscreen root actor must have same size as text view. Otherwise, text alignment won't work.
1450       mOffscreenRootActor.SetSize( textViewSize );
1451     }
1452
1453     if( ( mRelayoutData.mTextSizeForRelayoutOption.width > Math::MACHINE_EPSILON_1000 ) &&
1454         ( mRelayoutData.mTextSizeForRelayoutOption.height > Math::MACHINE_EPSILON_1000 ) )
1455     {
1456       // Set the image actor visible.
1457       // The image actor is set to non visible if there is no text to render.
1458       mOffscreenImageActor.SetVisible( true );
1459
1460       // Calculates the offscreen image's size. It takes into account different points:
1461       // * If text has italics, add a small offset is needed in order to not to cut the text next to the right edge.
1462       // * There is a maximum texture size the graphic subsystem can load on the memory.
1463       // * If the scroll is enabled, the offscreen image's size is never bigger than the text-view's size.
1464
1465       const Size offscreenSize( std::min( MAX_OFFSCREEN_RENDERING_SIZE,
1466                                           mVisualParameters.mScrollEnabled ? textViewSize.width : std::max( mRelayoutData.mTextSizeForRelayoutOption.width, textViewSize.width ) + mRelayoutData.mTextLayoutInfo.mMaxItalicsOffset ),
1467                                 std::min( MAX_OFFSCREEN_RENDERING_SIZE,
1468                                           mVisualParameters.mScrollEnabled ? textViewSize.height : std::max( mRelayoutData.mTextSizeForRelayoutOption.height, textViewSize.height ) ) );
1469
1470       const bool sizeChanged = offscreenSize != mCurrentOffscreenSize;
1471
1472       if( sizeChanged )
1473       {
1474         // Creates a frame buffer for offscreen rendering when the size is negotiated.
1475         mFrameBufferImage = FrameBufferImage::New( offscreenSize.width,
1476                                                    offscreenSize.height,
1477                                                    Pixel::RGBA8888 );
1478
1479         // Stores current text-view size to avoid create new Dali resources if text changes.
1480         mCurrentOffscreenSize = offscreenSize;
1481
1482         if( !mOffscreenCameraActor )
1483         {
1484           // Creates a new camera actor.
1485           mOffscreenCameraActor = CameraActor::New();
1486           mOffscreenCameraActor.SetParentOrigin( ParentOrigin::CENTER );
1487           mOffscreenCameraActor.SetAnchorPoint( AnchorPoint::CENTER );
1488           mOffscreenCameraActor.SetRotation(Degree(180.f), Vector3::YAXIS);
1489
1490           mOffscreenCameraActor.SetType( Dali::Camera::FREE_LOOK ); // Inherits position from the offscreen root actor.
1491
1492           mOffscreenRootActor.Add( mOffscreenCameraActor ); // camera to shoot the offscreen text
1493         }
1494
1495         // Calculate camera parameters for current text size.
1496         mOffscreenCameraActor.SetOrthographicProjection( offscreenSize );
1497       }
1498
1499       if( mVisualParameters.mScrollEnabled )
1500       {
1501         // Updates the offscreen camera position with the new scroll offset.
1502         mOffscreenCameraActor.SetX( mVisualParameters.mCameraScrollPosition.x );
1503         mOffscreenCameraActor.SetY( mVisualParameters.mCameraScrollPosition.y );
1504       }
1505       else
1506       {
1507         // Text's size could be bigger than text-view's size. In that case the camera must be aligned to cover the whole text.
1508         AlignOffscreenCameraActor( textViewSize, offscreenSize );
1509       }
1510
1511       if( !mRenderTask )
1512       {
1513         // Creates a new render task.
1514         mRenderTask = Stage::GetCurrent().GetRenderTaskList().CreateTask();
1515
1516         mRenderTask.SetSourceActor( mOffscreenRootActor );
1517         mRenderTask.SetInputEnabled( false );
1518         mRenderTask.SetClearColor( Color::TRANSPARENT );
1519         mRenderTask.SetClearEnabled( true );
1520         mRenderTask.SetExclusive( true );
1521
1522         // Connects the signal to the TextView::RenderTaskFinished method in order to make the root actor non visible when the render task is processed.
1523         mRenderTask.FinishedSignal().Connect( this, &TextView::RenderTaskFinished );
1524       }
1525
1526       if( sizeChanged )
1527       {
1528         mRenderTask.SetCameraActor( mOffscreenCameraActor );
1529         mRenderTask.SetTargetFrameBuffer( mFrameBufferImage );
1530       }
1531
1532       // Process the render task only once every time the text changes or the text-view's size canges.
1533       mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE );
1534     }
1535     else
1536     {
1537       // If there is no text just make any previous generated image invisible instead to process a render task with no text.
1538       mOffscreenImageActor.SetVisible( false );
1539     }
1540   }
1541 }
1542
1543 void TextView::AlignOffscreenCameraActor( const Size& textViewSize, const Size& offscreenSize )
1544 {
1545   float xPosition = 0.f;
1546   float yPosition = 0.f;
1547   Vector3 parentOrigin = ParentOrigin::CENTER;
1548   Vector3 anchorPoint = AnchorPoint::CENTER;
1549
1550   switch( mLayoutParameters.mHorizontalAlignment )
1551   {
1552     case Toolkit::Alignment::HorizontalLeft:
1553     {
1554       xPosition = 0.5f * ( offscreenSize.width - textViewSize.width );
1555       parentOrigin.x = 0.f;
1556       anchorPoint.x = 0.f;
1557       break;
1558     }
1559     case Toolkit::Alignment::HorizontalCenter:
1560     {
1561       // nothing to do.
1562       break;
1563     }
1564     case Toolkit::Alignment::HorizontalRight:
1565     {
1566       xPosition = 0.5f * ( textViewSize.width - offscreenSize.width );
1567       parentOrigin.x = 1.f;
1568       anchorPoint.x = 1.f;
1569       break;
1570     }
1571     default:
1572     {
1573       DALI_ASSERT_ALWAYS( !"TextView::AlignOffscreenCameraActor: Invalid alignment option." )
1574     }
1575   }
1576
1577   switch( mLayoutParameters.mVerticalAlignment )
1578   {
1579     case Toolkit::Alignment::VerticalTop:
1580     {
1581       yPosition = 0.5f * ( offscreenSize.height - textViewSize.height );
1582       parentOrigin.y = 0.f;
1583       anchorPoint.y = 0.f;
1584       break;
1585     }
1586     case Toolkit::Alignment::VerticalCenter:
1587     {
1588       // nothing to do.
1589       break;
1590     }
1591     case Toolkit::Alignment::VerticalBottom:
1592     {
1593       yPosition = 0.5f * ( textViewSize.height - offscreenSize.height );
1594       parentOrigin.y = 1.f;
1595       anchorPoint.y = 1.f;
1596       break;
1597     }
1598     default:
1599     {
1600       DALI_ASSERT_ALWAYS( !"TextView::AlignOffscreenCameraActor: Invalid alignment option." )
1601     }
1602   }
1603
1604   mOffscreenCameraActor.SetX( xPosition );
1605   mOffscreenCameraActor.SetY( yPosition );
1606
1607   mOffscreenImageActor.SetParentOrigin( parentOrigin );
1608   mOffscreenImageActor.SetAnchorPoint( anchorPoint );
1609 }
1610
1611 void TextView::RenderTaskFinished( Dali::RenderTask& renderTask )
1612 {
1613   // not to process the offscreen root actor by setting its visibility to false.
1614   mOffscreenRootActor.SetVisible( false );
1615
1616   // Sets the new size and the new frame buffer to the image actor.
1617   // Image actor must have same size as text. Otherwise text can be truncated.
1618   mOffscreenImageActor.SetSize( mCurrentOffscreenSize );
1619   mOffscreenImageActor.SetImage( mFrameBufferImage );
1620 }
1621
1622 void TextView::DestroyOffscreenRenderingResources()
1623 {
1624   if( mRenderTask )
1625   {
1626     mRenderTask.FinishedSignal().Disconnect( this, &TextView::RenderTaskFinished );
1627
1628     if( Stage::IsInstalled() )
1629     {
1630       Stage::GetCurrent().GetRenderTaskList().RemoveTask( mRenderTask );
1631     }
1632
1633     mRenderTask.Reset();
1634   }
1635
1636   // Remove and reset the root actor, image actor and camera actor as text-view is not rendering offscreen.
1637   if( mOffscreenCameraActor )
1638   {
1639     mOffscreenRootActor.Remove( mOffscreenCameraActor );
1640
1641     mOffscreenCameraActor.Reset();
1642   }
1643
1644   if( mOffscreenRootActor )
1645   {
1646     mOffscreenRootActor.Reset();
1647   }
1648
1649   if( mOffscreenImageActor )
1650   {
1651     mOffscreenImageActor.Reset();
1652   }
1653
1654   mCurrentOffscreenSize = Size( 0.f, 0.f );
1655
1656   if( mFrameBufferImage )
1657   {
1658     mFrameBufferImage.Reset();
1659   }
1660 }
1661
1662 void TextView::OnTextPan( Actor actor, PanGesture gesture )
1663 {
1664   if( 1u == gesture.numberOfTouches )
1665   {
1666     DoSetScrollPosition( mVisualParameters.mCameraScrollPosition - gesture.displacement );
1667   }
1668 }
1669
1670 void TextView::TrimScrollPosition()
1671 {
1672   const Vector3& textViewSize = GetControlSize();
1673
1674   // Before use the text's size, relayout the text is needed to get the actual text size.
1675   GetTextLayoutInfo();
1676
1677   // Calculates the range within the text could be scrolled. (When the text is aligned in the center).
1678   float maxHorizontalDisplacement = std::max( 0.f, 0.5f * ( mRelayoutData.mTextSizeForRelayoutOption.width - textViewSize.width ) );
1679   float maxVerticalDisplacement = std::max( 0.f, 0.5f * ( mRelayoutData.mTextSizeForRelayoutOption.height - textViewSize.height ) );
1680   float minHorizontalDisplacement = -maxHorizontalDisplacement;
1681   float minVerticalDisplacement = -maxVerticalDisplacement;
1682
1683   // Updates the range if the text is aligned on the right or left.
1684   switch( mLayoutParameters.mHorizontalAlignment )
1685   {
1686     case Toolkit::Alignment::HorizontalLeft:
1687     {
1688       maxHorizontalDisplacement *= 2.f;
1689       minHorizontalDisplacement = 0.f;
1690       break;
1691     }
1692     case Toolkit::Alignment::HorizontalCenter:
1693     {
1694       // nothing to do.
1695       break;
1696     }
1697     case Toolkit::Alignment::HorizontalRight:
1698     {
1699       maxHorizontalDisplacement = 0.f;
1700       minHorizontalDisplacement *= 2.f;
1701       break;
1702     }
1703     default:
1704     {
1705       DALI_ASSERT_ALWAYS( !"TextView::TrimScrollPosition: Invalid alignment option." )
1706     }
1707   }
1708
1709   // Updates the range if the text is aligned on the top or bottom.
1710   switch( mLayoutParameters.mVerticalAlignment )
1711   {
1712     case Toolkit::Alignment::VerticalTop:
1713     {
1714       maxVerticalDisplacement *= 2.f;
1715       minVerticalDisplacement = 0.f;
1716       break;
1717     }
1718     case Toolkit::Alignment::VerticalCenter:
1719     {
1720       //nothing to do
1721       break;
1722     }
1723     case Toolkit::Alignment::VerticalBottom:
1724     {
1725       maxVerticalDisplacement = 0.f;
1726       minVerticalDisplacement *= 2.f;
1727       break;
1728     }
1729     default:
1730     {
1731       DALI_ASSERT_ALWAYS( !"TextView::TrimScrollPosition: Invalid alignment option." )
1732     }
1733   }
1734
1735   // Trims the scroll position to be within the range.
1736   mVisualParameters.mCameraScrollPosition.x = std::min( maxHorizontalDisplacement, mVisualParameters.mCameraScrollPosition.x );
1737   mVisualParameters.mCameraScrollPosition.x = std::max( minHorizontalDisplacement, mVisualParameters.mCameraScrollPosition.x );
1738
1739   mVisualParameters.mCameraScrollPosition.y = std::min( maxVerticalDisplacement, mVisualParameters.mCameraScrollPosition.y );
1740   mVisualParameters.mCameraScrollPosition.y = std::max( minVerticalDisplacement, mVisualParameters.mCameraScrollPosition.y );
1741 }
1742
1743 void TextView::DoSetScrollPosition( const Vector2& position )
1744 {
1745   // Stores old scroll position.
1746   Vector2 delta( mVisualParameters.mCameraScrollPosition );
1747
1748   // Updates the scroll position
1749   mVisualParameters.mCameraScrollPosition = position;
1750
1751   // Ensures the text-view is covered with text.
1752   TrimScrollPosition();
1753
1754   // Calculate the difference with the previous scroll position
1755   delta.x = mVisualParameters.mCameraScrollPosition.x - delta.x;
1756   delta.y = mVisualParameters.mCameraScrollPosition.y - delta.y;
1757
1758   if( mOffscreenRootActor )
1759   {
1760     // If there is a render-task it needs to be refreshed. Therefore glyph-actors need to be
1761     // set to visible.
1762     mOffscreenRootActor.SetVisible( true );
1763   }
1764
1765   if( mOffscreenCameraActor )
1766   {
1767     // Update the offscreen camera with the new scroll position.
1768     mOffscreenCameraActor.SetX( mVisualParameters.mCameraScrollPosition.x );
1769     mOffscreenCameraActor.SetY( mVisualParameters.mCameraScrollPosition.y );
1770   }
1771
1772   if( mRenderTask )
1773   {
1774     // Refresh the render-task.
1775     mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE );
1776   }
1777
1778   // Emit the signal.
1779   Toolkit::TextView handle( GetOwner() );
1780   mScrolledSignalV2.Emit( handle, delta );
1781 }
1782
1783 void TextView::CombineExceedPolicies()
1784 {
1785   // Calculates the combination of exceed policies.
1786
1787   switch( mLayoutParameters.mWidthExceedPolicy )
1788   {
1789     case Toolkit::TextView::Original:
1790     {
1791       switch( mLayoutParameters.mHeightExceedPolicy )
1792       {
1793         case Toolkit::TextView::Original:
1794         {
1795           mLayoutParameters.mExceedPolicy = Original;
1796           break;
1797         }
1798         case Toolkit::TextView::Fade:
1799         {
1800           mLayoutParameters.mExceedPolicy = OriginalFade;
1801           break;
1802         }
1803         case Toolkit::TextView::ShrinkToFit:
1804         {
1805           mLayoutParameters.mExceedPolicy = OriginalShrink;
1806           break;
1807         }
1808         default:
1809         {
1810           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1811         }
1812       }
1813       break;
1814     }
1815     case Toolkit::TextView::Split:
1816     {
1817       switch( mLayoutParameters.mHeightExceedPolicy )
1818       {
1819         case Toolkit::TextView::Original:
1820         {
1821           mLayoutParameters.mExceedPolicy = SplitOriginal;
1822           break;
1823         }
1824         case Toolkit::TextView::Fade:
1825         {
1826           mLayoutParameters.mExceedPolicy = SplitFade;
1827           break;
1828         }
1829         case Toolkit::TextView::ShrinkToFit:
1830         {
1831           mLayoutParameters.mExceedPolicy = SplitShrink;
1832           break;
1833         }
1834         default:
1835         {
1836           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1837         }
1838       }
1839       break;
1840     }
1841     case Toolkit::TextView::Fade:
1842     {
1843       switch( mLayoutParameters.mHeightExceedPolicy )
1844       {
1845         case Toolkit::TextView::Original:
1846         {
1847           mLayoutParameters.mExceedPolicy = FadeOriginal;
1848           break;
1849         }
1850         case Toolkit::TextView::Fade:
1851         {
1852           mLayoutParameters.mExceedPolicy = Fade;
1853           break;
1854         }
1855         default:
1856         {
1857           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1858         }
1859       }
1860       break;
1861     }
1862     case Toolkit::TextView::ShrinkToFit:
1863     {
1864       switch( mLayoutParameters.mHeightExceedPolicy )
1865       {
1866         case Toolkit::TextView::Original:
1867         {
1868           mLayoutParameters.mExceedPolicy = ShrinkOriginal;
1869           break;
1870         }
1871         case Toolkit::TextView::Fade:
1872         {
1873           mLayoutParameters.mExceedPolicy = ShrinkFade;
1874           break;
1875         }
1876         case Toolkit::TextView::ShrinkToFit:
1877         {
1878           mLayoutParameters.mExceedPolicy = Shrink;
1879           break;
1880         }
1881         default:
1882         {
1883           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1884         }
1885       }
1886       break;
1887     }
1888     case Toolkit::TextView::EllipsizeEnd:
1889     {
1890       switch( mLayoutParameters.mHeightExceedPolicy )
1891       {
1892         case Toolkit::TextView::Original:
1893         {
1894           mLayoutParameters.mExceedPolicy = EllipsizeEndOriginal;
1895           break;
1896         }
1897         case Toolkit::TextView::EllipsizeEnd:
1898         {
1899           mLayoutParameters.mExceedPolicy = EllipsizeEnd;
1900           break;
1901         }
1902         default:
1903         {
1904           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1905         }
1906       }
1907       break;
1908     }
1909     default:
1910     {
1911       DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width exceed policy" );
1912     }
1913   }
1914 }
1915
1916 Actor TextView::GetRootActor() const
1917 {
1918   // Get the root actor, if text-view was rendering offscreen, or the text-view itself.
1919
1920   Actor rootActor;
1921
1922   if( mVisualParameters.mSnapshotModeEnabled )
1923   {
1924     rootActor = mOffscreenRootActor;
1925   }
1926   else
1927   {
1928     rootActor = Self();
1929   }
1930
1931   return rootActor;
1932 }
1933
1934 void TextView::OnMultilinePolicyPropertySet( Property::Value propertyValue )
1935 {
1936   std::string policyName( propertyValue.Get<std::string>() );
1937   if(policyName == "SplitByNewLineChar")
1938   {
1939     SetMultilinePolicy(Toolkit::TextView::SplitByNewLineChar);
1940   }
1941   else if(policyName == "SplitByWord")
1942   {
1943     SetMultilinePolicy(Toolkit::TextView::SplitByWord);
1944   }
1945   else if(policyName == "SplitByChar")
1946   {
1947     SetMultilinePolicy(Toolkit::TextView::SplitByChar);
1948   }
1949   else
1950   {
1951     DALI_ASSERT_ALWAYS( !"TextView::OnMultilinePolicyPropertySet(). Invalid Property value." );
1952   }
1953 }
1954
1955 void TextView::OnWidthExceedPolicyPropertySet( Property::Value propertyValue )
1956 {
1957   std::string policyName( propertyValue.Get<std::string>() );
1958   if(policyName == "Original")
1959   {
1960     SetWidthExceedPolicy(Toolkit::TextView::Original);
1961   }
1962   else if(policyName == "Truncate")
1963   {
1964     SetWidthExceedPolicy(Toolkit::TextView::Truncate);
1965   }
1966   else if(policyName == "Fade")
1967   {
1968     SetWidthExceedPolicy(Toolkit::TextView::Fade);
1969   }
1970   else if(policyName == "Split")
1971   {
1972     SetWidthExceedPolicy(Toolkit::TextView::Split);
1973   }
1974   else if(policyName == "ShrinkToFit")
1975   {
1976     SetWidthExceedPolicy(Toolkit::TextView::ShrinkToFit);
1977   }
1978   else if(policyName == "EllipsizeEnd")
1979   {
1980     SetWidthExceedPolicy(Toolkit::TextView::EllipsizeEnd);
1981   }
1982   else
1983   {
1984     DALI_ASSERT_ALWAYS( !"TextView::OnWidthExceedPolicyPropertySet(). Invalid Property value." );
1985   }
1986 }
1987
1988 void TextView::OnHeightExceedPolicyPropertySet( Property::Value propertyValue )
1989 {
1990   std::string policyName( propertyValue.Get<std::string>() );
1991   if(policyName == "Original")
1992   {
1993     SetHeightExceedPolicy(Toolkit::TextView::Original);
1994   }
1995   else if(policyName == "Truncate")
1996   {
1997     SetHeightExceedPolicy(Toolkit::TextView::Truncate);
1998   }
1999   else if(policyName == "Fade")
2000   {
2001     SetHeightExceedPolicy(Toolkit::TextView::Fade);
2002   }
2003   else if(policyName == "Split")
2004   {
2005     SetHeightExceedPolicy(Toolkit::TextView::Split);
2006   }
2007   else if(policyName == "ShrinkToFit")
2008   {
2009     SetHeightExceedPolicy(Toolkit::TextView::ShrinkToFit);
2010   }
2011   else
2012   {
2013     DALI_ASSERT_ALWAYS( !"TextView::OnHeightExceedPolicyPropertySet(). Invalid Property value." );
2014   }
2015 }
2016
2017 void TextView::OnLineJustificationPropertySet( Property::Value propertyValue )
2018 {
2019   std::string policyName( propertyValue.Get<std::string>() );
2020   if(policyName == "Left")
2021   {
2022     SetLineJustification(Toolkit::TextView::Left);
2023   }
2024   else if(policyName == "Center")
2025   {
2026     SetLineJustification(Toolkit::TextView::Center);
2027   }
2028   else if(policyName == "Right")
2029   {
2030     SetLineJustification(Toolkit::TextView::Right);
2031   }
2032   else if(policyName == "Justified")
2033   {
2034     SetLineJustification(Toolkit::TextView::Justified);
2035   }
2036   else
2037   {
2038     DALI_ASSERT_ALWAYS( !"TextView::OnLineJustificationPropertySet(). Invalid Property value." );
2039   }
2040 }
2041
2042 void TextView::OnFadeBoundaryPropertySet( Property::Index propertyIndex, Property::Value propertyValue )
2043 {
2044   PixelSize boundary( propertyValue.Get<unsigned int>() );
2045
2046   if ( propertyIndex == mPropertyFadeBoundaryLeft )
2047   {
2048     mVisualParameters.mFadeBoundary.mLeft = boundary;
2049   }
2050   else if ( propertyIndex == mPropertyFadeBoundaryRight )
2051   {
2052     mVisualParameters.mFadeBoundary.mRight = boundary;
2053   }
2054   else if ( propertyIndex == mPropertyFadeBoundaryTop )
2055   {
2056     mVisualParameters.mFadeBoundary.mTop = boundary;
2057   }
2058   else if ( propertyIndex == mPropertyFadeBoundaryBottom )
2059   {
2060     mVisualParameters.mFadeBoundary.mBottom = boundary;
2061   }
2062   else
2063   {
2064     DALI_ASSERT_ALWAYS( !"TextView::OnFadeBoundaryPropertySet(). Invalid Property value." );
2065   }
2066 }
2067
2068 void TextView::OnAlignmentPropertySet( Property::Index propertyIndex, Property::Value propertyValue )
2069 {
2070   std::string value( propertyValue.Get<std::string>() );
2071
2072   if( propertyIndex == mPropertyHorizontalAlignment )
2073   {
2074     if(value == "HorizontalLeft")
2075     {
2076       mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalLeft;
2077     }
2078     else if( value == "HorizontalCenter")
2079     {
2080       mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalCenter;
2081     }
2082     else if( value == "HorizontalRight")
2083     {
2084       mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalRight;
2085     }
2086     else
2087     {
2088       DALI_ASSERT_ALWAYS( !"TextView::OnAlignmentPropertySet(). Invalid Property value." );
2089     }
2090   }
2091   else if( propertyIndex == mPropertyVerticalAlignment )
2092   {
2093     if( value == "VerticalTop" )
2094     {
2095       mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalTop;
2096     }
2097     else if( value == "VerticalCenter")
2098     {
2099       mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalCenter;
2100     }
2101     else if( value == "VerticalBottom")
2102     {
2103       mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalBottom;
2104     }
2105     else
2106     {
2107       DALI_ASSERT_ALWAYS( !"TextView::OnAlignmentPropertySet(). Invalid Property value." );
2108     }
2109   }
2110 }
2111
2112 } // namespace Internal
2113
2114 } // namespace Toolkit
2115
2116 } // namespace Dali