Merge "If the height is small even if scrolling is enabled, it should be elide."...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-controls / text-label-impl.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-toolkit/internal/controls/text-controls/text-label-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/object/type-registry-helper.h>
23 #include <dali/devel-api/object/property-helper-devel.h>
24 #include <dali/devel-api/adaptor-framework/image-loading.h>
25 #include <dali/integration-api/debug.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/public-api/text/rendering-backend.h>
29 #include <dali-toolkit/public-api/text/text-enumerations.h>
30 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
31 #include <dali-toolkit/internal/text/property-string-parser.h>
32 #include <dali-toolkit/internal/text/rendering/text-backend.h>
33 #include <dali-toolkit/internal/text/text-effects-style.h>
34 #include <dali-toolkit/internal/text/text-font-style.h>
35 #include <dali-toolkit/internal/text/text-view.h>
36 #include <dali-toolkit/internal/text/text-definitions.h>
37 #include <dali-toolkit/internal/styling/style-manager-impl.h>
38
39 #include <dali-toolkit/public-api/align-enumerations.h>
40 #include <dali-toolkit/internal/text/text-enumerations-impl.h>
41 #include <dali-toolkit/devel-api/controls/control-devel.h>
42 #include <dali-toolkit/devel-api/visual-factory/visual-base.h>
43 #include <dali-toolkit/public-api/visuals/text-visual-properties.h>
44 #include <dali-toolkit/public-api/visuals/visual-properties.h>
45 #include <dali-toolkit/devel-api/visual-factory/visual-factory.h>
46
47 #include <dali-toolkit/devel-api/controls/text-controls/text-label-devel.h>
48
49 using namespace Dali::Toolkit::Text;
50
51 namespace Dali
52 {
53
54 namespace Toolkit
55 {
56
57 namespace Internal
58 {
59
60 namespace
61 {
62   const unsigned int DEFAULT_RENDERING_BACKEND = Dali::Toolkit::Text::DEFAULT_RENDERING_BACKEND;
63
64   /**
65    * @brief How the text visual should be aligned vertically inside the control.
66    *
67    * 0.0f aligns the text to the top, 0.5f aligns the text to the center, 1.0f aligns the text to the bottom.
68    * The alignment depends on the alignment value of the text label (Use Text::VerticalAlignment enumerations).
69    */
70   const float VERTICAL_ALIGNMENT_TABLE[ Text::VerticalAlignment::BOTTOM + 1 ] =
71   {
72     0.0f,  // VerticalAlignment::TOP
73     0.5f,  // VerticalAlignment::CENTER
74     1.0f   // VerticalAlignment::BOTTOM
75   };
76 }
77
78 namespace
79 {
80
81 #if defined ( DEBUG_ENABLED )
82   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, true, "LOG_TEXT_CONTROLS");
83 #endif
84
85 const Scripting::StringEnum AUTO_SCROLL_STOP_MODE_TABLE[] =
86 {
87   { "IMMEDIATE", Toolkit::TextLabel::AutoScrollStopMode::IMMEDIATE },
88   { "FINISH_LOOP",  Toolkit::TextLabel::AutoScrollStopMode::FINISH_LOOP  },
89 };
90 const unsigned int AUTO_SCROLL_STOP_MODE_TABLE_COUNT = sizeof( AUTO_SCROLL_STOP_MODE_TABLE ) / sizeof( AUTO_SCROLL_STOP_MODE_TABLE[0] );
91
92 // Type registration
93 BaseHandle Create()
94 {
95   return Toolkit::TextLabel::New();
96 }
97
98 // Setup properties, signals and actions using the type-registry.
99 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TextLabel, Toolkit::Control, Create );
100
101 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "renderingBackend",          INTEGER, RENDERING_BACKEND          )
102 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "text",                      STRING,  TEXT                       )
103 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "fontFamily",                STRING,  FONT_FAMILY                )
104 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "fontStyle",                 MAP,     FONT_STYLE                 )
105 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "pointSize",                 FLOAT,   POINT_SIZE                 )
106 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "multiLine",                 BOOLEAN, MULTI_LINE                 )
107 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "horizontalAlignment",       STRING,  HORIZONTAL_ALIGNMENT       )
108 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "verticalAlignment",         STRING,  VERTICAL_ALIGNMENT         )
109 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "unusedPropertyTextColor",   VECTOR4, UNUSED_PROPERTY_TEXT_COLOR )
110 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "shadowOffset",              VECTOR2, SHADOW_OFFSET              )
111 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "shadowColor",               VECTOR4, SHADOW_COLOR               )
112 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "underlineEnabled",          BOOLEAN, UNDERLINE_ENABLED          )
113 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "underlineColor",            VECTOR4, UNDERLINE_COLOR            )
114 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "underlineHeight",           FLOAT,   UNDERLINE_HEIGHT           )
115 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "enableMarkup",              BOOLEAN, ENABLE_MARKUP              )
116 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "enableAutoScroll",          BOOLEAN, ENABLE_AUTO_SCROLL         )
117 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "autoScrollSpeed",           INTEGER, AUTO_SCROLL_SPEED          )
118 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "autoScrollLoopCount",       INTEGER, AUTO_SCROLL_LOOP_COUNT     )
119 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "autoScrollGap",             FLOAT,   AUTO_SCROLL_GAP            )
120 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "lineSpacing",               FLOAT,   LINE_SPACING               )
121 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "underline",                 MAP,     UNDERLINE                  )
122 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "shadow",                    MAP,     SHADOW                     )
123 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "emboss",                    MAP,     EMBOSS                     )
124 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "outline",                   MAP,     OUTLINE                    )
125 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "pixelSize",                 FLOAT,   PIXEL_SIZE                 )
126 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "ellipsis",                  BOOLEAN, ELLIPSIS                   )
127 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "autoScrollLoopDelay",       FLOAT,   AUTO_SCROLL_LOOP_DELAY     )
128 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "autoScrollStopMode",        STRING,  AUTO_SCROLL_STOP_MODE      )
129 DALI_PROPERTY_REGISTRATION_READ_ONLY( Toolkit, TextLabel, "lineCount",                 INTEGER, LINE_COUNT                 )
130 DALI_PROPERTY_REGISTRATION( Toolkit,           TextLabel, "lineWrapMode",              INTEGER, LINE_WRAP_MODE             )
131 DALI_DEVEL_PROPERTY_REGISTRATION_READ_ONLY( Toolkit, TextLabel, "textDirection",       INTEGER, TEXT_DIRECTION             )
132 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit,     TextLabel, "verticalLineAlignment",     INTEGER, VERTICAL_LINE_ALIGNMENT    )
133 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit,     TextLabel, "textBackground",            MAP,     BACKGROUND                 )
134 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit,     TextLabel, "ignoreSpacesAfterText",     BOOLEAN, IGNORE_SPACES_AFTER_TEXT   )
135 DALI_DEVEL_PROPERTY_REGISTRATION( Toolkit,     TextLabel, "matchSystemLanguageDirection", BOOLEAN, MATCH_SYSTEM_LANGUAGE_DIRECTION )
136 DALI_ANIMATABLE_PROPERTY_REGISTRATION_WITH_DEFAULT( Toolkit, TextLabel, "textColor",      Color::BLACK,     TEXT_COLOR     )
137 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit,    TextLabel, "textColorRed",   TEXT_COLOR_RED,   TEXT_COLOR, 0  )
138 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit,    TextLabel, "textColorGreen", TEXT_COLOR_GREEN, TEXT_COLOR, 1  )
139 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit,    TextLabel, "textColorBlue",  TEXT_COLOR_BLUE,  TEXT_COLOR, 2  )
140 DALI_ANIMATABLE_PROPERTY_COMPONENT_REGISTRATION( Toolkit,    TextLabel, "textColorAlpha", TEXT_COLOR_ALPHA, TEXT_COLOR, 3  )
141 DALI_TYPE_REGISTRATION_END()
142
143 } // namespace
144
145 Toolkit::TextLabel TextLabel::New()
146 {
147   // Create the implementation, temporarily owned by this handle on stack
148   IntrusivePtr< TextLabel > impl = new TextLabel();
149
150   // Pass ownership to CustomActor handle
151   Toolkit::TextLabel handle( *impl );
152
153   // Second-phase init of the implementation
154   // This can only be done after the CustomActor connection has been made...
155   impl->Initialize();
156
157   return handle;
158 }
159
160 void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
161 {
162   Toolkit::TextLabel label = Toolkit::TextLabel::DownCast( Dali::BaseHandle( object ) );
163
164   if( label )
165   {
166     TextLabel& impl( GetImpl( label ) );
167     switch( index )
168     {
169       case Toolkit::TextLabel::Property::RENDERING_BACKEND:
170       {
171         DALI_LOG_WARNING("[%s] Using deprecated Property TextLabel::Property::RENDERING_BACKEND which is no longer supported and will be ignored\n", __FUNCTION__);
172
173         int backend = value.Get< int >();
174
175 #ifndef ENABLE_VECTOR_BASED_TEXT_RENDERING
176         if( Text::RENDERING_VECTOR_BASED == backend )
177         {
178           backend = TextAbstraction::BITMAP_GLYPH; // Fallback to bitmap-based rendering
179         }
180 #endif
181         if( impl.mRenderingBackend != backend )
182         {
183           impl.mRenderingBackend = backend;
184           impl.mTextUpdateNeeded = true;
185
186           if( impl.mController )
187           {
188             // When using the vector-based rendering, the size of the GLyphs are different
189             TextAbstraction::GlyphType glyphType = (Text::RENDERING_VECTOR_BASED == impl.mRenderingBackend) ? TextAbstraction::VECTOR_GLYPH : TextAbstraction::BITMAP_GLYPH;
190             impl.mController->SetGlyphType( glyphType );
191           }
192         }
193         break;
194       }
195       case Toolkit::TextLabel::Property::TEXT:
196       {
197         if( impl.mController )
198         {
199           impl.mController->SetText( value.Get< std::string >() );
200         }
201         break;
202       }
203       case Toolkit::TextLabel::Property::FONT_FAMILY:
204       {
205         if( impl.mController )
206         {
207           const std::string& fontFamily = value.Get< std::string >();
208
209           DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextLabel::SetProperty Property::FONT_FAMILY newFont(%s)\n", fontFamily.c_str() );
210           impl.mController->SetDefaultFontFamily( fontFamily );
211         }
212         break;
213       }
214       case Toolkit::TextLabel::Property::FONT_STYLE:
215       {
216         SetFontStyleProperty( impl.mController, value, Text::FontStyle::DEFAULT );
217         break;
218       }
219       case Toolkit::TextLabel::Property::POINT_SIZE:
220       {
221         if( impl.mController )
222         {
223           const float pointSize = value.Get< float >();
224
225           if( !Equals( impl.mController->GetDefaultFontSize( Text::Controller::POINT_SIZE ), pointSize ) )
226           {
227             impl.mController->SetDefaultFontSize( pointSize, Text::Controller::POINT_SIZE );
228           }
229         }
230         break;
231       }
232       case Toolkit::TextLabel::Property::MULTI_LINE:
233       {
234         if( impl.mController )
235         {
236           impl.mController->SetMultiLineEnabled( value.Get< bool >() );
237         }
238         break;
239       }
240       case Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT:
241       {
242         if( impl.mController )
243         {
244           Text::HorizontalAlignment::Type alignment( static_cast< Text::HorizontalAlignment::Type >( -1 ) ); // Set to invalid value to ensure a valid mode does get set
245           if( Text::GetHorizontalAlignmentEnumeration( value, alignment ) )
246           {
247             impl.mController->SetHorizontalAlignment( alignment );
248           }
249         }
250         break;
251       }
252       case Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT:
253       {
254         if( impl.mController )
255         {
256           Toolkit::Text::VerticalAlignment::Type alignment( static_cast< Text::VerticalAlignment::Type >( -1 ) ); // Set to invalid value to ensure a valid mode does get set
257           if( Text::GetVerticalAlignmentEnumeration( value, alignment ) )
258           {
259             impl.mController->SetVerticalAlignment( alignment );
260           }
261         }
262         break;
263       }
264
265       case Toolkit::TextLabel::Property::UNUSED_PROPERTY_TEXT_COLOR:
266       {
267         label.SetProperty( Toolkit::TextLabel::Property::TEXT_COLOR, value );
268         impl.mTextUpdateNeeded = true;
269         break;
270       }
271
272       case Toolkit::TextLabel::Property::SHADOW_OFFSET:
273       {
274         if( impl.mController )
275         {
276           const Vector2& shadowOffset = value.Get< Vector2 >();
277           if ( impl.mController->GetShadowOffset() != shadowOffset )
278           {
279             impl.mController->SetShadowOffset( shadowOffset );
280             impl.mTextUpdateNeeded = true;
281           }
282         }
283         break;
284       }
285       case Toolkit::TextLabel::Property::SHADOW_COLOR:
286       {
287         if( impl.mController )
288         {
289           const Vector4& shadowColor = value.Get< Vector4 >();
290           if ( impl.mController->GetShadowColor() != shadowColor )
291           {
292             impl.mController->SetShadowColor( shadowColor );
293             impl.mTextUpdateNeeded = true;
294           }
295         }
296         break;
297       }
298       case Toolkit::TextLabel::Property::UNDERLINE_COLOR:
299       {
300         if( impl.mController )
301         {
302           const Vector4& color = value.Get< Vector4 >();
303           if ( impl.mController->GetUnderlineColor() != color )
304           {
305             impl.mController->SetUnderlineColor( color );
306             impl.mTextUpdateNeeded = true;
307           }
308         }
309         break;
310       }
311       case Toolkit::TextLabel::Property::UNDERLINE_ENABLED:
312       {
313         if( impl.mController )
314         {
315           const bool enabled = value.Get< bool >();
316           if ( impl.mController->IsUnderlineEnabled() != enabled )
317           {
318             impl.mController->SetUnderlineEnabled( enabled );
319             impl.mTextUpdateNeeded = true;
320           }
321         }
322         break;
323       }
324
325       case Toolkit::TextLabel::Property::UNDERLINE_HEIGHT:
326       {
327         if( impl.mController )
328         {
329           float height = value.Get< float >();
330           if( fabsf( impl.mController->GetUnderlineHeight() - height ) > Math::MACHINE_EPSILON_1000 )
331           {
332             impl.mController->SetUnderlineHeight( height );
333             impl.mTextUpdateNeeded = true;
334           }
335         }
336         break;
337       }
338       case Toolkit::TextLabel::Property::ENABLE_MARKUP:
339       {
340         if( impl.mController )
341         {
342           const bool enableMarkup = value.Get<bool>();
343           impl.mController->SetMarkupProcessorEnabled( enableMarkup );
344         }
345         break;
346       }
347       case Toolkit::TextLabel::Property::ENABLE_AUTO_SCROLL:
348       {
349         if( impl.mController )
350         {
351           const bool enableAutoScroll = value.Get<bool>();
352           // If request to auto scroll is the same as current state then do nothing.
353           if ( enableAutoScroll != impl.mController->IsAutoScrollEnabled() )
354           {
355              // If request is disable (false) and auto scrolling is enabled then need to stop it
356              if ( enableAutoScroll == false )
357              {
358                if( impl.mTextScroller )
359                {
360                  impl.mTextScroller->StopScrolling();
361                }
362              }
363              // If request is enable (true) then start autoscroll as not already running
364              else
365              {
366                impl.mController->SetAutoScrollEnabled( enableAutoScroll );
367              }
368           }
369         }
370         break;
371       }
372       case Toolkit::TextLabel::Property::AUTO_SCROLL_STOP_MODE:
373       {
374         if( !impl.mTextScroller )
375         {
376           impl.mTextScroller = Text::TextScroller::New( impl );
377         }
378         Toolkit::TextLabel::AutoScrollStopMode::Type stopMode = impl.mTextScroller->GetStopMode();
379         if( Scripting::GetEnumerationProperty< Toolkit::TextLabel::AutoScrollStopMode::Type >( value,
380                                                                                                     AUTO_SCROLL_STOP_MODE_TABLE,
381                                                                                                     AUTO_SCROLL_STOP_MODE_TABLE_COUNT,
382                                                                                                     stopMode ) )
383         {
384             impl.mTextScroller->SetStopMode( stopMode );
385         }
386         break;
387       }
388       case Toolkit::TextLabel::Property::AUTO_SCROLL_SPEED:
389       {
390         if( !impl.mTextScroller )
391         {
392           impl.mTextScroller = Text::TextScroller::New( impl );
393         }
394         impl.mTextScroller->SetSpeed( value.Get<int>() );
395         break;
396       }
397       case Toolkit::TextLabel::Property::AUTO_SCROLL_LOOP_COUNT:
398       {
399         if( !impl.mTextScroller )
400         {
401           impl.mTextScroller = Text::TextScroller::New( impl );
402         }
403         impl.mTextScroller->SetLoopCount( value.Get<int>() );
404         break;
405       }
406       case Toolkit::TextLabel::Property::AUTO_SCROLL_LOOP_DELAY:
407       {
408          if( !impl.mTextScroller )
409         {
410           impl.mTextScroller = Text::TextScroller::New( impl );
411         }
412         impl.mTextScroller->SetLoopDelay( value.Get<float>() );
413         break;
414       }
415       case Toolkit::TextLabel::Property::AUTO_SCROLL_GAP:
416       {
417         if( !impl.mTextScroller )
418         {
419           impl.mTextScroller = Text::TextScroller::New( impl );
420         }
421         impl.mTextScroller->SetGap( value.Get<float>() );
422         break;
423       }
424       case Toolkit::TextLabel::Property::LINE_SPACING:
425       {
426         if( impl.mController )
427         {
428           const float lineSpacing = value.Get<float>();
429
430           // Don't trigger anything if the line spacing didn't change
431           if( impl.mController->SetDefaultLineSpacing( lineSpacing ) )
432           {
433             impl.mTextUpdateNeeded = true;
434           }
435         }
436         break;
437       }
438       case Toolkit::TextLabel::Property::UNDERLINE:
439       {
440         const bool update = SetUnderlineProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
441         if( update )
442         {
443           impl.mTextUpdateNeeded = true;
444         }
445         break;
446       }
447       case Toolkit::TextLabel::Property::SHADOW:
448       {
449         const bool update = SetShadowProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
450         if( update )
451         {
452           impl.mTextUpdateNeeded = true;
453         }
454         break;
455       }
456       case Toolkit::TextLabel::Property::EMBOSS:
457       {
458         const bool update = SetEmbossProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
459         if( update )
460         {
461           impl.mTextUpdateNeeded = true;
462         }
463         break;
464       }
465       case Toolkit::TextLabel::Property::OUTLINE:
466       {
467         const bool update = SetOutlineProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
468         if( update )
469         {
470           impl.mTextUpdateNeeded = true;
471         }
472         break;
473       }
474       case Toolkit::TextLabel::Property::PIXEL_SIZE:
475       {
476         if( impl.mController )
477         {
478           const float pixelSize = value.Get< float >();
479           DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel %p PIXEL_SIZE %f\n", impl.mController.Get(), pixelSize );
480
481           if( !Equals( impl.mController->GetDefaultFontSize( Text::Controller::PIXEL_SIZE ), pixelSize ) )
482           {
483             impl.mController->SetDefaultFontSize( pixelSize, Text::Controller::PIXEL_SIZE );
484           }
485         }
486         break;
487       }
488       case Toolkit::TextLabel::Property::ELLIPSIS:
489       {
490         if( impl.mController )
491         {
492           const bool ellipsis = value.Get<bool>();
493           DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel %p ELLIPSIS %d\n", impl.mController.Get(), ellipsis );
494
495           impl.mController->SetTextElideEnabled( ellipsis );
496         }
497         break;
498       }
499       case Toolkit::TextLabel::Property::LINE_WRAP_MODE:
500       {
501         if( impl.mController )
502         {
503           Text::LineWrap::Mode lineWrapMode( static_cast< Text::LineWrap::Mode >( -1 ) ); // Set to invalid value to ensure a valid mode does get set
504           if( GetLineWrapModeEnumeration( value, lineWrapMode ) )
505           {
506             DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel %p LineWrap::MODE %d\n", impl.mController.Get(), lineWrapMode );
507             impl.mController->SetLineWrapMode( lineWrapMode );
508           }
509         }
510         break;
511       }
512       case Toolkit::DevelTextLabel::Property::VERTICAL_LINE_ALIGNMENT:
513       {
514         if( impl.mController && impl.mController->GetTextModel() )
515         {
516           DevelText::VerticalLineAlignment::Type alignment = static_cast<DevelText::VerticalLineAlignment::Type>( value.Get<int>() );
517
518           impl.mController->SetVerticalLineAlignment( alignment );
519
520           // Property doesn't affect the layout, only Visual must be updated
521           TextVisual::EnableRendererUpdate( impl.mVisual );
522
523           // No need to trigger full re-layout. Instead call UpdateRenderer() directly
524           TextVisual::UpdateRenderer( impl.mVisual );
525         }
526         break;
527       }
528       case Toolkit::DevelTextLabel::Property::BACKGROUND:
529       {
530         const bool update = SetBackgroundProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
531         if( update )
532         {
533           impl.mTextUpdateNeeded = true;
534         }
535         break;
536       }
537       case Toolkit::DevelTextLabel::Property::IGNORE_SPACES_AFTER_TEXT:
538       {
539         impl.mController->SetIgnoreSpacesAfterText(value.Get< bool >());
540         break;
541       }
542       case Toolkit::DevelTextLabel::Property::MATCH_SYSTEM_LANGUAGE_DIRECTION:
543       {
544         impl.mController->SetMatchSystemLanguageDirection(value.Get< bool >());
545         break;
546       }
547     }
548
549     // Request relayout when text update is needed. It's necessary to call it
550     // as changing the property not via UI interaction brings no effect if only
551     // the mTextUpdateNeeded is changed.
552     if( impl.mTextUpdateNeeded )
553     {
554       // need to request relayout as size of text may have changed
555       impl.RequestTextRelayout();
556     }
557   }
558
559
560
561
562 }
563
564 Property::Value TextLabel::GetProperty( BaseObject* object, Property::Index index )
565 {
566   Property::Value value;
567
568   Toolkit::TextLabel label = Toolkit::TextLabel::DownCast( Dali::BaseHandle( object ) );
569
570   if( label )
571   {
572     TextLabel& impl( GetImpl( label ) );
573     switch( index )
574     {
575       case Toolkit::TextLabel::Property::RENDERING_BACKEND:
576       {
577         DALI_LOG_WARNING("[%s] Using deprecated Property TextLabel::Property::RENDERING_BACKEND which is no longer supported and will be ignored\n", __FUNCTION__);
578
579         value = impl.mRenderingBackend;
580         break;
581       }
582       case Toolkit::TextLabel::Property::TEXT:
583       {
584         if( impl.mController )
585         {
586           std::string text;
587           impl.mController->GetText( text );
588           value = text;
589         }
590         break;
591       }
592       case Toolkit::TextLabel::Property::FONT_FAMILY:
593       {
594         if( impl.mController )
595         {
596           value = impl.mController->GetDefaultFontFamily();
597         }
598         break;
599       }
600       case Toolkit::TextLabel::Property::FONT_STYLE:
601       {
602         GetFontStyleProperty( impl.mController, value, Text::FontStyle::DEFAULT );
603         break;
604       }
605       case Toolkit::TextLabel::Property::POINT_SIZE:
606       {
607         if( impl.mController )
608         {
609           value = impl.mController->GetDefaultFontSize( Text::Controller::POINT_SIZE );
610         }
611         break;
612       }
613       case Toolkit::TextLabel::Property::MULTI_LINE:
614       {
615         if( impl.mController )
616         {
617           value = impl.mController->IsMultiLineEnabled();
618         }
619         break;
620       }
621       case Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT:
622       {
623         if( impl.mController )
624         {
625           const char* name = Text::GetHorizontalAlignmentString( impl.mController->GetHorizontalAlignment() );
626
627            if ( name )
628            {
629              value = std::string( name );
630            }
631         }
632         break;
633       }
634       case Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT:
635       {
636         if( impl.mController )
637         {
638           const char* name = Text::GetVerticalAlignmentString( impl.mController->GetVerticalAlignment() );
639           if( name )
640           {
641             value = std::string( name );
642           }
643         }
644         break;
645       }
646       case Toolkit::TextLabel::Property::UNUSED_PROPERTY_TEXT_COLOR:
647       {
648         value = label.GetProperty( Toolkit::TextLabel::Property::TEXT_COLOR );
649         break;
650       }
651       case Toolkit::TextLabel::Property::SHADOW_OFFSET:
652       {
653         if ( impl.mController )
654         {
655           value = impl.mController->GetShadowOffset();
656         }
657         break;
658       }
659       case Toolkit::TextLabel::Property::SHADOW_COLOR:
660       {
661         if ( impl.mController )
662         {
663           value = impl.mController->GetShadowColor();
664         }
665         break;
666       }
667       case Toolkit::TextLabel::Property::UNDERLINE_COLOR:
668       {
669         if ( impl.mController )
670         {
671           value = impl.mController->GetUnderlineColor();
672         }
673         break;
674       }
675       case Toolkit::TextLabel::Property::UNDERLINE_ENABLED:
676       {
677         if ( impl.mController )
678         {
679           value = impl.mController->IsUnderlineEnabled();
680         }
681         break;
682       }
683       case Toolkit::TextLabel::Property::UNDERLINE_HEIGHT:
684       {
685         if ( impl.mController )
686         {
687           value = impl.mController->GetUnderlineHeight();
688         }
689         break;
690       }
691       case Toolkit::TextLabel::Property::ENABLE_MARKUP:
692       {
693         if( impl.mController )
694         {
695           value = impl.mController->IsMarkupProcessorEnabled();
696         }
697         break;
698       }
699       case Toolkit::TextLabel::Property::ENABLE_AUTO_SCROLL:
700       {
701         if( impl.mController )
702         {
703           value = impl.mController->IsAutoScrollEnabled();
704         }
705         break;
706       }
707       case Toolkit::TextLabel::Property::AUTO_SCROLL_STOP_MODE:
708       {
709         if( impl.mTextScroller )
710         {
711           const char* mode = Scripting::GetEnumerationName< Toolkit::TextLabel::AutoScrollStopMode::Type >( impl.mTextScroller->GetStopMode(),
712                                                                                                                  AUTO_SCROLL_STOP_MODE_TABLE,
713                                                                                                                  AUTO_SCROLL_STOP_MODE_TABLE_COUNT );
714           if( mode )
715           {
716             value = std::string( mode );
717           }
718         }
719         break;
720       }
721       case Toolkit::TextLabel::Property::AUTO_SCROLL_SPEED:
722       {
723         TextLabel& impl( GetImpl( label ) );
724         if ( impl.mTextScroller )
725         {
726           value = impl.mTextScroller->GetSpeed();
727         }
728         break;
729       }
730       case Toolkit::TextLabel::Property::AUTO_SCROLL_LOOP_COUNT:
731       {
732         if( impl.mController )
733         {
734           TextLabel& impl( GetImpl( label ) );
735           if ( impl.mTextScroller )
736           {
737             value = impl.mTextScroller->GetLoopCount();
738           }
739         }
740         break;
741       }
742       case Toolkit::TextLabel::Property::AUTO_SCROLL_LOOP_DELAY:
743       {
744         if( impl.mController )
745         {
746           TextLabel& impl( GetImpl( label ) );
747           if ( impl.mTextScroller )
748           {
749             value = impl.mTextScroller->GetLoopDelay();
750           }
751         }
752         break;
753       }
754       case Toolkit::TextLabel::Property::AUTO_SCROLL_GAP:
755       {
756         TextLabel& impl( GetImpl( label ) );
757         if ( impl.mTextScroller )
758         {
759           value = impl.mTextScroller->GetGap();
760         }
761         break;
762       }
763       case Toolkit::TextLabel::Property::LINE_SPACING:
764       {
765         if( impl.mController )
766         {
767           value = impl.mController->GetDefaultLineSpacing();
768         }
769         break;
770       }
771       case Toolkit::TextLabel::Property::UNDERLINE:
772       {
773         GetUnderlineProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
774         break;
775       }
776       case Toolkit::TextLabel::Property::SHADOW:
777       {
778         GetShadowProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
779         break;
780       }
781       case Toolkit::TextLabel::Property::EMBOSS:
782       {
783         GetEmbossProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
784         break;
785       }
786       case Toolkit::TextLabel::Property::OUTLINE:
787       {
788         GetOutlineProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
789         break;
790       }
791       case Toolkit::TextLabel::Property::PIXEL_SIZE:
792       {
793         if( impl.mController )
794         {
795           value = impl.mController->GetDefaultFontSize( Text::Controller::PIXEL_SIZE );
796         }
797         break;
798       }
799       case Toolkit::TextLabel::Property::ELLIPSIS:
800       {
801         if( impl.mController )
802         {
803           value = impl.mController->IsTextElideEnabled();
804         }
805         break;
806       }
807       case Toolkit::TextLabel::Property::LINE_WRAP_MODE:
808       {
809         if( impl.mController )
810         {
811           value = impl.mController->GetLineWrapMode();
812         }
813         break;
814       }
815       case Toolkit::TextLabel::Property::LINE_COUNT:
816       {
817         if( impl.mController )
818         {
819           float width = label.GetProperty( Actor::Property::SIZE_WIDTH ).Get<float>();
820           value = impl.mController->GetLineCount( width );
821         }
822         break;
823       }
824       case Toolkit::DevelTextLabel::Property::TEXT_DIRECTION:
825       {
826         if( impl.mController )
827         {
828           value = impl.mController->GetTextDirection();
829         }
830         break;
831       }
832       case Toolkit::DevelTextLabel::Property::VERTICAL_LINE_ALIGNMENT:
833       {
834         if( impl.mController )
835         {
836           value = impl.mController->GetVerticalLineAlignment();
837         }
838         break;
839       }
840       case Toolkit::DevelTextLabel::Property::BACKGROUND:
841       {
842         GetBackgroundProperties( impl.mController, value, Text::EffectStyle::DEFAULT );
843         break;
844       }
845       case Toolkit::DevelTextLabel::Property::IGNORE_SPACES_AFTER_TEXT:
846       {
847         value = impl.mController->IsIgnoreSpacesAfterText();
848         break;
849       }
850       case Toolkit::DevelTextLabel::Property::MATCH_SYSTEM_LANGUAGE_DIRECTION:
851       {
852         value = impl.mController->IsMatchSystemLanguageDirection();
853         break;
854       }
855     }
856   }
857
858   return value;
859 }
860
861 void TextLabel::OnInitialize()
862 {
863   Actor self = Self();
864
865   Property::Map propertyMap;
866   propertyMap.Add( Toolkit::Visual::Property::TYPE, Toolkit::Visual::TEXT );
867
868   mVisual =  Toolkit::VisualFactory::Get().CreateVisual( propertyMap );
869   DevelControl::RegisterVisual( *this, Toolkit::TextLabel::Property::TEXT, mVisual  );
870
871   TextVisual::SetAnimatableTextColorProperty( mVisual, Toolkit::TextLabel::Property::TEXT_COLOR );
872
873   mController = TextVisual::GetController(mVisual);
874   if( mController )
875   {
876     mController->SetControlInterface(this);
877   }
878
879   // Use height-for-width negotiation by default
880   self.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
881   self.SetResizePolicy( ResizePolicy::DIMENSION_DEPENDENCY, Dimension::HEIGHT );
882
883   // Enable the text ellipsis.
884   mController->SetTextElideEnabled( true );   // If false then text larger than control will overflow
885
886   // Sets layoutDirection value
887   Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>( self.GetProperty( Dali::Actor::Property::LAYOUT_DIRECTION ).Get<int>() );
888   mController->SetLayoutDirection( layoutDirection );
889
890   Layout::Engine& engine = mController->GetLayoutEngine();
891   engine.SetCursorWidth( 0u ); // Do not layout space for the cursor.
892 }
893
894 void TextLabel::OnStyleChange( Toolkit::StyleManager styleManager, StyleChange::Type change )
895 {
896   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextLabel::OnStyleChange\n");
897
898   switch ( change )
899   {
900     case StyleChange::DEFAULT_FONT_CHANGE:
901     {
902       // Property system did not set the font so should update it.
903       const std::string& newFont = GetImpl( styleManager ).GetDefaultFontFamily();
904       DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::OnStyleChange StyleChange::DEFAULT_FONT_CHANGE newFont(%s)\n", newFont.c_str() );
905       mController->UpdateAfterFontChange( newFont );
906       RelayoutRequest();
907       break;
908     }
909     case StyleChange::DEFAULT_FONT_SIZE_CHANGE:
910     {
911       GetImpl( styleManager ).ApplyThemeStyle( Toolkit::Control( GetOwner() ) );
912       RelayoutRequest();
913       break;
914     }
915     case StyleChange::THEME_CHANGE:
916     {
917       // Nothing to do, let control base class handle this
918       break;
919     }
920   }
921
922   // Up call to Control
923   Control::OnStyleChange( styleManager, change );
924 }
925
926 Vector3 TextLabel::GetNaturalSize()
927 {
928   Extents padding;
929   padding = Self().GetProperty<Extents>( Toolkit::Control::Property::PADDING );
930
931   Vector3 naturalSize = mController->GetNaturalSize();
932   naturalSize.width += ( padding.start + padding.end );
933   naturalSize.height += ( padding.top + padding.bottom );
934
935   return naturalSize;
936 }
937
938 float TextLabel::GetHeightForWidth( float width )
939 {
940   Extents padding;
941   padding = Self().GetProperty<Extents>( Toolkit::Control::Property::PADDING );
942
943   return mController->GetHeightForWidth( width ) + padding.top + padding.bottom;
944 }
945
946 void TextLabel::OnPropertySet( Property::Index index, Property::Value propertyValue )
947 {
948   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextLabel::OnPropertySet index[%d]\n", index );
949
950   switch ( index )
951   {
952     case Toolkit::TextLabel::Property::TEXT_COLOR:
953     {
954       const Vector4& textColor = propertyValue.Get< Vector4 >();
955       if( mController->GetDefaultColor() != textColor )
956       {
957          mController->SetDefaultColor( textColor );
958          mTextUpdateNeeded = true;
959       }
960       break;
961     }
962     default:
963     {
964       Control::OnPropertySet( index, propertyValue ); // up call to control for non-handled properties
965       break;
966     }
967   }
968 }
969
970 void TextLabel::OnRelayout( const Vector2& size, RelayoutContainer& container )
971 {
972   DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::OnRelayout\n" );
973
974   Extents padding;
975   padding = Self().GetProperty<Extents>( Toolkit::Control::Property::PADDING );
976
977   Vector2 contentSize( size.x - ( padding.start + padding.end ), size.y - ( padding.top + padding.bottom ) );
978
979   // Support Right-To-Left
980   Dali::LayoutDirection::Type layoutDirection = static_cast<Dali::LayoutDirection::Type>( Self().GetProperty( Dali::Actor::Property::LAYOUT_DIRECTION ).Get<int>() );
981
982   const Text::Controller::UpdateTextType updateTextType = mController->Relayout( contentSize, layoutDirection );
983
984   if( ( Text::Controller::NONE_UPDATED != ( Text::Controller::MODEL_UPDATED & updateTextType ) )
985      || mTextUpdateNeeded )
986   {
987     DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::OnRelayout IsAutoScrollEnabled[%s] [%p]\n", ( mController->IsAutoScrollEnabled())?"true":"false", this );
988
989     // Update the visual
990     TextVisual::EnableRendererUpdate( mVisual );
991
992     // Support Right-To-Left of padding
993     if( Dali::LayoutDirection::RIGHT_TO_LEFT == layoutDirection )
994     {
995       std::swap( padding.start, padding.end );
996     }
997
998     // Calculate the size of the visual that can fit the text
999     Size layoutSize = mController->GetTextModel()->GetLayoutSize();
1000     layoutSize.x = contentSize.x;
1001
1002     const Vector2& shadowOffset = mController->GetTextModel()->GetShadowOffset();
1003     if ( shadowOffset.y > Math::MACHINE_EPSILON_1 )
1004     {
1005       layoutSize.y += shadowOffset.y;
1006     }
1007
1008     float outlineWidth = mController->GetTextModel()->GetOutlineWidth();
1009     layoutSize.y += outlineWidth * 2.0f;
1010     layoutSize.y = std::min( layoutSize.y, contentSize.y );
1011
1012     // Calculate the offset for vertical alignment only, as the layout engine will do the horizontal alignment.
1013     Vector2 alignmentOffset;
1014     alignmentOffset.x = 0.0f;
1015     alignmentOffset.y = ( contentSize.y - layoutSize.y ) * VERTICAL_ALIGNMENT_TABLE[mController->GetVerticalAlignment()];
1016
1017     Property::Map visualTransform;
1018     visualTransform.Add( Toolkit::Visual::Transform::Property::SIZE, layoutSize )
1019                    .Add( Toolkit::Visual::Transform::Property::SIZE_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
1020                    .Add( Toolkit::Visual::Transform::Property::OFFSET, Vector2( padding.start, padding.top ) + alignmentOffset )
1021                    .Add( Toolkit::Visual::Transform::Property::OFFSET_POLICY, Vector2( Toolkit::Visual::Transform::Policy::ABSOLUTE, Toolkit::Visual::Transform::Policy::ABSOLUTE ) )
1022                    .Add( Toolkit::Visual::Transform::Property::ORIGIN, Toolkit::Align::TOP_BEGIN )
1023                    .Add( Toolkit::Visual::Transform::Property::ANCHOR_POINT, Toolkit::Align::TOP_BEGIN );
1024     mVisual.SetTransformAndSize( visualTransform, size );
1025
1026     if ( mController->IsAutoScrollEnabled() )
1027     {
1028       SetUpAutoScrolling();
1029     }
1030
1031     mTextUpdateNeeded = false;
1032   }
1033 }
1034
1035 void TextLabel::RequestTextRelayout()
1036 {
1037   RelayoutRequest();
1038   Toolkit::DevelControl::RequestLayout( *this );
1039 }
1040
1041 void TextLabel::SetUpAutoScrolling()
1042 {
1043   const Size& controlSize = mController->GetView().GetControlSize();
1044   const Size textNaturalSize = GetNaturalSize().GetVectorXY(); // As relayout of text may not be done at this point natural size is used to get size. Single line scrolling only.
1045   const Text::CharacterDirection direction = mController->GetAutoScrollDirection();
1046
1047   DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::SetUpAutoScrolling textNaturalSize[%f,%f] controlSize[%f,%f]\n",
1048                  textNaturalSize.x,textNaturalSize.y , controlSize.x,controlSize.y );
1049
1050   if ( !mTextScroller )
1051   {
1052     DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::SetUpAutoScrolling Creating default TextScoller\n" );
1053
1054     // If speed, loopCount or gap not set via property system then will need to create a TextScroller with defaults
1055     mTextScroller = Text::TextScroller::New( *this );
1056   }
1057
1058   // Calculate the actual gap before scrolling wraps.
1059   int textPadding = std::max( controlSize.x - textNaturalSize.x, 0.0f );
1060   float wrapGap = std::max( mTextScroller->GetGap(), textPadding );
1061   Vector2 textureSize = textNaturalSize + Vector2(wrapGap, 0.0f); // Add the gap as a part of the texture
1062
1063   // Create a texture of the text for scrolling
1064   Size verifiedSize = textureSize;
1065   const int maxTextureSize = Dali::GetMaxTextureSize();
1066
1067   //if the texture size width exceed maxTextureSize, modify the visual model size and enabled the ellipsis
1068   if( verifiedSize.width > maxTextureSize )
1069   {
1070     verifiedSize.width = maxTextureSize;
1071     if( textNaturalSize.width > maxTextureSize )
1072     {
1073       mController->SetTextElideEnabled( true );
1074     }
1075     GetHeightForWidth( maxTextureSize );
1076     wrapGap = std::max( maxTextureSize - textNaturalSize.width, 0.0f );
1077   }
1078
1079   Text::TypesetterPtr typesetter = Text::Typesetter::New( mController->GetTextModel() );
1080
1081   PixelData data = typesetter->Render( verifiedSize, mController->GetTextDirection(), Text::Typesetter::RENDER_TEXT_AND_STYLES, true, Pixel::RGBA8888 ); // ignore the horizontal alignment
1082   Texture texture = Texture::New( Dali::TextureType::TEXTURE_2D,
1083                                   data.GetPixelFormat(),
1084                                   data.GetWidth(),
1085                                   data.GetHeight() );
1086   texture.Upload( data );
1087
1088   TextureSet textureSet = TextureSet::New();
1089   textureSet.SetTexture( 0u, texture );
1090
1091   // Filter mode needs to be set to linear to produce better quality while scaling.
1092   Sampler sampler = Sampler::New();
1093   sampler.SetFilterMode( FilterMode::LINEAR, FilterMode::LINEAR );
1094   sampler.SetWrapMode( Dali::WrapMode::DEFAULT, Dali::WrapMode::REPEAT, Dali::WrapMode::DEFAULT ); // Wrap the texture in the x direction
1095   textureSet.SetSampler( 0u, sampler );
1096
1097   // Set parameters for scrolling
1098   Renderer renderer = static_cast<Internal::Visual::Base&>( GetImplementation( mVisual ) ).GetRenderer();
1099   mTextScroller->SetParameters( Self(), renderer, textureSet, controlSize, verifiedSize, wrapGap, direction, mController->GetHorizontalAlignment(), mController->GetVerticalAlignment() );
1100 }
1101
1102 void TextLabel::ScrollingFinished()
1103 {
1104   // Pure Virtual from TextScroller Interface
1105   DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::ScrollingFinished\n");
1106   mController->SetAutoScrollEnabled( false );
1107   mController->SetTextElideEnabled( true );
1108   RequestTextRelayout();
1109 }
1110
1111 TextLabel::TextLabel()
1112 : Control( ControlBehaviour( CONTROL_BEHAVIOUR_DEFAULT ) ),
1113   mRenderingBackend( DEFAULT_RENDERING_BACKEND ),
1114   mTextUpdateNeeded( false )
1115 {
1116 }
1117
1118 TextLabel::~TextLabel()
1119 {
1120 }
1121
1122 } // namespace Internal
1123
1124 } // namespace Toolkit
1125
1126 } // namespace Dali