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