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