Multiple text background color support for left-to-right text only in TextField
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-effects-style.cpp
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // FILE HEADER
19 #include <dali-toolkit/internal/text/text-effects-style.h>
20
21 // INTERNAL INCLUDES
22 #include <dali-toolkit/devel-api/controls/text-controls/text-style-properties-devel.h>
23 #include <dali-toolkit/internal/text/markup-processor-helper-functions.h>
24 #include <dali-toolkit/internal/text/property-string-parser.h>
25
26 namespace Dali
27 {
28
29 namespace Toolkit
30 {
31
32 namespace Text
33 {
34
35 namespace
36 {
37 const std::string COLOR_KEY( "color" );
38 const std::string OFFSET_KEY( "offset" );
39 const std::string BLUR_RADIUS_KEY( "blurRadius" );
40 const std::string WIDTH_KEY( "width" );
41 const std::string HEIGHT_KEY( "height" );
42 const std::string ENABLE_KEY( "enable" );
43 const std::string TRUE_TOKEN( "true" );
44 const std::string FALSE_TOKEN( "false" );
45 }
46
47 bool ParseShadowProperties( const Property::Map& shadowPropertiesMap,
48                             bool& colorDefined,
49                             Vector4& color,
50                             bool& offsetDefined,
51                             Vector2& offset,
52                             bool& blurRadiusDefined,
53                             float& blurRadius )
54 {
55   const unsigned int numberOfItems = shadowPropertiesMap.Count();
56
57   // Parses and applies the style.
58   for( unsigned int index = 0u; index < numberOfItems; ++index )
59   {
60     const KeyValuePair& valueGet = shadowPropertiesMap.GetKeyValue( index );
61
62     if( ( DevelText::Shadow::Property::COLOR == valueGet.first.indexKey ) || ( COLOR_KEY == valueGet.first.stringKey ) )
63     {
64       /// Color key.
65       colorDefined = true;
66
67       if( valueGet.second.GetType() == Dali::Property::STRING )
68       {
69         const std::string colorStr = valueGet.second.Get<std::string>();
70         Text::ColorStringToVector4( colorStr.c_str(), colorStr.size(), color );
71       }
72       else
73       {
74         color = valueGet.second.Get<Vector4>();
75       }
76     }
77     else if( ( DevelText::Shadow::Property::OFFSET == valueGet.first.indexKey ) || ( OFFSET_KEY == valueGet.first.stringKey ) )
78     {
79       /// Offset key.
80       offsetDefined = true;
81
82       if( valueGet.second.GetType() == Dali::Property::STRING )
83       {
84         const std::string offsetStr = valueGet.second.Get<std::string>();
85         StringToVector2( offsetStr.c_str(), offsetStr.size(), offset );
86       }
87       else
88       {
89         offset = valueGet.second.Get<Vector2>();
90       }
91     }
92     else if( ( DevelText::Shadow::Property::BLUR_RADIUS == valueGet.first.indexKey ) || ( BLUR_RADIUS_KEY == valueGet.first.stringKey ) )
93     {
94       /// Blur radius key.
95       blurRadiusDefined = true;
96
97       if( valueGet.second.GetType() == Dali::Property::STRING )
98       {
99         const std::string blurRadiusStr = valueGet.second.Get<std::string>();
100         blurRadius = StringToFloat( blurRadiusStr.c_str() );
101       }
102       else
103       {
104         blurRadius = valueGet.second.Get<float>();
105       }
106     }
107   }
108
109   return 0u == numberOfItems;
110 }
111
112 bool ParseUnderlineProperties( const Property::Map& underlinePropertiesMap,
113                                bool& enabled,
114                                bool& colorDefined,
115                                Vector4& color,
116                                bool& heightDefined,
117                                float& height )
118 {
119   const unsigned int numberOfItems = underlinePropertiesMap.Count();
120
121   // Parses and applies the style.
122   for( unsigned int index = 0u; index < numberOfItems; ++index )
123   {
124     const KeyValuePair& valueGet = underlinePropertiesMap.GetKeyValue( index );
125
126     if( ( DevelText::Underline::Property::ENABLE == valueGet.first.indexKey ) || ( ENABLE_KEY == valueGet.first.stringKey ) )
127     {
128       /// Enable key.
129       if( valueGet.second.GetType() == Dali::Property::STRING )
130       {
131         const std::string enableStr = valueGet.second.Get<std::string>();
132         enabled = Text::TokenComparison( TRUE_TOKEN, enableStr.c_str(), enableStr.size() );
133       }
134       else
135       {
136         enabled = valueGet.second.Get<bool>();
137       }
138     }
139     else if( ( DevelText::Underline::Property::COLOR == valueGet.first.indexKey ) || ( COLOR_KEY == valueGet.first.stringKey ) )
140     {
141       /// Color key.
142       colorDefined = true;
143
144       if( valueGet.second.GetType() == Dali::Property::STRING )
145       {
146         const std::string colorStr = valueGet.second.Get<std::string>();
147         Text::ColorStringToVector4( colorStr.c_str(), colorStr.size(), color );
148       }
149       else
150       {
151         color = valueGet.second.Get<Vector4>();
152       }
153     }
154     else if( ( DevelText::Underline::Property::HEIGHT == valueGet.first.indexKey ) || ( HEIGHT_KEY == valueGet.first.stringKey ) )
155     {
156       /// Height key.
157       heightDefined = true;
158
159       if( valueGet.second.GetType() == Dali::Property::STRING )
160       {
161         const std::string heightStr = valueGet.second.Get<std::string>();
162         height = StringToFloat( heightStr.c_str() );
163       }
164       else
165       {
166         height = valueGet.second.Get<float>();
167       }
168     }
169   }
170
171   return 0u == numberOfItems;
172 }
173
174 bool ParseOutlineProperties( const Property::Map& underlinePropertiesMap,
175                                bool& colorDefined,
176                                Vector4& color,
177                                bool& widthDefined,
178                                uint16_t& width )
179 {
180   const unsigned int numberOfItems = underlinePropertiesMap.Count();
181
182   // Parses and applies the style.
183   for( unsigned int index = 0u; index < numberOfItems; ++index )
184   {
185     const KeyValuePair& valueGet = underlinePropertiesMap.GetKeyValue( index );
186
187     if( ( DevelText::Outline::Property::COLOR == valueGet.first.indexKey ) || ( COLOR_KEY == valueGet.first.stringKey ) )
188     {
189       /// Color key.
190       colorDefined = true;
191       color = valueGet.second.Get<Vector4>();
192     }
193     else if( ( DevelText::Outline::Property::WIDTH == valueGet.first.indexKey ) || ( WIDTH_KEY == valueGet.first.stringKey ) )
194     {
195       /// Width key.
196       widthDefined = true;
197       width = static_cast<uint16_t>( valueGet.second.Get<float>() );
198     }
199   }
200
201   return 0u == numberOfItems;
202 }
203
204 bool ParseBackgroundProperties( const Property::Map& backgroundProperties,
205                                 bool& enabled,
206                                 bool& colorDefined,
207                                 Vector4& color )
208 {
209   const unsigned int numberOfItems = backgroundProperties.Count();
210
211   // Parses and applies the style.
212   for( unsigned int index = 0u; index < numberOfItems; ++index )
213   {
214     const KeyValuePair& valueGet = backgroundProperties.GetKeyValue( index );
215
216     if( ( DevelText::Background::Property::ENABLE == valueGet.first.indexKey ) || ( ENABLE_KEY == valueGet.first.stringKey ) )
217     {
218       /// Enable key.
219       enabled = valueGet.second.Get<bool>();
220     }
221     else if( ( DevelText::Background::Property::COLOR == valueGet.first.indexKey ) || ( COLOR_KEY == valueGet.first.stringKey ) )
222     {
223       /// Color key.
224       colorDefined = true;
225       color = valueGet.second.Get<Vector4>();
226     }
227   }
228
229   return 0u == numberOfItems;
230 }
231
232 bool SetUnderlineProperties( ControllerPtr controller, const Property::Value& value, EffectStyle::Type type )
233 {
234   bool update = false;
235
236   if( controller )
237   {
238     switch( type )
239     {
240       case EffectStyle::DEFAULT:
241       {
242         const Property::Map& propertiesMap = value.Get<Property::Map>();
243
244         bool enabled = false;
245         bool colorDefined = false;
246         Vector4 color;
247         bool heightDefined = false;
248         float height = 0.f;
249
250         bool empty = true;
251
252         if ( propertiesMap.Empty() )
253         {
254           // Map empty so check if a string provided
255           const std::string propertyString = value.Get<std::string>();
256
257           if ( !propertyString.empty() )
258           {
259             Property::Map parsedStringMap;
260             Text::ParsePropertyString( propertyString, parsedStringMap );
261
262             empty = ParseUnderlineProperties( parsedStringMap,
263                                               enabled,
264                                               colorDefined,
265                                               color,
266                                               heightDefined,
267                                               height );
268
269             controller->UnderlineSetByString( !empty );
270           }
271         }
272         else
273         {
274            empty = ParseUnderlineProperties( propertiesMap,
275                                              enabled,
276                                              colorDefined,
277                                              color,
278                                              heightDefined,
279                                              height );
280
281            controller->UnderlineSetByString( false );
282         }
283
284         if( !empty )
285         {
286           if( enabled != controller->IsUnderlineEnabled() )
287           {
288             controller->SetUnderlineEnabled( enabled );
289             update = true;
290           }
291
292           // Sets the default underline values.
293           if( colorDefined && ( controller->GetUnderlineColor() != color ) )
294           {
295             controller->SetUnderlineColor( color );
296             update = true;
297           }
298
299           if( heightDefined && ( fabsf( controller->GetUnderlineHeight() - height ) > Math::MACHINE_EPSILON_1000 ) )
300           {
301             controller->SetUnderlineHeight( height );
302             update = true;
303           }
304         }
305         else
306         {
307           // Disable underline.
308           if( controller->IsUnderlineEnabled() )
309           {
310             controller->SetUnderlineEnabled( false );
311             update = true;
312           }
313         }
314         break;
315       }
316       case EffectStyle::INPUT:
317       {
318         const std::string& underlineProperties = value.Get<std::string>();
319
320         controller->SetInputUnderlineProperties( underlineProperties );
321         break;
322       }
323     } // switch
324   } // if( controller )
325
326   return update;
327 }
328
329 void GetUnderlineProperties( ControllerPtr controller, Property::Value& value, EffectStyle::Type type )
330 {
331   if( controller )
332   {
333     switch( type )
334     {
335       case EffectStyle::DEFAULT:
336       {
337         const bool enabled = controller->IsUnderlineEnabled();
338         const Vector4& color = controller->GetUnderlineColor();
339         const float height = controller->GetUnderlineHeight();
340
341         if ( controller->IsUnderlineSetByString() )
342         {
343           std::string underlineProperties = "{\"enable\":";
344           const std::string enabledStr = enabled ? "true" : "false";
345           underlineProperties += "\"" + enabledStr + "\",";
346
347           std::string colorStr;
348           Vector4ToColorString( color, colorStr );
349           underlineProperties += "\"color\":\"" + colorStr + "\",";
350
351           std::string heightStr;
352           FloatToString( height, heightStr );
353           underlineProperties += "\"height\":\"" + heightStr + "\"}";
354
355           value = underlineProperties;
356         }
357         else
358         {
359           Property::Map map;
360
361           map.Insert( ENABLE_KEY, enabled );
362           map.Insert( COLOR_KEY, color );
363           map.Insert( HEIGHT_KEY, height );
364
365           value = map;
366         }
367
368         break;
369       }
370       case EffectStyle::INPUT:
371       {
372         value = controller->GetInputUnderlineProperties();
373         break;
374       }
375     }
376   }
377 }
378
379 bool SetShadowProperties( ControllerPtr controller, const Property::Value& value, EffectStyle::Type type )
380 {
381   bool update = false;
382
383   if( controller )
384   {
385     switch( type )
386     {
387       case EffectStyle::DEFAULT:
388       {
389         const Property::Map& propertiesMap = value.Get<Property::Map>();
390
391         bool colorDefined = false;
392         Vector4 color;
393         bool offsetDefined = false;
394         Vector2 offset;
395         bool blurRadiusDefined = false;
396         float blurRadius;
397
398         bool empty = true;
399
400         if ( propertiesMap.Empty() )
401         {
402            // Map empty so check if a string provided
403            const std::string propertyString = value.Get<std::string>();
404
405            Property::Map parsedStringMap;
406            Text::ParsePropertyString( propertyString, parsedStringMap );
407
408            empty = ParseShadowProperties( parsedStringMap,
409                                           colorDefined,
410                                           color,
411                                           offsetDefined,
412                                           offset,
413                                           blurRadiusDefined,
414                                           blurRadius );
415
416            controller->ShadowSetByString( !empty );
417
418         }
419         else
420         {
421           empty = ParseShadowProperties( propertiesMap,
422                                          colorDefined,
423                                          color,
424                                          offsetDefined,
425                                          offset,
426                                          blurRadiusDefined,
427                                          blurRadius );
428
429           controller->ShadowSetByString( false );
430         }
431
432         if( !empty )
433         {
434           // Sets the default shadow values.
435           if( colorDefined && ( controller->GetShadowColor() != color ) )
436           {
437             controller->SetShadowColor( color );
438             update = true;
439           }
440
441           if( offsetDefined && ( controller->GetShadowOffset() != offset ) )
442           {
443             controller->SetShadowOffset( offset );
444             update = true;
445           }
446
447           if( blurRadiusDefined && ( controller->GetShadowBlurRadius() != blurRadius ) )
448           {
449             controller->SetShadowBlurRadius( blurRadius );
450             update = true;
451           }
452         }
453         else
454         {
455           // Disable shadow.
456           if( Vector2::ZERO != controller->GetShadowOffset() )
457           {
458             controller->SetShadowOffset( Vector2::ZERO );
459           }
460         }
461         break;
462       }
463       case EffectStyle::INPUT:
464       {
465         const std::string& shadowString = value.Get<std::string>();
466
467         controller->SetInputShadowProperties( shadowString );
468         break;
469       }
470     } // switch
471   } // if( controller )
472
473   return update;
474 }
475
476 void GetShadowProperties( ControllerPtr controller, Property::Value& value, EffectStyle::Type type )
477 {
478   if( controller )
479   {
480     switch( type )
481     {
482       case EffectStyle::DEFAULT:
483       {
484         const Vector4& color = controller->GetShadowColor();
485         const Vector2& offset = controller->GetShadowOffset();
486         const float& blurRadius = controller->GetShadowBlurRadius();
487
488         if ( controller->IsShadowSetByString() )
489         {
490           std::string shadowProperties = "{";
491
492           std::string colorStr;
493           Vector4ToColorString( color, colorStr );
494           shadowProperties += "\"color\":\"" + colorStr + "\",";
495
496           std::string offsetStr;
497           Vector2ToString( offset, offsetStr );
498           shadowProperties += "\"offset\":\"" + offsetStr + "\",";
499
500           std::string blurRadiusStr;
501           FloatToString( blurRadius, blurRadiusStr );
502           shadowProperties += "\"blurRadius\":\"" + blurRadiusStr + "\"}";
503
504           value = shadowProperties;
505         }
506         else
507         {
508           Property::Map map;
509
510           map.Insert( COLOR_KEY, color );
511           map.Insert( OFFSET_KEY, offset );
512           map.Insert( BLUR_RADIUS_KEY, blurRadius );
513
514           value = map;
515         }
516         break;
517       }
518       case EffectStyle::INPUT:
519       {
520         value = controller->GetInputShadowProperties();
521         break;
522       }
523     }
524   }
525 }
526
527 bool SetEmbossProperties( ControllerPtr controller, const Property::Value& value, EffectStyle::Type type )
528 {
529   bool update = false;
530
531   if( controller )
532   {
533     const std::string properties = value.Get< std::string >();
534
535     switch( type )
536     {
537       case EffectStyle::DEFAULT:
538       {
539         // Stores the default emboss's properties string to be recovered by the GetEmbossProperties() function.
540         controller->SetDefaultEmbossProperties( properties );
541         break;
542       }
543       case EffectStyle::INPUT:
544       {
545         // Stores the input emboss's properties string to be recovered by the GetEmbossProperties() function.
546         controller->SetInputEmbossProperties( properties );
547         break;
548       }
549     }
550   }
551
552   return update;
553 }
554
555 void GetEmbossProperties( ControllerPtr controller, Property::Value& value, EffectStyle::Type type )
556 {
557   if( controller )
558   {
559     switch( type )
560     {
561       case EffectStyle::DEFAULT:
562       {
563         value = controller->GetDefaultEmbossProperties();
564         break;
565       }
566       case EffectStyle::INPUT:
567       {
568         value = controller->GetInputEmbossProperties();
569         break;
570       }
571     }
572   }
573 }
574
575 bool SetOutlineProperties( ControllerPtr controller, const Property::Value& value, EffectStyle::Type type )
576 {
577   bool update = false;
578
579   if( controller )
580   {
581     switch( type )
582     {
583       case EffectStyle::DEFAULT:
584       {
585         const Property::Map& propertiesMap = value.Get<Property::Map>();
586
587         bool colorDefined = false;
588         Vector4 color;
589         bool widthDefined = false;
590         uint16_t width = 0u;
591
592         bool empty = true;
593
594         if ( propertiesMap.Empty() )
595         {
596            // Map empty so check if a string provided
597            // This is purely to maintain backward compatibility, but we don't parse the string to be a property map.
598            const std::string propertyString = value.Get<std::string>();
599
600            // Stores the default outline's properties string to be recovered by the GetOutlineProperties() function.
601            controller->SetDefaultOutlineProperties( propertyString );
602
603            controller->OutlineSetByString( true );
604         }
605         else
606         {
607            empty = ParseOutlineProperties( propertiesMap,
608                                            colorDefined,
609                                            color,
610                                            widthDefined,
611                                            width );
612
613            controller->OutlineSetByString( false );
614         }
615
616         if( !empty )
617         {
618           // Sets the default outline values.
619           if( colorDefined && ( controller->GetOutlineColor() != color ) )
620           {
621             controller->SetOutlineColor( color );
622             update = true;
623           }
624
625           if( widthDefined && ( controller->GetOutlineWidth() != width ) )
626           {
627             controller->SetOutlineWidth( width );
628             update = true;
629           }
630         }
631         else
632         {
633           // Disable outline
634           if( 0u != controller->GetOutlineWidth() )
635           {
636             controller->SetOutlineWidth( 0u );
637             update = true;
638           }
639         }
640         break;
641       }
642       case EffectStyle::INPUT:
643       {
644         const std::string& outlineProperties = value.Get<std::string>();
645
646         controller->SetInputOutlineProperties( outlineProperties );
647         break;
648       }
649     } // switch
650   } // if( controller )
651
652   return update;
653 }
654
655 void GetOutlineProperties( ControllerPtr controller, Property::Value& value, EffectStyle::Type type )
656 {
657   if( controller )
658   {
659     switch( type )
660     {
661       case EffectStyle::DEFAULT:
662       {
663         if ( controller->IsOutlineSetByString() )
664         {
665           value = controller->GetDefaultOutlineProperties();
666           break;
667         }
668         else
669         {
670           const Vector4& color = controller->GetOutlineColor();
671           const uint16_t width = controller->GetOutlineWidth();
672
673           Property::Map map;
674           map.Insert( COLOR_KEY, color );
675           map.Insert( WIDTH_KEY, static_cast<int>( width ) );
676
677           value = map;
678
679           break;
680         }
681       }
682       case EffectStyle::INPUT:
683       {
684         value = controller->GetInputOutlineProperties();
685         break;
686       }
687     }
688   }
689 }
690
691 bool SetBackgroundProperties( ControllerPtr controller, const Property::Value& value, EffectStyle::Type type )
692 {
693   bool update = false;
694
695   if( controller )
696   {
697     switch( type )
698     {
699       case EffectStyle::DEFAULT:
700       {
701         const Property::Map& propertiesMap = value.Get<Property::Map>();
702
703         bool enabled = false;
704         bool colorDefined = false;
705         Vector4 color;
706
707         bool empty = true;
708
709         if ( !propertiesMap.Empty() )
710         {
711            empty = ParseBackgroundProperties( propertiesMap,
712                                               enabled,
713                                               colorDefined,
714                                               color );
715         }
716
717         if( !empty )
718         {
719           if( enabled != controller->IsBackgroundEnabled() )
720           {
721             controller->SetBackgroundEnabled( enabled );
722             update = true;
723           }
724
725           if( colorDefined && ( controller->GetBackgroundColor() != color ) )
726           {
727             controller->SetBackgroundColor( color );
728             update = true;
729           }
730         }
731         else
732         {
733           // Disable background.
734           if( controller->IsBackgroundEnabled() )
735           {
736             controller->SetBackgroundEnabled( false );
737             update = true;
738           }
739         }
740         break;
741       }
742       case EffectStyle::INPUT:
743       {
744         // Text background is not supported while inputting yet
745         break;
746       }
747     } // switch
748   } // if( controller )
749
750   return update;
751 }
752
753 void GetBackgroundProperties( ControllerPtr controller, Property::Value& value, EffectStyle::Type type )
754 {
755   if( controller )
756   {
757     switch( type )
758     {
759       case EffectStyle::DEFAULT:
760       {
761         const bool enabled = controller->IsBackgroundEnabled();
762         const Vector4& color = controller->GetBackgroundColor();
763
764         Property::Map map;
765         map.Insert( ENABLE_KEY, enabled );
766         map.Insert( COLOR_KEY, color );
767
768         value = map;
769
770         break;
771
772       }
773       case EffectStyle::INPUT:
774       {
775         // Text background is not supported while inputting yet
776         break;
777       }
778     }
779   }
780 }
781
782
783 } // namespace Text
784
785 } // namespace Toolkit
786
787 } // namespace Dali