Resolve incorrect position for Ellipsis when mixed LTR & RTL languages and set layout...
[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 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   Property::Map result;
293   if(mThemeBuilder)
294   {
295     result = mThemeBuilder.GetConfigurations();
296   }
297   else
298   {
299     DALI_LOG_STREAM(gLogFilter, Debug::Concise, "GetConfigurations()  Loading default theme");
300
301     bool themeLoaded = false;
302
303     mThemeBuilder = CreateBuilder(mThemeBuilderConstants);
304
305     // Load default theme because this is first try to load stylesheet.
306     themeLoaded = LoadJSON(mThemeBuilder, mDefaultThemeFilePath);
307     mThemeFile  = mDefaultThemeFilePath;
308
309     if(themeLoaded)
310     {
311       result = mThemeBuilder.GetConfigurations();
312     }
313     DALI_LOG_STREAM(gLogFilter, Debug::Concise, "  themeLoaded" << (themeLoaded ? "success" : "failure"));
314   }
315
316   DALI_LOG_STREAM(gLogFilter, Debug::Concise, "GetConfigurations()\n On exit, result Count: " << (result.Count() != 0));
317   DALI_LOG_STREAM(gLogFilter, Debug::Verbose, "          result: " << result);
318
319   return result;
320 }
321
322 void StyleManager::SetBrokenImageUrl(DevelStyleManager::BrokenImageType brokenImageType, const std::string& brokenImageUrl)
323 {
324   int brokenType = static_cast<int>(brokenImageType);
325   mBrokenImageUrls[brokenType] = brokenImageUrl;
326   Toolkit::StyleManager styleManager = StyleManager::Get();
327   mBrokenImageChangedSignal.Emit(styleManager);
328 }
329
330 std::string StyleManager::GetBrokenImageUrl(DevelStyleManager::BrokenImageType brokenImageType)
331 {
332   int brokenType = static_cast<int>(brokenImageType);
333   return mBrokenImageUrls[brokenType];
334 }
335
336 std::vector<std::string> StyleManager::GetBrokenImageUrlList()
337 {
338   // create a list for brokenImage
339   std::vector<std::string> brokenImageUrlList;
340   for(int i = 0; i < COUNT_BROKEN_IMAGE_MAX; i++)
341   {
342     if(!mBrokenImageUrls[i].empty())
343     {
344       brokenImageUrlList.push_back(mBrokenImageUrls[i]);
345     }
346   }
347   return brokenImageUrlList;
348 }
349
350 bool StyleManager::LoadFile(const std::string& filename, std::string& stringOut)
351 {
352   DALI_ASSERT_DEBUG(0 != filename.length());
353
354   // as toolkit is platform agnostic, it cannot load files from filesystem
355   // ask style monitor to load the style sheet
356   if(mStyleMonitor)
357   {
358     return mStyleMonitor.LoadThemeFile(filename, stringOut);
359   }
360
361   return false;
362 }
363
364 Toolkit::Builder StyleManager::CreateBuilder(const Property::Map& constants)
365 {
366   Toolkit::Builder builder = Toolkit::Builder::New();
367   builder.AddConstants(constants);
368
369   return builder;
370 }
371
372 bool StyleManager::LoadJSON(Toolkit::Builder builder, const std::string& jsonFilePath)
373 {
374   std::string fileString;
375   if(LoadFile(jsonFilePath, fileString))
376   {
377     builder.LoadFromString(fileString);
378     return true;
379   }
380   else
381   {
382     DALI_LOG_WARNING("Error loading file '%s'\n", jsonFilePath.c_str());
383     return false;
384   }
385 }
386
387 static void CollectQualifiers(std::vector<std::string>& qualifiersOut)
388 {
389   // Append the relevant qualifier for orientation
390   // int orientation = 0; // Get the orientation from the system
391   /*
392   //// To Do /////
393   Getting orientation from the system, and determine Qualifie LANDSCAPE or PORTRAIT
394   orientation  0, 180 : PORTRAIT_QUALIFIER (default)
395   orientation 90, 270 : LANDSCAPE_QUALIFIER
396   */
397
398   qualifiersOut.push_back(std::string(PORTRAIT_QUALIFIER));
399 }
400
401 /**
402  * @brief Construct a qualified style name out of qualifiers
403  *
404  * A qualifed style name will be in the format: style-qualifier0-qualifier1-qualifierN
405  *
406  * @param[in] styleName The root name of the style
407  * @param[in] qualifiers List of qualifier names
408  * @param[out] qualifiedStyleOut The qualified style name
409  */
410 static void BuildQualifiedStyleName(
411   const std::string&              styleName,
412   const std::vector<std::string>& qualifiers,
413   std::string&                    qualifiedStyleOut)
414 {
415   qualifiedStyleOut.append(styleName);
416
417   for(std::vector<std::string>::const_iterator it    = qualifiers.begin(),
418                                                itEnd = qualifiers.end();
419       it != itEnd;
420       ++it)
421   {
422     const std::string& str = *it;
423
424     qualifiedStyleOut.append("-");
425     qualifiedStyleOut.append(str);
426   }
427 }
428
429 static bool GetStyleNameForControl(Toolkit::Builder builder, Toolkit::Control control, std::string& styleName)
430 {
431   styleName = control.GetStyleName();
432
433   if(styleName.empty())
434   {
435     styleName = control.GetTypeName();
436   }
437
438   // Apply the style after choosing the correct actual style (e.g. landscape or portrait)
439   std::vector<std::string> qualifiers;
440   CollectQualifiers(qualifiers);
441
442   bool        found = 0;
443   std::string qualifiedStyleName;
444   do
445   {
446     qualifiedStyleName.clear();
447     BuildQualifiedStyleName(styleName, qualifiers, qualifiedStyleName);
448
449     // Break if style found or we have tried the root style name (qualifiers is empty)
450     if(GetImpl(builder).LookupStyleName(qualifiedStyleName))
451     {
452       found = true;
453       break;
454     }
455     if(qualifiers.size() == 0)
456     {
457       break;
458     }
459     // Remove the last qualifier in an attempt to find a style that is valid
460     qualifiers.pop_back();
461   } while(!found);
462
463   if(found)
464   {
465     styleName = qualifiedStyleName;
466   }
467   return found;
468 }
469
470 void StyleManager::ApplyStyle(Toolkit::Builder builder, Toolkit::Control control)
471 {
472   std::string styleName = control.GetStyleName();
473   if(GetStyleNameForControl(builder, control, styleName))
474   {
475     builder.ApplyStyle(styleName, control);
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