Minor reduce textlabel creation time.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / styling / style-manager-impl.cpp
1 /*
2  * Copyright (c) 2022 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 // CLASS HEADER
18 #include "style-manager-impl.h"
19
20 // EXTERNAL INCLUDES
21 #include <dali/devel-api/common/singleton-service.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/adaptor-framework/application.h>
24 #include <dali/public-api/object/type-registry-helper.h>
25 #include <dali/public-api/object/type-registry.h>
26
27 // INTERNAL INCLUDES
28 #include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
29 #include <dali-toolkit/internal/builder/builder-impl.h>
30 #include <dali-toolkit/internal/feedback/feedback-style.h>
31 #include <dali-toolkit/public-api/controls/control-impl.h>
32 #include <dali-toolkit/public-api/controls/control.h>
33 #include <dali-toolkit/public-api/styling/style-manager.h>
34
35 namespace
36 {
37 //const char* LANDSCAPE_QUALIFIER = "landscape";
38 const char* PORTRAIT_QUALIFIER  = "portrait";
39 const char* FONT_SIZE_QUALIFIER = "fontsize";
40
41 const char* DEFAULT_THEME_FILE_NAME = "dali-toolkit-default-theme.json";
42
43 const char* PACKAGE_PATH_KEY              = "PACKAGE_PATH";
44 const char* APPLICATION_RESOURCE_PATH_KEY = "APPLICATION_RESOURCE_PATH";
45
46 const char* DEFAULT_TOOLKIT_PACKAGE_PATH = "/toolkit/";
47
48 static constexpr int32_t COUNT_BROKEN_IMAGE_MAX = 3;
49
50 #if defined(DEBUG_ENABLED)
51 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_STYLE");
52 #endif
53
54 } // namespace
55
56 namespace Dali
57 {
58 namespace Toolkit
59 {
60 namespace Internal
61 {
62 namespace
63 {
64 BaseHandle Create()
65 {
66   BaseHandle handle = StyleManager::Get();
67
68   if(!handle)
69   {
70     SingletonService singletonService(SingletonService::Get());
71     if(singletonService)
72     {
73       Toolkit::StyleManager manager = Toolkit::StyleManager(new Internal::StyleManager());
74       singletonService.Register(typeid(manager), manager);
75       handle = manager;
76     }
77   }
78
79   return handle;
80 }
81
82 DALI_TYPE_REGISTRATION_BEGIN_CREATE(Toolkit::StyleManager, Dali::BaseHandle, Create, true)
83 DALI_TYPE_REGISTRATION_END()
84
85 } // namespace
86
87 Toolkit::StyleManager StyleManager::Get()
88 {
89   Toolkit::StyleManager manager;
90
91   SingletonService singletonService(SingletonService::Get());
92   if(singletonService)
93   {
94     // Check whether the style manager is already created
95     Dali::BaseHandle handle = singletonService.GetSingleton(typeid(Toolkit::StyleManager));
96     if(handle)
97     {
98       // If so, downcast the handle of singleton
99       manager = Toolkit::StyleManager(dynamic_cast<StyleManager*>(handle.GetObjectPtr()));
100     }
101   }
102
103   return manager;
104 }
105
106 StyleManager::StyleManager()
107 : mDefaultFontSize(-1),
108   mDefaultFontFamily(""),
109   mDefaultThemeFilePath(),
110   mFeedbackStyle(nullptr)
111 {
112   // Add theme builder constants
113   const std::string dataReadOnlyDir                     = AssetManager::GetDaliDataReadOnlyPath();
114   mThemeBuilderConstants[PACKAGE_PATH_KEY]              = dataReadOnlyDir + DEFAULT_TOOLKIT_PACKAGE_PATH;
115   mThemeBuilderConstants[APPLICATION_RESOURCE_PATH_KEY] = Application::GetResourcePath();
116
117   mStyleMonitor = StyleMonitor::Get();
118   if(mStyleMonitor)
119   {
120     mStyleMonitor.StyleChangeSignal().Connect(this, &StyleManager::StyleMonitorChange);
121     mDefaultFontSize = mStyleMonitor.GetDefaultFontSize();
122   }
123
124   // Set the full path for the default style theme.
125   const std::string styleDirPath = AssetManager::GetDaliStylePath();
126   mDefaultThemeFilePath          = styleDirPath + DEFAULT_THEME_FILE_NAME;
127
128   // Sound & haptic style
129   mFeedbackStyle = new FeedbackStyle();
130
131   // Initialize BrokenImages
132   mBrokenImageUrls.assign(COUNT_BROKEN_IMAGE_MAX, "");
133 }
134
135 StyleManager::~StyleManager()
136 {
137   delete mFeedbackStyle;
138 }
139
140 void StyleManager::ApplyTheme(const std::string& themeFile)
141 {
142   SetTheme(themeFile);
143 }
144
145 void StyleManager::ApplyDefaultTheme()
146 {
147   SetTheme(mDefaultThemeFilePath);
148 }
149
150 const std::string& StyleManager::GetDefaultFontFamily() const
151 {
152   return mDefaultFontFamily;
153 }
154
155 void StyleManager::SetStyleConstant(const std::string& key, const Property::Value& value)
156 {
157   mStyleBuilderConstants[key] = value;
158 }
159
160 bool StyleManager::GetStyleConstant(const std::string& key, Property::Value& valueOut)
161 {
162   Property::Value* value = mStyleBuilderConstants.Find(key);
163   if(value)
164   {
165     valueOut = *value;
166     return true;
167   }
168
169   return false;
170 }
171
172 void StyleManager::ApplyThemeStyle(Toolkit::Control control)
173 {
174   if(!mThemeBuilder)
175   {
176     ApplyDefaultTheme();
177   }
178
179   if(mThemeBuilder)
180   {
181     ApplyStyle(mThemeBuilder, control);
182   }
183 }
184
185 void StyleManager::ApplyThemeStyleAtInit(Toolkit::Control control)
186 {
187   ApplyThemeStyle(control);
188
189   if(mFeedbackStyle)
190   {
191     mFeedbackStyle->ObjectCreated(control);
192   }
193 }
194
195 void StyleManager::ApplyStyle(Toolkit::Control control, const std::string& jsonFileName, const std::string& styleName)
196 {
197   bool builderReady = false;
198
199   // First look in the cache
200   Toolkit::Builder builder = FindCachedBuilder(jsonFileName);
201   if(builder)
202   {
203     builderReady = true;
204   }
205   else
206   {
207     // Merge theme and style constants
208     Property::Map constants(mThemeBuilderConstants);
209     constants.Merge(mStyleBuilderConstants);
210
211     // Create it
212     builder = CreateBuilder(constants);
213
214     if(LoadJSON(builder, jsonFileName))
215     {
216       CacheBuilder(builder, jsonFileName);
217       builderReady = true;
218     }
219   }
220
221   // Apply the style to the control
222   if(builderReady)
223   {
224     builder.ApplyStyle(styleName, control);
225   }
226 }
227
228 Toolkit::StyleManager::StyleChangedSignalType& StyleManager::StyleChangedSignal()
229 {
230   return mStyleChangedSignal;
231 }
232
233 Toolkit::StyleManager::StyleChangedSignalType& StyleManager::ControlStyleChangeSignal()
234 {
235   return mControlStyleChangeSignal;
236 }
237
238 Toolkit::DevelStyleManager::BrokenImageChangedSignalType& StyleManager::BrokenImageChangedSignal()
239 {
240   return mBrokenImageChangedSignal;
241 }
242
243 void StyleManager::SetTheme(const std::string& themeFile)
244 {
245   bool themeLoaded = false;
246   bool loading     = false;
247
248   // If we haven't loaded a theme, or the stored theme file is empty, or
249   // the previously loaded theme is different to the requested theme,
250   // first reset the builder and load the default theme.
251   if(!mThemeBuilder || mThemeFile.empty() || mThemeFile.compare(themeFile) != 0)
252   {
253     loading       = true;
254     mThemeBuilder = CreateBuilder(mThemeBuilderConstants);
255     themeLoaded   = LoadJSON(mThemeBuilder, mDefaultThemeFilePath); // Sets themeLoaded to true if theme exists
256   }
257
258   if(themeFile.compare(mDefaultThemeFilePath) != 0)
259   {
260     // The theme is different to the default: Merge it
261     loading = true;
262     themeLoaded |= LoadJSON(mThemeBuilder, themeFile);
263   }
264
265   if(loading)
266   {
267     mThemeFile = themeFile;
268
269     if(themeLoaded)
270     {
271       // We've successfully loaded the theme file
272       if(mFeedbackStyle)
273       {
274         mFeedbackStyle->StyleChanged(mThemeFile, StyleChange::THEME_CHANGE);
275       }
276
277       EmitStyleChangeSignals(StyleChange::THEME_CHANGE);
278     }
279     else
280     {
281       // We tried to load a theme, but it failed. Ensure the builder is reset
282       mThemeBuilder.Reset();
283       mThemeFile.clear();
284     }
285   }
286 }
287
288 const Property::Map& StyleManager::GetConfigurations()
289 {
290   DALI_LOG_STREAM(gLogFilter, Debug::Concise, "GetConfigurations()\n On entry, mThemeBuilder: " << (bool(mThemeBuilder) ? "Created" : "Empty") << "  mThemeFile: " << mThemeFile);
291
292   if(!mThemeBuilder)
293   {
294     DALI_LOG_STREAM(gLogFilter, Debug::Concise, "GetConfigurations()  Loading default theme");
295
296     mThemeBuilder = CreateBuilder(mThemeBuilderConstants);
297
298     // Load default theme because this is first try to load stylesheet.
299 #if defined(DEBUG_ENABLED)
300     bool themeLoaded = LoadJSON(mThemeBuilder, mDefaultThemeFilePath);
301     DALI_LOG_STREAM(gLogFilter, Debug::Concise, "  themeLoaded" << (themeLoaded ? "success" : "failure"));
302 #else
303     LoadJSON(mThemeBuilder, mDefaultThemeFilePath);
304 #endif
305
306     mThemeFile = mDefaultThemeFilePath;
307   }
308
309 #if defined(DEBUG_ENABLED)
310   Property::Map result = mThemeBuilder.GetConfigurations();
311   DALI_LOG_STREAM(gLogFilter, Debug::Concise, "GetConfigurations()\n On exit, result Count: " << (result.Count() != 0));
312   DALI_LOG_STREAM(gLogFilter, Debug::Verbose, "          result: " << result);
313 #endif
314
315   return mThemeBuilder.GetConfigurations();
316 }
317
318 void StyleManager::SetBrokenImageUrl(DevelStyleManager::BrokenImageType brokenImageType, const std::string& brokenImageUrl)
319 {
320   int brokenType                     = static_cast<int>(brokenImageType);
321   mBrokenImageUrls[brokenType]       = brokenImageUrl;
322   Toolkit::StyleManager styleManager = StyleManager::Get();
323   mBrokenImageChangedSignal.Emit(styleManager);
324 }
325
326 std::string StyleManager::GetBrokenImageUrl(DevelStyleManager::BrokenImageType brokenImageType)
327 {
328   int brokenType = static_cast<int>(brokenImageType);
329   return mBrokenImageUrls[brokenType];
330 }
331
332 std::vector<std::string> StyleManager::GetBrokenImageUrlList()
333 {
334   // create a list for brokenImage
335   std::vector<std::string> brokenImageUrlList;
336   for(int i = 0; i < COUNT_BROKEN_IMAGE_MAX; i++)
337   {
338     if(!mBrokenImageUrls[i].empty())
339     {
340       brokenImageUrlList.push_back(mBrokenImageUrls[i]);
341     }
342   }
343   return brokenImageUrlList;
344 }
345
346 bool StyleManager::LoadFile(const std::string& filename, std::string& stringOut)
347 {
348   DALI_ASSERT_DEBUG(0 != filename.length());
349
350   // as toolkit is platform agnostic, it cannot load files from filesystem
351   // ask style monitor to load the style sheet
352   if(mStyleMonitor)
353   {
354     return mStyleMonitor.LoadThemeFile(filename, stringOut);
355   }
356
357   return false;
358 }
359
360 Toolkit::Builder StyleManager::CreateBuilder(const Property::Map& constants)
361 {
362   Toolkit::Builder builder = Toolkit::Builder::New();
363   builder.AddConstants(constants);
364
365   return builder;
366 }
367
368 bool StyleManager::LoadJSON(Toolkit::Builder builder, const std::string& jsonFilePath)
369 {
370   std::string fileString;
371   if(LoadFile(jsonFilePath, fileString))
372   {
373     builder.LoadFromString(fileString);
374     return true;
375   }
376   else
377   {
378     DALI_LOG_WARNING("Error loading file '%s'\n", jsonFilePath.c_str());
379     return false;
380   }
381 }
382
383 static void CollectQualifiers(std::vector<std::string>& qualifiersOut)
384 {
385   // Append the relevant qualifier for orientation
386   // int orientation = 0; // Get the orientation from the system
387   /*
388   //// To Do /////
389   Getting orientation from the system, and determine Qualifie LANDSCAPE or PORTRAIT
390   orientation  0, 180 : PORTRAIT_QUALIFIER (default)
391   orientation 90, 270 : LANDSCAPE_QUALIFIER
392   */
393
394   qualifiersOut.push_back(std::string(PORTRAIT_QUALIFIER));
395 }
396
397 /**
398  * @brief Construct a qualified style name out of qualifiers
399  *
400  * A qualifed style name will be in the format: style-qualifier0-qualifier1-qualifierN
401  *
402  * @param[in] styleName The root name of the style
403  * @param[in] qualifiers List of qualifier names
404  * @param[out] qualifiedStyleOut The qualified style name
405  */
406 static void BuildQualifiedStyleName(
407   const std::string&              styleName,
408   const std::vector<std::string>& qualifiers,
409   std::string&                    qualifiedStyleOut)
410 {
411   qualifiedStyleOut.append(styleName);
412
413   for(std::vector<std::string>::const_iterator it    = qualifiers.begin(),
414                                                itEnd = qualifiers.end();
415       it != itEnd;
416       ++it)
417   {
418     const std::string& str = *it;
419
420     qualifiedStyleOut.append("-");
421     qualifiedStyleOut.append(str);
422   }
423 }
424
425 static bool GetStyleNameForControl(Toolkit::Builder builder, Toolkit::Control control, std::string& styleName)
426 {
427   styleName = control.GetStyleName();
428
429   if(styleName.empty())
430   {
431     styleName = control.GetTypeName();
432   }
433
434   // Apply the style after choosing the correct actual style (e.g. landscape or portrait)
435   std::vector<std::string> qualifiers;
436   CollectQualifiers(qualifiers);
437
438   bool        found = 0;
439   std::string qualifiedStyleName;
440   do
441   {
442     qualifiedStyleName.clear();
443     BuildQualifiedStyleName(styleName, qualifiers, qualifiedStyleName);
444
445     // Break if style found or we have tried the root style name (qualifiers is empty)
446     if(GetImpl(builder).LookupStyleName(qualifiedStyleName))
447     {
448       found = true;
449       break;
450     }
451     if(qualifiers.size() == 0)
452     {
453       break;
454     }
455     // Remove the last qualifier in an attempt to find a style that is valid
456     qualifiers.pop_back();
457   } while(!found);
458
459   if(found)
460   {
461     styleName = qualifiedStyleName;
462   }
463   return found;
464 }
465
466 void StyleManager::ApplyStyle(Toolkit::Builder builder, Toolkit::Control control)
467 {
468   std::string styleName = control.GetStyleName();
469   if(GetStyleNameForControl(builder, control, styleName))
470   {
471     builder.ApplyStyle(styleName, control);
472   }
473
474   if(mDefaultFontSize >= 0)
475   {
476     // Apply the style for logical font size
477     std::stringstream fontSizeQualifier;
478     fontSizeQualifier << styleName << FONT_SIZE_QUALIFIER << mDefaultFontSize;
479     builder.ApplyStyle(fontSizeQualifier.str(), control);
480   }
481 }
482
483 const StylePtr StyleManager::GetRecordedStyle(Toolkit::Control control)
484 {
485   if(mThemeBuilder)
486   {
487     std::string styleName = control.GetStyleName();
488
489     if(GetStyleNameForControl(mThemeBuilder, control, styleName))
490     {
491       const StylePtr style = GetImpl(mThemeBuilder).GetStyle(styleName);
492       return style;
493     }
494   }
495   return StylePtr(NULL);
496 }
497
498 Toolkit::Builder StyleManager::FindCachedBuilder(const std::string& key)
499 {
500   BuilderMap::iterator builderIt = mBuilderCache.find(key);
501   if(builderIt != mBuilderCache.end())
502   {
503     return builderIt->second;
504   }
505
506   return Toolkit::Builder();
507 }
508
509 void StyleManager::CacheBuilder(Toolkit::Builder builder, const std::string& key)
510 {
511   mBuilderCache[key] = builder;
512 }
513
514 void StyleManager::StyleMonitorChange(StyleMonitor styleMonitor, StyleChange::Type styleChange)
515 {
516   switch(styleChange)
517   {
518     case StyleChange::DEFAULT_FONT_CHANGE:
519     {
520       mDefaultFontFamily = styleMonitor.GetDefaultFontFamily();
521       break;
522     }
523
524     case StyleChange::DEFAULT_FONT_SIZE_CHANGE:
525     {
526       mDefaultFontSize = styleMonitor.GetDefaultFontSize();
527       break;
528     }
529
530     case StyleChange::THEME_CHANGE:
531     {
532       SetTheme(styleMonitor.GetTheme());
533       break;
534     }
535   }
536   EmitStyleChangeSignals(styleChange);
537 }
538
539 void StyleManager::EmitStyleChangeSignals(StyleChange::Type styleChange)
540 {
541   Toolkit::StyleManager styleManager = StyleManager::Get();
542
543   // Update Controls first
544   mControlStyleChangeSignal.Emit(styleManager, styleChange);
545
546   // Inform application last
547   mStyleChangedSignal.Emit(styleManager, styleChange);
548 }
549
550 } // namespace Internal
551
552 } // namespace Toolkit
553
554 } // namespace Dali