aa30efdf6a1b6fdf06251805c73176ccc0d6d30b
[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 );
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 );
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 );
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 );
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 text-actors.
595       const bool hasTextActors = !mRelayoutData.mTextActors.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( hasTextActors )
612       {
613         // Remove text-actors from the text-view as some text-operation like CreateTextInfo()
614         // add them to the text-actor cache.
615         TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors );
616         mRelayoutData.mTextActors.clear();
617       }
618
619       // Relays-out but doesn't add text-actors to the text-view.
620       DoRelayOut( textViewSize.GetVectorXY(), mask );
621
622       if( hasTextActors )
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<TextActor>::iterator it = mRelayoutData.mTextActors.begin(), endIt = mRelayoutData.mTextActors.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 text-actors
669     if( !mRelayoutData.mTextActors.empty() )
670     {
671       TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors );
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     }
706     else
707     {
708       Actor self = Self();
709
710       if( mOffscreenRootActor )
711       {
712         self.Remove( mOffscreenRootActor );
713       }
714
715       if( mOffscreenImageActor )
716       {
717         self.Remove( mOffscreenImageActor );
718       }
719
720       DestroyOffscreenRenderingResources();
721     }
722
723     if( RELAYOUT_ALL != mRelayoutOperations )
724     {
725       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
726                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
727                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
728                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
729                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
730     }
731     RelayoutRequest();
732   }
733 }
734
735 bool TextView::IsSnapshotModeEnabled() const
736 {
737   return mVisualParameters.mSnapshotModeEnabled;
738 }
739
740 void TextView::SetScrollEnabled( const bool enable )
741 {
742   if( enable != mVisualParameters.mScrollEnabled )
743   {
744     mVisualParameters.mScrollEnabled = enable;
745
746     if( mVisualParameters.mScrollEnabled )
747     {
748       // Offscreen rendering is needed to enable text scroll.
749
750       // Stores previous value of the snapshot mode.
751       mPreviousSnapshotModeEnabled = IsSnapshotModeEnabled();
752
753       {
754         // SetSnapshotModeEnabled() modifies the mPreviousSnapshotModeEnabled just in case it's called after SetScrollEnabled(),
755         // this lock prevents to modify the mPreviousSnapshotModeEnabled when SetSnapshotModeEnabled() from this method.
756         Lock lock( mLockPreviousSnapshotMode );
757         SetSnapshotModeEnabled( true );
758       }
759
760       // Creates the pan gesture detector and attach the text-view.
761       mPanGestureDetector = PanGestureDetector::New();
762       mPanGestureDetector.DetectedSignal().Connect( this, &TextView::OnTextPan );
763       mPanGestureDetector.Attach( Self() );
764     }
765     else
766     {
767       // Removes the pan gesture detector.
768       if( mPanGestureDetector )
769       {
770         mPanGestureDetector.Detach( Self() );
771         mPanGestureDetector.DetectedSignal().Disconnect( this, &TextView::OnTextPan );
772         mPanGestureDetector.Reset();
773       }
774
775       // Restores the previous state for snapshot mode.
776       SetSnapshotModeEnabled( mPreviousSnapshotModeEnabled );
777     }
778   }
779 }
780
781 bool TextView::IsScrollEnabled() const
782 {
783   return mVisualParameters.mScrollEnabled;
784 }
785
786 void TextView::SetScrollPosition( const Vector2& position )
787 {
788   if( position != mVisualParameters.mCameraScrollPosition )
789   {
790     // Guard against destruction during signal emission
791     // Note that Emit() methods are called indirectly from within DoSetScrollPosition()
792     Toolkit::TextView handle( GetOwner() );
793
794     DoSetScrollPosition( position );
795
796     // Check if the new scroll position has been trimmed.
797     mVisualParameters.mScrollPositionTrimmed = ( position != mVisualParameters.mCameraScrollPosition );
798   }
799 }
800
801 const Vector2& TextView::GetScrollPosition() const
802 {
803   return mVisualParameters.mCameraScrollPosition;
804 }
805
806 bool TextView::IsScrollPositionTrimmed() const
807 {
808   return mVisualParameters.mScrollPositionTrimmed;
809 }
810
811 Toolkit::TextView::ScrolledSignalV2& TextView::ScrolledSignal()
812 {
813   return mScrolledSignalV2;
814 }
815
816 bool TextView::DoConnectSignal( BaseObject* object, ConnectionTrackerInterface* tracker, const std::string& signalName, FunctorDelegate* functor )
817 {
818   Dali::BaseHandle handle( object );
819
820   bool connected( true );
821   Toolkit::TextView textView = Toolkit::TextView::DownCast(handle);
822
823   if( Dali::Toolkit::TextView::SIGNAL_TEXT_SCROLLED == signalName )
824   {
825     textView.ScrolledSignal().Connect( tracker, functor );
826   }
827   else
828   {
829     // signalName does not match any signal
830     connected = false;
831   }
832
833   return connected;
834 }
835
836 TextView::LayoutParameters::LayoutParameters()
837 : mMultilinePolicy( Toolkit::TextView::SplitByNewLineChar ),
838   mWidthExceedPolicy( Toolkit::TextView::Original ),
839   mHeightExceedPolicy( Toolkit::TextView::Original ),
840   mHorizontalAlignment( Toolkit::Alignment::HorizontalCenter ),
841   mVerticalAlignment( Toolkit::Alignment::VerticalCenter ),
842   mLineJustification( Toolkit::TextView::Left ),
843   mLineHeightOffset( 0.f ),
844   mEllipsizeText()
845 {
846   // Sets ellipsize text
847   MarkupProcessor::StyledTextArray styledEllipsize;
848   MarkupProcessor::GetStyledTextArray( std::string( "..." ), mEllipsizeText );
849 }
850
851 TextView::LayoutParameters::LayoutParameters( const Toolkit::TextView::MultilinePolicy   multilinePolicy,
852                                               const Toolkit::TextView::ExceedPolicy      widthExceedPolicy,
853                                               const Toolkit::TextView::ExceedPolicy      heightExceedPolicy,
854                                               const Toolkit::Alignment::Type             alignmentType,
855                                               const Toolkit::TextView::LineJustification lineJustification,
856                                               const float                                lineHeightOffset,
857                                               const std::string&                         ellipsizeText )
858 : mMultilinePolicy( multilinePolicy ),
859   mWidthExceedPolicy( widthExceedPolicy ),
860   mHeightExceedPolicy( heightExceedPolicy ),
861   mHorizontalAlignment(),
862   mVerticalAlignment(),
863   mLineJustification( lineJustification ),
864   mLineHeightOffset( lineHeightOffset ),
865   mEllipsizeText()
866 {
867   // Sets alignment
868   Toolkit::Alignment::Type horizontalAlignment( ( alignmentType & Toolkit::Alignment::HorizontalLeft ? Toolkit::Alignment::HorizontalLeft :
869                                                   ( alignmentType & Toolkit::Alignment::HorizontalCenter ? Toolkit::Alignment::HorizontalCenter :
870                                                     ( alignmentType & Toolkit::Alignment::HorizontalRight ? Toolkit::Alignment::HorizontalRight : Toolkit::Alignment::HorizontalCenter ) ) ) );
871   Toolkit::Alignment::Type verticalAlignment( ( alignmentType & Toolkit::Alignment::VerticalTop ? Toolkit::Alignment::VerticalTop :
872                                                 ( alignmentType & Toolkit::Alignment::VerticalCenter ? Toolkit::Alignment::VerticalCenter :
873                                                   ( alignmentType & Toolkit::Alignment::VerticalBottom ? Toolkit::Alignment::VerticalBottom : Toolkit::Alignment::VerticalCenter ) ) ) );
874
875   mHorizontalAlignment = horizontalAlignment;
876   mVerticalAlignment = verticalAlignment;
877
878   // Sets ellipsize text
879   MarkupProcessor::StyledTextArray styledEllipsize;
880   MarkupProcessor::GetStyledTextArray( ellipsizeText, mEllipsizeText );
881 }
882
883 TextView::LayoutParameters::LayoutParameters( const TextView::LayoutParameters& layoutParameters )
884 : mMultilinePolicy( layoutParameters.mMultilinePolicy ),
885   mWidthExceedPolicy( layoutParameters.mWidthExceedPolicy ),
886   mHeightExceedPolicy( layoutParameters.mHeightExceedPolicy ),
887   mHorizontalAlignment( layoutParameters.mHorizontalAlignment ),
888   mVerticalAlignment( layoutParameters.mVerticalAlignment ),
889   mLineJustification( layoutParameters.mLineJustification ),
890   mLineHeightOffset( layoutParameters.mLineHeightOffset ),
891   mEllipsizeText( layoutParameters.mEllipsizeText )
892 {
893 }
894
895 TextView::LayoutParameters& TextView::LayoutParameters::operator=( const TextView::LayoutParameters& layoutParameters )
896 {
897   mMultilinePolicy = layoutParameters.mMultilinePolicy;
898   mWidthExceedPolicy = layoutParameters.mWidthExceedPolicy;
899   mHeightExceedPolicy = layoutParameters.mHeightExceedPolicy;
900   mHorizontalAlignment = layoutParameters.mHorizontalAlignment;
901   mVerticalAlignment = layoutParameters.mVerticalAlignment;
902   mLineJustification = layoutParameters.mLineJustification;
903   mLineHeightOffset = layoutParameters.mLineHeightOffset;
904   mEllipsizeText = layoutParameters.mEllipsizeText;
905
906   return *this;
907 }
908
909 TextView::VisualParameters::VisualParameters()
910 : mFadeBoundary(),
911   mSortModifier( 0.f ),
912   mCameraScrollPosition( 0.f, 0.f ),
913   mSnapshotModeEnabled( false ),
914   mScrollEnabled( false ),
915   mScrollPositionTrimmed( false )
916 {
917 }
918
919 TextView::VisualParameters::VisualParameters( const VisualParameters& visualParameters )
920 : mFadeBoundary( visualParameters.mFadeBoundary ),
921   mSortModifier( visualParameters.mSortModifier ),
922   mCameraScrollPosition( visualParameters.mCameraScrollPosition ),
923   mSnapshotModeEnabled( visualParameters.mSnapshotModeEnabled ),
924   mScrollEnabled( visualParameters.mScrollEnabled ),
925   mScrollPositionTrimmed( visualParameters.mScrollPositionTrimmed )
926 {
927 }
928
929 TextView::VisualParameters& TextView::VisualParameters::operator=( const TextView::VisualParameters& visualParameters )
930 {
931   mFadeBoundary = visualParameters.mFadeBoundary;
932   mSortModifier = visualParameters.mSortModifier;
933   mCameraScrollPosition = visualParameters.mCameraScrollPosition;
934   mSnapshotModeEnabled = visualParameters.mSnapshotModeEnabled;
935   mScrollEnabled = visualParameters.mScrollEnabled;
936   mScrollPositionTrimmed = visualParameters.mScrollPositionTrimmed;
937
938   return *this;
939 }
940
941 TextView::RelayoutData::RelayoutData()
942 : mTextViewSize(),
943   mShrinkFactor( 1.f ),
944   mTextLayoutInfo(),
945   mCharacterLogicalToVisualMap(),
946   mCharacterVisualToLogicalMap(),
947   mTextActors(),
948   mCharacterLayoutInfoTable(),
949   mLines(),
950   mTextSizeForRelayoutOption()
951 {
952 }
953
954 TextView::RelayoutData::RelayoutData( const TextView::RelayoutData& relayoutData )
955 : mTextViewSize( relayoutData.mTextViewSize ),
956   mShrinkFactor( relayoutData.mShrinkFactor ),
957   mTextLayoutInfo( relayoutData.mTextLayoutInfo ),
958   mCharacterLogicalToVisualMap( relayoutData.mCharacterLogicalToVisualMap ),
959   mCharacterVisualToLogicalMap( relayoutData.mCharacterVisualToLogicalMap ),
960   mTextActors( relayoutData.mTextActors ),
961   mCharacterLayoutInfoTable( relayoutData.mCharacterLayoutInfoTable ),
962   mLines( relayoutData.mLines ),
963   mTextSizeForRelayoutOption( relayoutData.mTextSizeForRelayoutOption )
964 {
965 }
966
967 TextView::RelayoutData& TextView::RelayoutData::operator=( const TextView::RelayoutData& relayoutData )
968 {
969   mTextViewSize = relayoutData.mTextViewSize;
970   mShrinkFactor = relayoutData.mShrinkFactor;
971   mTextLayoutInfo = relayoutData.mTextLayoutInfo;
972   mCharacterLogicalToVisualMap = relayoutData.mCharacterLogicalToVisualMap;
973   mCharacterVisualToLogicalMap = relayoutData.mCharacterVisualToLogicalMap;
974   mTextActors = relayoutData.mTextActors;
975   mCharacterLayoutInfoTable = relayoutData.mCharacterLayoutInfoTable;
976   mLines = relayoutData.mLines;
977   mTextSizeForRelayoutOption = relayoutData.mTextSizeForRelayoutOption;
978
979   return *this;
980 }
981
982 TextView::TextView()
983 : ControlImpl( false ),  // doesn't require touch events
984   mCurrentStyledText(),
985   mTextViewProcessorOperations(),
986   mLayoutParameters( Toolkit::TextView::SplitByNewLineChar,
987                      Toolkit::TextView::Original,
988                      Toolkit::TextView::Original,
989                      static_cast<Toolkit::Alignment::Type>( Toolkit::Alignment::HorizontalCenter | Toolkit::Alignment::VerticalCenter ),
990                      Toolkit::TextView::Left,
991                      PointSize( 0.f ),
992                      std::string( "..." ) ),
993   mVisualParameters(),
994   mRelayoutData(),
995   mRelayoutOperations( NO_RELAYOUT ),
996   mOffscreenRootActor(),
997   mOffscreenImageActor(),
998   mOffscreenCameraActor(),
999   mCurrentOffscreenSize(),
1000   mFrameBufferImage(),
1001   mRenderTask(),
1002   mPanGestureDetector(),
1003   mLockPreviousSnapshotMode( false ),
1004   mPreviousSnapshotModeEnabled( false )
1005 {
1006   TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText,
1007                                          mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo );
1008 }
1009
1010 TextView::~TextView()
1011 {
1012   // Destroys offscreen rendering resources.
1013   DestroyOffscreenRenderingResources();
1014
1015   // Destroys scroll pan gesture detector.
1016   if( mPanGestureDetector )
1017   {
1018     mPanGestureDetector.Reset();
1019   }
1020 }
1021
1022 Vector3 TextView::GetNaturalSize()
1023 {
1024   if( !mTextViewProcessorOperations.empty() )
1025   {
1026     // There are SetText, Inserts or Removes to do. It means the current layout info is not updated.
1027
1028     if( !mRelayoutData.mTextActors.empty() )
1029     {
1030       // Remove text-actors from the text-view as some text-operation like CreateTextInfo()
1031       // add them to the text-actor cache.
1032       TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors );
1033       mRelayoutData.mTextActors.clear();
1034
1035       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW );
1036       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
1037     }
1038
1039     PerformTextViewProcessorOperations();
1040   }
1041
1042   return Vector3( mRelayoutData.mTextLayoutInfo.mWholeTextSize.width, mRelayoutData.mTextLayoutInfo.mWholeTextSize.height, 0.f );
1043 }
1044
1045 float TextView::GetHeightForWidth( float width )
1046 {
1047   float height = 0.f;
1048
1049   if( ( Toolkit::TextView::SplitByNewLineChar == mLayoutParameters.mMultilinePolicy ) &&
1050       ( Toolkit::TextView::Original == mLayoutParameters.mWidthExceedPolicy ) &&
1051       ( Toolkit::TextView::Original == mLayoutParameters.mHeightExceedPolicy ) )
1052   {
1053     // If multiline and exceed policies are 'SplitByNewLineChar' and 'Original' is better get the height from the
1054     // natural size. GetNaturalSize() for this configuration is faster than DoRelayOut().
1055     height = GetNaturalSize().height;
1056   }
1057   else
1058   {
1059     // Check if the given width is different than the current one.
1060     const bool differentWidth = ( fabsf( width - mRelayoutData.mTextViewSize.width ) > Math::MACHINE_EPSILON_1000 );
1061
1062     // Check if the text-view has text-actors.
1063     const bool hasTextActors = !mRelayoutData.mTextActors.empty();
1064
1065     // Check which layout operations need to be done.
1066     const bool relayoutSizeAndPositionNeeded = ( mRelayoutOperations & RELAYOUT_SIZE_POSITION ) || differentWidth;
1067
1068     if( relayoutSizeAndPositionNeeded )
1069     {
1070       if( hasTextActors )
1071       {
1072         // Remove text-actors from the text-view as some text-operation like CreateTextInfo()
1073         // add them to the text-actor cache.
1074         TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors );
1075         mRelayoutData.mTextActors.clear();
1076       }
1077
1078       // Use the given width.
1079       const Vector2 textViewSize( width, GetControlSize().height );
1080
1081       // Relays-out but doesn't add text-actors to the text-view.
1082       DoRelayOut( textViewSize, RELAYOUT_SIZE_POSITION );
1083     }
1084
1085     // Retrieve the text height after relayout the text.
1086     height = mRelayoutData.mTextSizeForRelayoutOption.height;
1087
1088     if( differentWidth )
1089     {
1090       // Revert the relayout operation mask
1091       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_SIZE_POSITION );
1092     }
1093
1094     if( hasTextActors )
1095     {
1096       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_VIEW );
1097       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations | RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
1098     }
1099
1100     if( differentWidth || hasTextActors )
1101     {
1102       RelayoutRequest();
1103     }
1104   }
1105
1106   return height;
1107 }
1108
1109 float TextView::GetWidthForHeight( float height )
1110 {
1111   // TODO: Needs implementing properly, for now just return the natural width.
1112   return GetNaturalSize().width;
1113 }
1114
1115 void TextView::OnPropertySet( Property::Index index, Property::Value propertyValue )
1116 {
1117   if( index == mPropertyText )
1118   {
1119     SetText(propertyValue.Get<std::string>());
1120   }
1121   else if( index == mPropertyMultilinePolicy )
1122   {
1123     OnMultilinePolicyPropertySet(propertyValue);
1124   }
1125   else if( index == mPropertyWidthExceedPolicy )
1126   {
1127     OnWidthExceedPolicyPropertySet(propertyValue);
1128   }
1129   else if( index == mPropertyHeightExceedPolicy )
1130   {
1131     OnHeightExceedPolicyPropertySet(propertyValue);
1132   }
1133   else if( index == mPropertyLineJustification )
1134   {
1135     OnLineJustificationPropertySet(propertyValue);
1136   }
1137   else if( ( index == mPropertyFadeBoundaryLeft )  ||
1138            ( index == mPropertyFadeBoundaryRight ) ||
1139            ( index == mPropertyFadeBoundaryTop )   ||
1140            ( index == mPropertyFadeBoundaryBottom ) )
1141   {
1142     OnFadeBoundaryPropertySet( index, propertyValue );
1143   }
1144   else if( index == mPropertyLineHeightOffset )
1145   {
1146     Dali::PointSize pointSize( propertyValue.Get<float>() );
1147     SetLineHeightOffset(pointSize);
1148   }
1149   else if ( ( index == mPropertyHorizontalAlignment ) ||
1150             ( index == mPropertyVerticalAlignment ) )
1151   {
1152     OnAlignmentPropertySet( index, propertyValue );
1153   }
1154 }
1155
1156 void TextView::OnInitialize()
1157 {
1158   Actor self = Self();
1159
1160   mPropertyText = self.RegisterProperty( PROPERTY_TEXT, "", Property::READ_WRITE );
1161
1162   mPropertyMultilinePolicy = self.RegisterProperty( PROPERTY_MULTILINE_POLICY, "SplitByNewLineChar", Property::READ_WRITE );
1163
1164   mPropertyWidthExceedPolicy = self.RegisterProperty( PROPERTY_WIDTH_EXCEED_POLICY, "Original", Property::READ_WRITE );
1165
1166   mPropertyHeightExceedPolicy = self.RegisterProperty( PROPERTY_HEIGHT_EXCEED_POLICY, "Original", Property::READ_WRITE );
1167
1168   mPropertyLineJustification = self.RegisterProperty( PROPERTY_LINE_JUSTIFICATION, "Left", Property::READ_WRITE );
1169
1170   mPropertyFadeBoundaryLeft = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_LEFT, static_cast< int >( 0 ), Property::READ_WRITE );
1171
1172   mPropertyFadeBoundaryRight = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_RIGHT, static_cast< int >( 0 ), Property::READ_WRITE );
1173
1174   mPropertyFadeBoundaryTop = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_TOP, static_cast< int >( 0 ), Property::READ_WRITE );
1175
1176   mPropertyFadeBoundaryBottom = self.RegisterProperty( PROPERTY_FADE_BOUNDARY_BOTTOM, static_cast< int >( 0 ), Property::READ_WRITE );
1177
1178   mPropertyLineHeightOffset = self.RegisterProperty( PROPERTY_LINE_HEIGHT_OFFSET, 0.0f, Property::READ_WRITE );
1179
1180   mPropertyHorizontalAlignment = self.RegisterProperty( PROPERTY_HORIZONTAL_ALIGNMENT, "HorizontalCenter", Property::READ_WRITE );
1181
1182   mPropertyVerticalAlignment = self.RegisterProperty( PROPERTY_VERTICAL_ALIGNMENT, "VerticalCenter", Property::READ_WRITE );
1183
1184 }
1185
1186
1187 void TextView::OnStyleChange( StyleChange change )
1188 {
1189   mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo = TextViewProcessor::WordLayoutInfo();
1190   TextViewProcessor::CreateWordTextInfo( mLayoutParameters.mEllipsizeText,
1191                                          mRelayoutData.mTextLayoutInfo.mEllipsizeLayoutInfo );
1192
1193   SetText( mCurrentStyledText );
1194 }
1195
1196 void TextView::OnControlSizeSet( const Vector3& size )
1197 {
1198   if( size.GetVectorXY() != mRelayoutData.mTextViewSize )
1199   {
1200     // If a GetTextLayoutInfo() or GetHeightForWidth() arrives, relayout the text synchronously is needed in order to retrieve the right values.
1201     mRelayoutOperations = RELAYOUT_ALL;
1202
1203     // Request to be relaid out
1204     RelayoutRequest();
1205   }
1206 }
1207
1208 void TextView::OnRelaidOut( Vector2 size, ActorSizeContainer& container )
1209 {
1210   if( ( size.width < Math::MACHINE_EPSILON_1000 ) || ( size.height < Math::MACHINE_EPSILON_1000 ) )
1211   {
1212     // Not worth to relayout if width or height is equal to zero.
1213     return;
1214   }
1215
1216   if( size != mRelayoutData.mTextViewSize )
1217   {
1218     // if new size is different than the prevoius one, set positions and maybe sizes of all text-actor is needed.
1219     if( RELAYOUT_ALL != mRelayoutOperations )
1220     {
1221       mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations |
1222                                                                 RELAYOUT_REMOVE_TEXT_ACTORS |
1223                                                                 RELAYOUT_SIZE_POSITION |
1224                                                                 RELAYOUT_ALIGNMENT |
1225                                                                 RELAYOUT_VISIBILITY |
1226                                                                 RELAYOUT_TEXT_ACTOR_UPDATE |
1227                                                                 RELAYOUT_INSERT_TO_TEXT_VIEW |
1228                                                                 RELAYOUT_INSERT_TO_TEXT_ACTOR_LIST );
1229     }
1230   }
1231
1232   // Remove text-actors from text-view
1233   if( !mRelayoutData.mTextActors.empty() && ( mRelayoutOperations & RELAYOUT_REMOVE_TEXT_ACTORS ) )
1234   {
1235     TextViewRelayout::RemoveTextActors( GetRootActor(), mRelayoutData.mTextActors );
1236     mRelayoutData.mTextActors.clear();
1237   }
1238
1239   if( NO_RELAYOUT != mRelayoutOperations )
1240   {
1241     // Relays-out and add text-actors to the text-view.
1242     DoRelayOut( size, mRelayoutOperations );
1243     ProcessSnapshot( size );
1244   }
1245
1246   // Quite likely the texts of the text-actors are not going to be reused, so clear them.
1247   mRelayoutData.mTextActorCache.ClearTexts();
1248 }
1249
1250 void TextView::PerformTextViewProcessorOperations()
1251 {
1252   // Traverse the relayout operation vector ...
1253
1254   // Optimizes some operations.
1255   OptimizeTextViewProcessorOperations();
1256
1257   for( std::vector<TextViewProcessorMetadata>::const_iterator it = mTextViewProcessorOperations.begin(), endIt = mTextViewProcessorOperations.end(); it != endIt; ++it )
1258   {
1259     const TextViewProcessorMetadata& relayoutMetadata( *it );
1260
1261     switch( relayoutMetadata.mType )
1262     {
1263       case TextView::TextSet:
1264       {
1265         TextViewProcessor::CreateTextInfo( relayoutMetadata.mText,
1266                                            mLayoutParameters,
1267                                            mRelayoutData );
1268         break;
1269       }
1270       case TextView::TextInserted:
1271       {
1272         TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition,
1273                                            relayoutMetadata.mText,
1274                                            mLayoutParameters,
1275                                            mRelayoutData );
1276         break;
1277       }
1278       case TextView::TextReplaced:
1279       {
1280         TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition,
1281                                            relayoutMetadata.mNumberOfCharacters,
1282                                            relayoutMetadata.mText,
1283                                            mLayoutParameters,
1284                                            mRelayoutData );
1285         break;
1286       }
1287       case TextView::TextRemoved:
1288       {
1289         TextViewProcessor::UpdateTextInfo( relayoutMetadata.mPosition,
1290                                            relayoutMetadata.mNumberOfCharacters,
1291                                            mLayoutParameters,
1292                                            mRelayoutData,
1293                                            TextViewProcessor::CLEAR_TEXT ); // clears the text of the text-actors.
1294         break;
1295       }
1296       case TextView::NewLineHeight:
1297       {
1298         TextViewProcessor::UpdateTextInfo( mLayoutParameters.mLineHeightOffset,
1299                                            mRelayoutData.mTextLayoutInfo );
1300         break;
1301       }
1302       case TextView::NewStyle:
1303       {
1304         TextViewProcessor::UpdateTextInfo( ( *relayoutMetadata.mText.begin() ).mStyle,
1305                                            relayoutMetadata.mStyleMask,
1306                                            mRelayoutData );
1307         break;
1308       }
1309     }
1310   }
1311
1312   // Clear all operations when they are done.
1313   mTextViewProcessorOperations.clear();
1314 }
1315
1316 void TextView::OptimizeTextViewProcessorOperations()
1317 {
1318   // TODO: check if some operation can be discarted. i.e. InsertTextAt( 0, "a" ); RemoveTextFrom( 0, 1 );
1319
1320   // At the moment it only replaces a 'remove 1 character' followed by 'insert 1 character' in the same position by a 'replace' operation.
1321   // This sequence is used by text-input with predictive text. Change this two operations by a replace allows the text-view processor to
1322   // use the cache without clearing the text-actors.
1323
1324   std::vector<TextViewProcessorMetadata> textViewProcessorOperations;
1325
1326   for( std::vector<TextViewProcessorMetadata>::const_iterator it = mTextViewProcessorOperations.begin(), endIt = mTextViewProcessorOperations.end(); it != endIt; ++it )
1327   {
1328     const TextViewProcessorMetadata& relayoutMetadata( *it );
1329
1330     switch( relayoutMetadata.mType )
1331     {
1332       case TextView::TextRemoved:
1333       {
1334         bool optimizationDone = false;
1335
1336         if( it + 1 != endIt )
1337         {
1338           const TextViewProcessorMetadata& nextRelayoutMetadata( *( it + 1 ) );
1339           if( TextView::TextInserted == nextRelayoutMetadata.mType )
1340           {
1341             if( relayoutMetadata.mPosition == nextRelayoutMetadata.mPosition )
1342             {
1343               optimizationDone = true;
1344               TextViewProcessorMetadata newRelayoutMetadata;
1345               newRelayoutMetadata.mType = TextView::TextReplaced;
1346               newRelayoutMetadata.mPosition = relayoutMetadata.mPosition;
1347               newRelayoutMetadata.mNumberOfCharacters = relayoutMetadata.mNumberOfCharacters;
1348               newRelayoutMetadata.mText = nextRelayoutMetadata.mText;
1349               textViewProcessorOperations.push_back( newRelayoutMetadata );
1350
1351               // do not access the TextInserted operation in next iteration.
1352               ++it;
1353             }
1354           }
1355         }
1356
1357         if( !optimizationDone )
1358         {
1359           textViewProcessorOperations.push_back( relayoutMetadata );
1360         }
1361         break;
1362       }
1363       default:
1364       {
1365         textViewProcessorOperations.push_back( relayoutMetadata );
1366       }
1367     }
1368   }
1369
1370   mTextViewProcessorOperations = textViewProcessorOperations;
1371 }
1372
1373 void TextView::DoRelayOut( const Size& textViewSize, const RelayoutOperationMask relayoutOperationMask )
1374 {
1375   // Traverse the relayout operation vector. It fills the natural size, layout and text-actor data structures.
1376   if( !mTextViewProcessorOperations.empty() )
1377   {
1378     PerformTextViewProcessorOperations();
1379   }
1380
1381   CombineExceedPolicies();
1382
1383   Actor rootActor;
1384   if( mVisualParameters.mSnapshotModeEnabled )
1385   {
1386     rootActor = mOffscreenRootActor;
1387   }
1388   else
1389   {
1390     rootActor = Self();
1391   }
1392
1393   mRelayoutData.mTextViewSize = textViewSize;
1394   switch( mLayoutParameters.mMultilinePolicy )
1395   {
1396     case Toolkit::TextView::SplitByNewLineChar:              // multiline policy == SplitByNewLineChar.
1397     {
1398       SplitByNewLineChar::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData );
1399       break;
1400     } // SplitByNewLineChar
1401
1402     case Toolkit::TextView::SplitByWord:                     // multiline policy == SplitByWord.
1403     {
1404       SplitByWord::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData );
1405       break;
1406     } // SplitByWord
1407
1408     case Toolkit::TextView::SplitByChar:                     // multiline policy == SplitByChar.
1409     {
1410       SplitByChar::Relayout( rootActor, relayoutOperationMask, mLayoutParameters, mVisualParameters, mRelayoutData );
1411       break;
1412     } // SplitByChar
1413   } // switch( mMultilinePolicy )
1414
1415   // Remove done operations from the mask.
1416   mRelayoutOperations = static_cast<RelayoutOperationMask>( mRelayoutOperations & ~relayoutOperationMask );
1417 }
1418
1419 void TextView::ProcessSnapshot( const Size& textViewSize )
1420 {
1421   if( mVisualParameters.mSnapshotModeEnabled )
1422   {
1423     // If layout options change, it's needed generate a new image.
1424
1425     if( mOffscreenRootActor )
1426     {
1427       // Set the root actor visible.
1428       // The root actor is set to non visible after the render task is processed.
1429       mOffscreenRootActor.SetVisible( true );
1430
1431       // The offscreen root actor must have same size as text view. Otherwise, text alignment won't work.
1432       mOffscreenRootActor.SetSize( textViewSize );
1433     }
1434
1435     if( ( mRelayoutData.mTextSizeForRelayoutOption.width > Math::MACHINE_EPSILON_1000 ) &&
1436         ( mRelayoutData.mTextSizeForRelayoutOption.height > Math::MACHINE_EPSILON_1000 ) )
1437     {
1438       // Set the image actor visible.
1439       // The image actor is set to non visible if there is no text to render.
1440       mOffscreenImageActor.SetVisible( true );
1441
1442       // Calculates the offscreen image's size. It takes into account different points:
1443       // * If text has italics, add a small offset is needed in order to not to cut the text next to the right edge.
1444       // * There is a maximum texture size the graphic subsystem can load on the memory.
1445       // * If the scroll is enabled, the offscreen image's size is never bigger than the text-view's size.
1446
1447       const Size offscreenSize( std::min( MAX_OFFSCREEN_RENDERING_SIZE,
1448                                           mVisualParameters.mScrollEnabled ? textViewSize.width : std::max( mRelayoutData.mTextSizeForRelayoutOption.width, textViewSize.width ) + mRelayoutData.mTextLayoutInfo.mMaxItalicsOffset ),
1449                                 std::min( MAX_OFFSCREEN_RENDERING_SIZE,
1450                                           mVisualParameters.mScrollEnabled ? textViewSize.height : std::max( mRelayoutData.mTextSizeForRelayoutOption.height, textViewSize.height ) ) );
1451
1452       const bool sizeChanged = offscreenSize != mCurrentOffscreenSize;
1453
1454       if( sizeChanged )
1455       {
1456         // Creates a frame buffer for offscreen rendering when the size is negotiated.
1457         mFrameBufferImage = FrameBufferImage::New( offscreenSize.width,
1458                                                    offscreenSize.height,
1459                                                    Pixel::RGBA8888 );
1460
1461         // Stores current text-view size to avoid create new Dali resources if text changes.
1462         mCurrentOffscreenSize = offscreenSize;
1463
1464         if( !mOffscreenCameraActor )
1465         {
1466           // Creates a new camera actor.
1467           mOffscreenCameraActor = CameraActor::New();
1468           mOffscreenCameraActor.SetParentOrigin( ParentOrigin::CENTER );
1469           mOffscreenCameraActor.SetAnchorPoint( AnchorPoint::CENTER );
1470
1471           mOffscreenCameraActor.SetType( Dali::Camera::FREE_LOOK ); // Inherits position from the offscreen root actor.
1472
1473           mOffscreenRootActor.Add( mOffscreenCameraActor ); // camera to shoot the offscreen text
1474
1475           // Images are expected to be from top to bottom, but OpenGL buffers are bottom to top
1476           mOffscreenCameraActor.SetInvertYAxis( false );
1477         }
1478
1479         // Calculate camera parameters for current text size.
1480         mOffscreenCameraActor.SetOrthographicProjection( offscreenSize );
1481       }
1482
1483       if( mVisualParameters.mScrollEnabled )
1484       {
1485         // Updates the offscreen camera position with the new scroll offset.
1486         mOffscreenCameraActor.SetX( mVisualParameters.mCameraScrollPosition.x );
1487         mOffscreenCameraActor.SetY( mVisualParameters.mCameraScrollPosition.y );
1488       }
1489       else
1490       {
1491         // Text's size could be bigger than text-view's size. In that case the camera must be aligned to cover the whole text.
1492         AlignOffscreenCameraActor( textViewSize, offscreenSize );
1493       }
1494
1495       if( !mRenderTask )
1496       {
1497         // Creates a new render task.
1498         mRenderTask = Stage::GetCurrent().GetRenderTaskList().CreateTask();
1499
1500         mRenderTask.SetSourceActor( mOffscreenRootActor );
1501         mRenderTask.SetInputEnabled( false );
1502         mRenderTask.SetClearColor( Color::TRANSPARENT );
1503         mRenderTask.SetClearEnabled( true );
1504         mRenderTask.SetExclusive( true );
1505
1506         // Connects the signal to the TextView::RenderTaskFinished method in order to make the root actor non visible when the render task is processed.
1507         mRenderTask.FinishedSignal().Connect( this, &TextView::RenderTaskFinished );
1508       }
1509
1510       if( sizeChanged )
1511       {
1512         mRenderTask.SetCameraActor( mOffscreenCameraActor );
1513         mRenderTask.SetTargetFrameBuffer( mFrameBufferImage );
1514       }
1515
1516       // Process the render task only once every time the text changes or the text-view's size canges.
1517       mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE );
1518     }
1519     else
1520     {
1521       // If there is no text just make any previous generated image invisible instead to process a render task with no text.
1522       mOffscreenImageActor.SetVisible( false );
1523     }
1524   }
1525 }
1526
1527 void TextView::AlignOffscreenCameraActor( const Size& textViewSize, const Size& offscreenSize )
1528 {
1529   float xPosition = 0.f;
1530   float yPosition = 0.f;
1531   Vector3 parentOrigin = ParentOrigin::CENTER;
1532   Vector3 anchorPoint = AnchorPoint::CENTER;
1533
1534   switch( mLayoutParameters.mHorizontalAlignment )
1535   {
1536     case Toolkit::Alignment::HorizontalLeft:
1537     {
1538       xPosition = 0.5f * ( offscreenSize.width - textViewSize.width );
1539       parentOrigin.x = 0.f;
1540       anchorPoint.x = 0.f;
1541       break;
1542     }
1543     case Toolkit::Alignment::HorizontalCenter:
1544     {
1545       // nothing to do.
1546       break;
1547     }
1548     case Toolkit::Alignment::HorizontalRight:
1549     {
1550       xPosition = 0.5f * ( textViewSize.width - offscreenSize.width );
1551       parentOrigin.x = 1.f;
1552       anchorPoint.x = 1.f;
1553       break;
1554     }
1555     default:
1556     {
1557       DALI_ASSERT_ALWAYS( !"TextView::AlignOffscreenCameraActor: Invalid alignment option." )
1558     }
1559   }
1560
1561   switch( mLayoutParameters.mVerticalAlignment )
1562   {
1563     case Toolkit::Alignment::VerticalTop:
1564     {
1565       yPosition = 0.5f * ( offscreenSize.height - textViewSize.height );
1566       parentOrigin.y = 0.f;
1567       anchorPoint.y = 0.f;
1568       break;
1569     }
1570     case Toolkit::Alignment::VerticalCenter:
1571     {
1572       // nothing to do.
1573       break;
1574     }
1575     case Toolkit::Alignment::VerticalBottom:
1576     {
1577       yPosition = 0.5f * ( textViewSize.height - offscreenSize.height );
1578       parentOrigin.y = 1.f;
1579       anchorPoint.y = 1.f;
1580       break;
1581     }
1582     default:
1583     {
1584       DALI_ASSERT_ALWAYS( !"TextView::AlignOffscreenCameraActor: Invalid alignment option." )
1585     }
1586   }
1587
1588   mOffscreenCameraActor.SetX( xPosition );
1589   mOffscreenCameraActor.SetY( yPosition );
1590
1591   mOffscreenImageActor.SetParentOrigin( parentOrigin );
1592   mOffscreenImageActor.SetAnchorPoint( anchorPoint );
1593 }
1594
1595 void TextView::RenderTaskFinished( Dali::RenderTask& renderTask )
1596 {
1597   // not to process the offscreen root actor by setting its visibility to false.
1598   mOffscreenRootActor.SetVisible( false );
1599
1600   // Sets the new size and the new frame buffer to the image actor.
1601   // Image actor must have same size as text. Otherwise text can be truncated.
1602   mOffscreenImageActor.SetSize( mCurrentOffscreenSize );
1603   mOffscreenImageActor.SetImage( mFrameBufferImage );
1604 }
1605
1606 void TextView::DestroyOffscreenRenderingResources()
1607 {
1608   if( mRenderTask )
1609   {
1610     mRenderTask.FinishedSignal().Disconnect( this, &TextView::RenderTaskFinished );
1611
1612     if( Stage::IsInstalled() )
1613     {
1614       Stage::GetCurrent().GetRenderTaskList().RemoveTask( mRenderTask );
1615     }
1616
1617     mRenderTask.Reset();
1618   }
1619
1620   // Remove and reset the root actor, image actor and camera actor as text-view is not rendering offscreen.
1621   if( mOffscreenCameraActor )
1622   {
1623     mOffscreenRootActor.Remove( mOffscreenCameraActor );
1624
1625     mOffscreenCameraActor.Reset();
1626   }
1627
1628   if( mOffscreenRootActor )
1629   {
1630     mOffscreenRootActor.Reset();
1631   }
1632
1633   if( mOffscreenImageActor )
1634   {
1635     mOffscreenImageActor.Reset();
1636   }
1637
1638   mCurrentOffscreenSize = Size( 0.f, 0.f );
1639
1640   if( mFrameBufferImage )
1641   {
1642     mFrameBufferImage.Reset();
1643   }
1644 }
1645
1646 void TextView::OnTextPan( Actor actor, PanGesture gesture )
1647 {
1648   if( 1u == gesture.numberOfTouches )
1649   {
1650     DoSetScrollPosition( mVisualParameters.mCameraScrollPosition - gesture.displacement );
1651   }
1652 }
1653
1654 void TextView::TrimScrollPosition()
1655 {
1656   const Vector3& textViewSize = GetControlSize();
1657
1658   // Before use the text's size, relayout the text is needed to get the actual text size.
1659   GetTextLayoutInfo();
1660
1661   // Calculates the range within the text could be scrolled. (When the text is aligned in the center).
1662   float maxHorizontalDisplacement = std::max( 0.f, 0.5f * ( mRelayoutData.mTextSizeForRelayoutOption.width - textViewSize.width ) );
1663   float maxVerticalDisplacement = std::max( 0.f, 0.5f * ( mRelayoutData.mTextSizeForRelayoutOption.height - textViewSize.height ) );
1664   float minHorizontalDisplacement = -maxHorizontalDisplacement;
1665   float minVerticalDisplacement = -maxVerticalDisplacement;
1666
1667   // Updates the range if the text is aligned on the right or left.
1668   switch( mLayoutParameters.mHorizontalAlignment )
1669   {
1670     case Toolkit::Alignment::HorizontalLeft:
1671     {
1672       maxHorizontalDisplacement *= 2.f;
1673       minHorizontalDisplacement = 0.f;
1674       break;
1675     }
1676     case Toolkit::Alignment::HorizontalCenter:
1677     {
1678       // nothing to do.
1679       break;
1680     }
1681     case Toolkit::Alignment::HorizontalRight:
1682     {
1683       maxHorizontalDisplacement = 0.f;
1684       minHorizontalDisplacement *= 2.f;
1685       break;
1686     }
1687     default:
1688     {
1689       DALI_ASSERT_ALWAYS( !"TextView::TrimScrollPosition: Invalid alignment option." )
1690     }
1691   }
1692
1693   // Updates the range if the text is aligned on the top or bottom.
1694   switch( mLayoutParameters.mVerticalAlignment )
1695   {
1696     case Toolkit::Alignment::VerticalTop:
1697     {
1698       maxVerticalDisplacement *= 2.f;
1699       minVerticalDisplacement = 0.f;
1700       break;
1701     }
1702     case Toolkit::Alignment::VerticalCenter:
1703     {
1704       //nothing to do
1705       break;
1706     }
1707     case Toolkit::Alignment::VerticalBottom:
1708     {
1709       maxVerticalDisplacement = 0.f;
1710       minVerticalDisplacement *= 2.f;
1711       break;
1712     }
1713     default:
1714     {
1715       DALI_ASSERT_ALWAYS( !"TextView::TrimScrollPosition: Invalid alignment option." )
1716     }
1717   }
1718
1719   // Trims the scroll position to be within the range.
1720   mVisualParameters.mCameraScrollPosition.x = std::min( maxHorizontalDisplacement, mVisualParameters.mCameraScrollPosition.x );
1721   mVisualParameters.mCameraScrollPosition.x = std::max( minHorizontalDisplacement, mVisualParameters.mCameraScrollPosition.x );
1722
1723   mVisualParameters.mCameraScrollPosition.y = std::min( maxVerticalDisplacement, mVisualParameters.mCameraScrollPosition.y );
1724   mVisualParameters.mCameraScrollPosition.y = std::max( minVerticalDisplacement, mVisualParameters.mCameraScrollPosition.y );
1725 }
1726
1727 void TextView::DoSetScrollPosition( const Vector2& position )
1728 {
1729   // Stores old scroll position.
1730   Vector2 delta( mVisualParameters.mCameraScrollPosition );
1731
1732   // Updates the scroll position
1733   mVisualParameters.mCameraScrollPosition = position;
1734
1735   // Ensures the text-view is covered with text.
1736   TrimScrollPosition();
1737
1738   // Calculate the difference with the previous scroll position
1739   delta.x = mVisualParameters.mCameraScrollPosition.x - delta.x;
1740   delta.y = mVisualParameters.mCameraScrollPosition.y - delta.y;
1741
1742   if( mOffscreenRootActor )
1743   {
1744     // If there is a render-task it needs to be refreshed. Therefore text-actors need to be
1745     // set to visible.
1746     mOffscreenRootActor.SetVisible( true );
1747   }
1748
1749   if( mOffscreenCameraActor )
1750   {
1751     // Update the offscreen camera with the new scroll position.
1752     mOffscreenCameraActor.SetX( mVisualParameters.mCameraScrollPosition.x );
1753     mOffscreenCameraActor.SetY( mVisualParameters.mCameraScrollPosition.y );
1754   }
1755
1756   if( mRenderTask )
1757   {
1758     // Refresh the render-task.
1759     mRenderTask.SetRefreshRate( RenderTask::REFRESH_ONCE );
1760   }
1761
1762   // Emit the signal.
1763   Toolkit::TextView handle( GetOwner() );
1764   mScrolledSignalV2.Emit( handle, delta );
1765 }
1766
1767 void TextView::CombineExceedPolicies()
1768 {
1769   // Calculates the combination of exceed policies.
1770
1771   switch( mLayoutParameters.mWidthExceedPolicy )
1772   {
1773     case Toolkit::TextView::Original:
1774     {
1775       switch( mLayoutParameters.mHeightExceedPolicy )
1776       {
1777         case Toolkit::TextView::Original:
1778         {
1779           mLayoutParameters.mExceedPolicy = Original;
1780           break;
1781         }
1782         case Toolkit::TextView::Fade:
1783         {
1784           mLayoutParameters.mExceedPolicy = OriginalFade;
1785           break;
1786         }
1787         case Toolkit::TextView::ShrinkToFit:
1788         {
1789           mLayoutParameters.mExceedPolicy = OriginalShrink;
1790           break;
1791         }
1792         default:
1793         {
1794           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1795         }
1796       }
1797       break;
1798     }
1799     case Toolkit::TextView::Split:
1800     {
1801       switch( mLayoutParameters.mHeightExceedPolicy )
1802       {
1803         case Toolkit::TextView::Original:
1804         {
1805           mLayoutParameters.mExceedPolicy = SplitOriginal;
1806           break;
1807         }
1808         case Toolkit::TextView::Fade:
1809         {
1810           mLayoutParameters.mExceedPolicy = SplitFade;
1811           break;
1812         }
1813         case Toolkit::TextView::ShrinkToFit:
1814         {
1815           mLayoutParameters.mExceedPolicy = SplitShrink;
1816           break;
1817         }
1818         default:
1819         {
1820           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1821         }
1822       }
1823       break;
1824     }
1825     case Toolkit::TextView::Fade:
1826     {
1827       switch( mLayoutParameters.mHeightExceedPolicy )
1828       {
1829         case Toolkit::TextView::Original:
1830         {
1831           mLayoutParameters.mExceedPolicy = FadeOriginal;
1832           break;
1833         }
1834         case Toolkit::TextView::Fade:
1835         {
1836           mLayoutParameters.mExceedPolicy = Fade;
1837           break;
1838         }
1839         default:
1840         {
1841           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1842         }
1843       }
1844       break;
1845     }
1846     case Toolkit::TextView::ShrinkToFit:
1847     {
1848       switch( mLayoutParameters.mHeightExceedPolicy )
1849       {
1850         case Toolkit::TextView::Original:
1851         {
1852           mLayoutParameters.mExceedPolicy = ShrinkOriginal;
1853           break;
1854         }
1855         case Toolkit::TextView::Fade:
1856         {
1857           mLayoutParameters.mExceedPolicy = ShrinkFade;
1858           break;
1859         }
1860         case Toolkit::TextView::ShrinkToFit:
1861         {
1862           mLayoutParameters.mExceedPolicy = Shrink;
1863           break;
1864         }
1865         default:
1866         {
1867           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1868         }
1869       }
1870       break;
1871     }
1872     case Toolkit::TextView::EllipsizeEnd:
1873     {
1874       switch( mLayoutParameters.mHeightExceedPolicy )
1875       {
1876         case Toolkit::TextView::Original:
1877         {
1878           mLayoutParameters.mExceedPolicy = EllipsizeEndOriginal;
1879           break;
1880         }
1881         case Toolkit::TextView::EllipsizeEnd:
1882         {
1883           mLayoutParameters.mExceedPolicy = EllipsizeEnd;
1884           break;
1885         }
1886         default:
1887         {
1888           DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width and height exceed policies combination" );
1889         }
1890       }
1891       break;
1892     }
1893     default:
1894     {
1895       DALI_ASSERT_ALWAYS( !"TextView::CombineExceedPolicies() Invalid width exceed policy" );
1896     }
1897   }
1898 }
1899
1900 Actor TextView::GetRootActor() const
1901 {
1902   // Get the root actor, if text-view was rendering offscreen, or the text-view itself.
1903
1904   Actor rootActor;
1905
1906   if( mVisualParameters.mSnapshotModeEnabled )
1907   {
1908     rootActor = mOffscreenRootActor;
1909   }
1910   else
1911   {
1912     rootActor = Self();
1913   }
1914
1915   return rootActor;
1916 }
1917
1918 void TextView::OnMultilinePolicyPropertySet( Property::Value propertyValue )
1919 {
1920   std::string policyName( propertyValue.Get<std::string>() );
1921   if(policyName == "SplitByNewLineChar")
1922   {
1923     SetMultilinePolicy(Toolkit::TextView::SplitByNewLineChar);
1924   }
1925   else if(policyName == "SplitByWord")
1926   {
1927     SetMultilinePolicy(Toolkit::TextView::SplitByWord);
1928   }
1929   else if(policyName == "SplitByChar")
1930   {
1931     SetMultilinePolicy(Toolkit::TextView::SplitByChar);
1932   }
1933   else
1934   {
1935     DALI_ASSERT_ALWAYS( !"TextView::OnMultilinePolicyPropertySet(). Invalid Property value." );
1936   }
1937 }
1938
1939 void TextView::OnWidthExceedPolicyPropertySet( Property::Value propertyValue )
1940 {
1941   std::string policyName( propertyValue.Get<std::string>() );
1942   if(policyName == "Original")
1943   {
1944     SetWidthExceedPolicy(Toolkit::TextView::Original);
1945   }
1946   else if(policyName == "Truncate")
1947   {
1948     SetWidthExceedPolicy(Toolkit::TextView::Truncate);
1949   }
1950   else if(policyName == "Fade")
1951   {
1952     SetWidthExceedPolicy(Toolkit::TextView::Fade);
1953   }
1954   else if(policyName == "Split")
1955   {
1956     SetWidthExceedPolicy(Toolkit::TextView::Split);
1957   }
1958   else if(policyName == "ShrinkToFit")
1959   {
1960     SetWidthExceedPolicy(Toolkit::TextView::ShrinkToFit);
1961   }
1962   else if(policyName == "EllipsizeEnd")
1963   {
1964     SetWidthExceedPolicy(Toolkit::TextView::EllipsizeEnd);
1965   }
1966   else
1967   {
1968     DALI_ASSERT_ALWAYS( !"TextView::OnWidthExceedPolicyPropertySet(). Invalid Property value." );
1969   }
1970 }
1971
1972 void TextView::OnHeightExceedPolicyPropertySet( Property::Value propertyValue )
1973 {
1974   std::string policyName( propertyValue.Get<std::string>() );
1975   if(policyName == "Original")
1976   {
1977     SetHeightExceedPolicy(Toolkit::TextView::Original);
1978   }
1979   else if(policyName == "Truncate")
1980   {
1981     SetHeightExceedPolicy(Toolkit::TextView::Truncate);
1982   }
1983   else if(policyName == "Fade")
1984   {
1985     SetHeightExceedPolicy(Toolkit::TextView::Fade);
1986   }
1987   else if(policyName == "Split")
1988   {
1989     SetHeightExceedPolicy(Toolkit::TextView::Split);
1990   }
1991   else if(policyName == "ShrinkToFit")
1992   {
1993     SetHeightExceedPolicy(Toolkit::TextView::ShrinkToFit);
1994   }
1995   else
1996   {
1997     DALI_ASSERT_ALWAYS( !"TextView::OnHeightExceedPolicyPropertySet(). Invalid Property value." );
1998   }
1999 }
2000
2001 void TextView::OnLineJustificationPropertySet( Property::Value propertyValue )
2002 {
2003   std::string policyName( propertyValue.Get<std::string>() );
2004   if(policyName == "Left")
2005   {
2006     SetLineJustification(Toolkit::TextView::Left);
2007   }
2008   else if(policyName == "Center")
2009   {
2010     SetLineJustification(Toolkit::TextView::Center);
2011   }
2012   else if(policyName == "Right")
2013   {
2014     SetLineJustification(Toolkit::TextView::Right);
2015   }
2016   else if(policyName == "Justified")
2017   {
2018     SetLineJustification(Toolkit::TextView::Justified);
2019   }
2020   else
2021   {
2022     DALI_ASSERT_ALWAYS( !"TextView::OnLineJustificationPropertySet(). Invalid Property value." );
2023   }
2024 }
2025
2026 void TextView::OnFadeBoundaryPropertySet( Property::Index propertyIndex, Property::Value propertyValue )
2027 {
2028   PixelSize boundary( propertyValue.Get<unsigned int>() );
2029
2030   if ( propertyIndex == mPropertyFadeBoundaryLeft )
2031   {
2032     mVisualParameters.mFadeBoundary.mLeft = boundary;
2033   }
2034   else if ( propertyIndex == mPropertyFadeBoundaryRight )
2035   {
2036     mVisualParameters.mFadeBoundary.mRight = boundary;
2037   }
2038   else if ( propertyIndex == mPropertyFadeBoundaryTop )
2039   {
2040     mVisualParameters.mFadeBoundary.mTop = boundary;
2041   }
2042   else if ( propertyIndex == mPropertyFadeBoundaryBottom )
2043   {
2044     mVisualParameters.mFadeBoundary.mBottom = boundary;
2045   }
2046   else
2047   {
2048     DALI_ASSERT_ALWAYS( !"TextView::OnFadeBoundaryPropertySet(). Invalid Property value." );
2049   }
2050 }
2051
2052 void TextView::OnAlignmentPropertySet( Property::Index propertyIndex, Property::Value propertyValue )
2053 {
2054   std::string value( propertyValue.Get<std::string>() );
2055
2056   if( propertyIndex == mPropertyHorizontalAlignment )
2057   {
2058     if(value == "HorizontalLeft")
2059     {
2060       mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalLeft;
2061     }
2062     else if( value == "HorizontalCenter")
2063     {
2064       mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalCenter;
2065     }
2066     else if( value == "HorizontalRight")
2067     {
2068       mLayoutParameters.mHorizontalAlignment = Toolkit::Alignment::HorizontalRight;
2069     }
2070     else
2071     {
2072       DALI_ASSERT_ALWAYS( !"TextView::OnAlignmentPropertySet(). Invalid Property value." );
2073     }
2074   }
2075   else if( propertyIndex == mPropertyVerticalAlignment )
2076   {
2077     if( value == "VerticalTop" )
2078     {
2079       mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalTop;
2080     }
2081     else if( value == "VerticalCenter")
2082     {
2083       mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalCenter;
2084     }
2085     else if( value == "VerticalBottom")
2086     {
2087       mLayoutParameters.mVerticalAlignment = Toolkit::Alignment::VerticalBottom;
2088     }
2089     else
2090     {
2091       DALI_ASSERT_ALWAYS( !"TextView::OnAlignmentPropertySet(). Invalid Property value." );
2092     }
2093   }
2094 }
2095
2096 } // namespace Internal
2097
2098 } // namespace Toolkit
2099
2100 } // namespace Dali