Merge "Changed signal order for StyleManager" into devel/master
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / controls / text-controls / text-label-impl.cpp
1 /*
2  * Copyright (c) 2015 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/devel-api/object/type-registry-helper.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali-toolkit/public-api/text/rendering-backend.h>
27 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
28 #include <dali-toolkit/internal/text/rendering/text-backend.h>
29 #include <dali-toolkit/internal/text/text-font-style.h>
30 #include <dali-toolkit/internal/text/text-view.h>
31 #include <dali-toolkit/internal/styling/style-manager-impl.h>
32
33 using Dali::Toolkit::Text::LayoutEngine;
34 using Dali::Toolkit::Text::Backend;
35
36 namespace Dali
37 {
38
39 namespace Toolkit
40 {
41
42 namespace Internal
43 {
44
45 namespace
46 {
47   const unsigned int DEFAULT_RENDERING_BACKEND = Dali::Toolkit::Text::DEFAULT_RENDERING_BACKEND;
48 }
49
50 namespace
51 {
52
53 #if defined(DEBUG_ENABLED)
54   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_CONTROLS");
55 #endif
56
57 const Scripting::StringEnum HORIZONTAL_ALIGNMENT_STRING_TABLE[] =
58 {
59   { "BEGIN",  Toolkit::Text::LayoutEngine::HORIZONTAL_ALIGN_BEGIN  },
60   { "CENTER", Toolkit::Text::LayoutEngine::HORIZONTAL_ALIGN_CENTER },
61   { "END",    Toolkit::Text::LayoutEngine::HORIZONTAL_ALIGN_END    },
62 };
63 const unsigned int HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE ) / sizeof( HORIZONTAL_ALIGNMENT_STRING_TABLE[0] );
64
65 const Scripting::StringEnum VERTICAL_ALIGNMENT_STRING_TABLE[] =
66 {
67   { "TOP",    Toolkit::Text::LayoutEngine::VERTICAL_ALIGN_TOP    },
68   { "CENTER", Toolkit::Text::LayoutEngine::VERTICAL_ALIGN_CENTER },
69   { "BOTTOM", Toolkit::Text::LayoutEngine::VERTICAL_ALIGN_BOTTOM },
70 };
71 const unsigned int VERTICAL_ALIGNMENT_STRING_TABLE_COUNT = sizeof( VERTICAL_ALIGNMENT_STRING_TABLE ) / sizeof( VERTICAL_ALIGNMENT_STRING_TABLE[0] );
72
73 // Type registration
74 BaseHandle Create()
75 {
76   return Toolkit::TextLabel::New();
77 }
78
79 // Setup properties, signals and actions using the type-registry.
80 DALI_TYPE_REGISTRATION_BEGIN( Toolkit::TextLabel, Toolkit::Control, Create );
81
82 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "renderingBackend",     INTEGER, RENDERING_BACKEND    )
83 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "text",                 STRING,  TEXT                 )
84 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "fontFamily",           STRING,  FONT_FAMILY          )
85 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "fontStyle",            STRING,  FONT_STYLE           )
86 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "pointSize",            FLOAT,   POINT_SIZE           )
87 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "multiLine",            BOOLEAN, MULTI_LINE           )
88 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "horizontalAlignment",  STRING,  HORIZONTAL_ALIGNMENT )
89 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "verticalAlignment",    STRING,  VERTICAL_ALIGNMENT   )
90 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "textColor",            VECTOR4, TEXT_COLOR           )
91 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "shadowOffset",         VECTOR2, SHADOW_OFFSET        )
92 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "shadowColor",          VECTOR4, SHADOW_COLOR         )
93 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "underlineEnabled",     BOOLEAN, UNDERLINE_ENABLED    )
94 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "underlineColor",       VECTOR4, UNDERLINE_COLOR      )
95 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "underlineHeight",      FLOAT,   UNDERLINE_HEIGHT     )
96 DALI_PROPERTY_REGISTRATION( Toolkit, TextLabel, "enableMarkup",         BOOLEAN, ENABLE_MARKUP        )
97
98 DALI_TYPE_REGISTRATION_END()
99
100 } // namespace
101
102 Toolkit::TextLabel TextLabel::New()
103 {
104   // Create the implementation, temporarily owned by this handle on stack
105   IntrusivePtr< TextLabel > impl = new TextLabel();
106
107   // Pass ownership to CustomActor handle
108   Toolkit::TextLabel handle( *impl );
109
110   // Second-phase init of the implementation
111   // This can only be done after the CustomActor connection has been made...
112   impl->Initialize();
113
114   return handle;
115 }
116
117 void TextLabel::SetProperty( BaseObject* object, Property::Index index, const Property::Value& value )
118 {
119   Toolkit::TextLabel label = Toolkit::TextLabel::DownCast( Dali::BaseHandle( object ) );
120
121   if( label )
122   {
123     TextLabel& impl( GetImpl( label ) );
124     switch( index )
125     {
126       case Toolkit::TextLabel::Property::RENDERING_BACKEND:
127       {
128         int backend = value.Get< int >();
129
130 #ifndef ENABLE_VECTOR_BASED_TEXT_RENDERING
131         if( Text::RENDERING_VECTOR_BASED == backend )
132         {
133           backend = TextAbstraction::BITMAP_GLYPH; // Fallback to bitmap-based rendering
134         }
135 #endif
136         if( impl.mRenderingBackend != backend )
137         {
138           impl.mRenderingBackend = backend;
139           impl.mRenderer.Reset();
140
141           if( impl.mController )
142           {
143             // When using the vector-based rendering, the size of the GLyphs are different
144             TextAbstraction::GlyphType glyphType = (Text::RENDERING_VECTOR_BASED == impl.mRenderingBackend) ? TextAbstraction::VECTOR_GLYPH : TextAbstraction::BITMAP_GLYPH;
145             impl.mController->SetGlyphType( glyphType );
146           }
147         }
148         break;
149       }
150       case Toolkit::TextLabel::Property::TEXT:
151       {
152         if( impl.mController )
153         {
154           impl.mController->SetText( value.Get< std::string >() );
155         }
156         break;
157       }
158       case Toolkit::TextLabel::Property::FONT_FAMILY:
159       {
160         if( impl.mController )
161         {
162           const std::string fontFamily = value.Get< std::string >();
163
164           DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::SetProperty Property::FONT_FAMILY newFont(%s)\n", fontFamily.c_str() );
165           impl.mController->SetDefaultFontFamily( fontFamily );
166         }
167         break;
168       }
169       case Toolkit::TextLabel::Property::FONT_STYLE:
170       {
171         SetFontStyleProperty( impl.mController, value, Text::FontStyle::DEFAULT );
172         break;
173       }
174       case Toolkit::TextLabel::Property::POINT_SIZE:
175       {
176         if( impl.mController )
177         {
178           const float pointSize = value.Get< float >();
179
180           if( !Equals( impl.mController->GetDefaultPointSize(), pointSize ) )
181           {
182             impl.mController->SetDefaultPointSize( pointSize );
183           }
184         }
185         break;
186       }
187       case Toolkit::TextLabel::Property::MULTI_LINE:
188       {
189         if( impl.mController )
190         {
191           impl.mController->SetMultiLineEnabled( value.Get< bool >() );
192         }
193         break;
194       }
195       case Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT:
196       {
197         if( impl.mController )
198         {
199           LayoutEngine::HorizontalAlignment alignment( LayoutEngine::HORIZONTAL_ALIGN_BEGIN );
200           if( Scripting::GetEnumeration< Toolkit::Text::LayoutEngine::HorizontalAlignment >( value.Get< std::string >().c_str(),
201                                                                                              HORIZONTAL_ALIGNMENT_STRING_TABLE,
202                                                                                              HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT,
203                                                                                              alignment ) )
204           {
205             impl.mController->SetHorizontalAlignment( alignment );
206           }
207         }
208         break;
209       }
210       case Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT:
211       {
212         if( impl.mController )
213         {
214           LayoutEngine::VerticalAlignment alignment( LayoutEngine::VERTICAL_ALIGN_BOTTOM );
215           if( Scripting::GetEnumeration< Toolkit::Text::LayoutEngine::VerticalAlignment >( value.Get< std::string >().c_str(),
216                                                                                            VERTICAL_ALIGNMENT_STRING_TABLE,
217                                                                                            VERTICAL_ALIGNMENT_STRING_TABLE_COUNT,
218                                                                                            alignment ) )
219           {
220             impl.mController->SetVerticalAlignment( alignment );
221           }
222         }
223         break;
224       }
225
226       case Toolkit::TextLabel::Property::TEXT_COLOR:
227       {
228         if( impl.mController )
229         {
230           const Vector4 textColor = value.Get< Vector4 >();
231           if( impl.mController->GetTextColor() != textColor )
232           {
233             impl.mController->SetTextColor( textColor );
234             impl.mRenderer.Reset();
235           }
236         }
237         break;
238       }
239
240       case Toolkit::TextLabel::Property::SHADOW_OFFSET:
241       {
242         if( impl.mController )
243         {
244           const Vector2 shadowOffset = value.Get< Vector2 >();
245           if ( impl.mController->GetShadowOffset() != shadowOffset )
246           {
247             impl.mController->SetShadowOffset( shadowOffset );
248             impl.mRenderer.Reset();
249           }
250         }
251         break;
252       }
253       case Toolkit::TextLabel::Property::SHADOW_COLOR:
254       {
255         if( impl.mController )
256         {
257           const Vector4 shadowColor = value.Get< Vector4 >();
258           if ( impl.mController->GetShadowColor() != shadowColor )
259           {
260             impl.mController->SetShadowColor( shadowColor );
261             impl.mRenderer.Reset();
262           }
263         }
264         break;
265       }
266       case Toolkit::TextLabel::Property::UNDERLINE_COLOR:
267       {
268         if( impl.mController )
269         {
270           const Vector4 color = value.Get< Vector4 >();
271           if ( impl.mController->GetUnderlineColor() != color )
272           {
273             impl.mController->SetUnderlineColor( color );
274             impl.mRenderer.Reset();
275           }
276         }
277         break;
278       }
279       case Toolkit::TextLabel::Property::UNDERLINE_ENABLED:
280       {
281         if( impl.mController )
282         {
283           const bool enabled = value.Get< bool >();
284           if ( impl.mController->IsUnderlineEnabled() != enabled )
285           {
286             impl.mController->SetUnderlineEnabled( enabled );
287             impl.mRenderer.Reset();
288           }
289         }
290         break;
291       }
292
293       case Toolkit::TextLabel::Property::UNDERLINE_HEIGHT:
294       {
295         if( impl.mController )
296         {
297           float height = value.Get< float >();
298           if( fabsf( impl.mController->GetUnderlineHeight() - height ) > Math::MACHINE_EPSILON_1000 )
299           {
300             impl.mController->SetUnderlineHeight( height );
301             impl.mRenderer.Reset();
302           }
303         }
304         break;
305       }
306       case Toolkit::TextLabel::Property::ENABLE_MARKUP:
307       {
308         if( impl.mController )
309         {
310           const bool enableMarkup = value.Get<bool>();
311           impl.mController->SetMarkupProcessorEnabled( enableMarkup );
312         }
313         break;
314       }
315     }
316   }
317 }
318
319 Property::Value TextLabel::GetProperty( BaseObject* object, Property::Index index )
320 {
321   Property::Value value;
322
323   Toolkit::TextLabel label = Toolkit::TextLabel::DownCast( Dali::BaseHandle( object ) );
324
325   if( label )
326   {
327     TextLabel& impl( GetImpl( label ) );
328     switch( index )
329     {
330       case Toolkit::TextLabel::Property::RENDERING_BACKEND:
331       {
332         value = impl.mRenderingBackend;
333         break;
334       }
335       case Toolkit::TextLabel::Property::TEXT:
336       {
337         if( impl.mController )
338         {
339           std::string text;
340           impl.mController->GetText( text );
341           value = text;
342         }
343         break;
344       }
345       case Toolkit::TextLabel::Property::FONT_FAMILY:
346       {
347         if( impl.mController )
348         {
349           value = impl.mController->GetDefaultFontFamily();
350         }
351         break;
352       }
353       case Toolkit::TextLabel::Property::FONT_STYLE:
354       {
355         GetFontStyleProperty( impl.mController, value, Text::FontStyle::DEFAULT );
356         break;
357       }
358       case Toolkit::TextLabel::Property::POINT_SIZE:
359       {
360         if( impl.mController )
361         {
362           value = impl.mController->GetDefaultPointSize();
363         }
364         break;
365       }
366       case Toolkit::TextLabel::Property::MULTI_LINE:
367       {
368         if( impl.mController )
369         {
370           value = impl.mController->IsMultiLineEnabled();
371         }
372         break;
373       }
374       case Toolkit::TextLabel::Property::HORIZONTAL_ALIGNMENT:
375       {
376         if( impl.mController )
377         {
378           const char* name = Scripting::GetEnumerationName< Toolkit::Text::LayoutEngine::HorizontalAlignment >( impl.mController->GetHorizontalAlignment(),
379                                                                                                                 HORIZONTAL_ALIGNMENT_STRING_TABLE,
380                                                                                                                 HORIZONTAL_ALIGNMENT_STRING_TABLE_COUNT );
381           if( name )
382           {
383             value = std::string( name );
384           }
385         }
386         break;
387       }
388       case Toolkit::TextLabel::Property::VERTICAL_ALIGNMENT:
389       {
390         if( impl.mController )
391         {
392           const char* name = Scripting::GetEnumerationName< Toolkit::Text::LayoutEngine::VerticalAlignment >( impl.mController->GetVerticalAlignment(),
393                                                                                                               VERTICAL_ALIGNMENT_STRING_TABLE,
394                                                                                                               VERTICAL_ALIGNMENT_STRING_TABLE_COUNT );
395           if( name )
396           {
397             value = std::string( name );
398           }
399         }
400         break;
401       }
402       case Toolkit::TextLabel::Property::TEXT_COLOR:
403       {
404         if ( impl.mController )
405         {
406           value = impl.mController->GetTextColor();
407         }
408         break;
409       }
410       case Toolkit::TextLabel::Property::SHADOW_OFFSET:
411       {
412         if ( impl.mController )
413         {
414           value = impl.mController->GetShadowOffset();
415         }
416         break;
417       }
418       case Toolkit::TextLabel::Property::SHADOW_COLOR:
419       {
420         if ( impl.mController )
421         {
422           value = impl.mController->GetShadowColor();
423         }
424         break;
425       }
426       case Toolkit::TextLabel::Property::UNDERLINE_COLOR:
427       {
428         if ( impl.mController )
429         {
430           value = impl.mController->GetUnderlineColor();
431         }
432         break;
433       }
434       case Toolkit::TextLabel::Property::UNDERLINE_ENABLED:
435       {
436         if ( impl.mController )
437         {
438           value = impl.mController->IsUnderlineEnabled();
439         }
440         break;
441       }
442       case Toolkit::TextLabel::Property::UNDERLINE_HEIGHT:
443       {
444         if ( impl.mController )
445         {
446           value = impl.mController->GetUnderlineHeight();
447         }
448         break;
449       }
450       case Toolkit::TextLabel::Property::ENABLE_MARKUP:
451       {
452         if( impl.mController )
453         {
454           value = impl.mController->IsMarkupProcessorEnabled();
455         }
456         break;
457       }
458     }
459   }
460
461   return value;
462 }
463
464 void TextLabel::OnInitialize()
465 {
466   Actor self = Self();
467
468   mController = Text::Controller::New( *this );
469
470   // When using the vector-based rendering, the size of the GLyphs are different
471   TextAbstraction::GlyphType glyphType = (Text::RENDERING_VECTOR_BASED == mRenderingBackend) ? TextAbstraction::VECTOR_GLYPH : TextAbstraction::BITMAP_GLYPH;
472   mController->SetGlyphType( glyphType );
473
474   // Use height-for-width negotiation by default
475   self.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::WIDTH );
476   self.SetResizePolicy( ResizePolicy::DIMENSION_DEPENDENCY, Dimension::HEIGHT );
477
478   // Enable the text ellipsis.
479   LayoutEngine& engine = mController->GetLayoutEngine();
480
481   engine.SetTextEllipsisEnabled( true );
482   engine.SetCursorWidth( 0u ); // Do not layout space for the cursor.
483
484   self.OnStageSignal().Connect( this, &TextLabel::OnStageConnect );
485 }
486
487 void TextLabel::OnStyleChange( Toolkit::StyleManager styleManager, StyleChange::Type change )
488 {
489   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextLabel::OnStyleChange\n");
490
491   switch ( change )
492   {
493     case StyleChange::DEFAULT_FONT_CHANGE:
494     {
495       // Property system did not set the font so should update it.
496       std::string newFont = styleManager.GetDefaultFontFamily();
497       DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::OnStyleChange StyleChange::DEFAULT_FONT_CHANGE newFont(%s)\n", newFont.c_str() );
498       mController->UpdateAfterFontChange( newFont );
499       break;
500     }
501
502     case StyleChange::DEFAULT_FONT_SIZE_CHANGE:
503     {
504       DALI_LOG_INFO( gLogFilter, Debug::General, "TextLabel::OnStyleChange StyleChange::DEFAULT_FONT_SIZE_CHANGE (%f)\n", mController->GetDefaultPointSize() );
505
506       if ( (mController->GetDefaultPointSize() <= 0.0f) ) // If DefaultPointSize not set by Property system it will be 0.0f
507       {
508         // Property system did not set the PointSize so should update it.
509         // todo instruct text-controller to update model
510       }
511       break;
512     }
513     case StyleChange::THEME_CHANGE:
514     {
515       GetImpl( styleManager ).ApplyThemeStyle( Toolkit::Control( GetOwner() ) );
516       break;
517     }
518   }
519 }
520
521 Vector3 TextLabel::GetNaturalSize()
522 {
523   return mController->GetNaturalSize();
524 }
525
526 float TextLabel::GetHeightForWidth( float width )
527 {
528   return mController->GetHeightForWidth( width );
529 }
530
531 void TextLabel::OnRelayout( const Vector2& size, RelayoutContainer& container )
532 {
533   if( mController->Relayout( size ) ||
534       !mRenderer )
535   {
536     if( !mRenderer )
537     {
538       mRenderer = Backend::Get().NewRenderer( mRenderingBackend );
539     }
540     RenderText();
541   }
542 }
543
544 void TextLabel::RequestTextRelayout()
545 {
546   RelayoutRequest();
547 }
548
549 void TextLabel::RenderText()
550 {
551   Actor self = Self();
552   Actor renderableActor;
553   if( mRenderer )
554   {
555     renderableActor = mRenderer->Render( mController->GetView(), DepthIndex::TEXT );
556   }
557
558   if( renderableActor != mRenderableActor )
559   {
560     UnparentAndReset( mRenderableActor );
561
562     if( renderableActor )
563     {
564       // TODO: Scroll and alignment needs to be refactored.
565       const Vector2& alignmentOffset = mController->GetAlignmentOffset();
566       renderableActor.SetPosition( 0.f, alignmentOffset.y );
567
568       self.Add( renderableActor );
569     }
570     mRenderableActor = renderableActor;
571   }
572 }
573
574 void TextLabel::OnStageConnect( Dali::Actor actor )
575 {
576   if ( mHasBeenStaged )
577   {
578     RenderText();
579   }
580   else
581   {
582     mHasBeenStaged = true;
583   }
584 }
585
586 void TextLabel::AddDecoration( Actor& actor, bool needsClipping )
587 {
588   // TextLabel does not show decorations
589 }
590
591 void TextLabel::OnStageConnection( int depth )
592 {
593   // Call the Control::OnStageConnection() to set the depth of the background.
594   Control::OnStageConnection( depth );
595
596   // The depth of the text renderer is set in the RenderText() called from OnRelayout().
597 }
598
599 void TextLabel::TextChanged()
600 {
601   // TextLabel does not provide a signal for this
602 }
603
604 void TextLabel::MaxLengthReached()
605 {
606   // Pure Virtual from TextController Interface, only needed when inputting text
607 }
608
609 TextLabel::TextLabel()
610 : Control( ControlBehaviour( REQUIRES_STYLE_CHANGE_SIGNALS ) ),
611   mRenderingBackend( DEFAULT_RENDERING_BACKEND ),
612   mHasBeenStaged( false )
613 {
614 }
615
616 TextLabel::~TextLabel()
617 {
618 }
619
620 } // namespace Internal
621
622 } // namespace Toolkit
623
624 } // namespace Dali