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