CMake - Option added to define the default toolkit resource path.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / styling / style-manager-impl.cpp
1 /*
2  * Copyright (c) 2018 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/public-api/object/type-registry.h>
23 #include <dali/public-api/object/type-registry-helper.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/public-api/adaptor-framework/application.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/public-api/controls/control.h>
31 #include <dali-toolkit/public-api/controls/control-impl.h>
32 #include <dali-toolkit/public-api/styling/style-manager.h>
33 #include <dali-toolkit/internal/feedback/feedback-style.h>
34
35 namespace
36 {
37
38 //const char* LANDSCAPE_QUALIFIER = "landscape";
39 const char* PORTRAIT_QUALIFIER  = "portrait";
40 const char* FONT_SIZE_QUALIFIER = "fontsize";
41
42 const char* DEFAULT_THEME_FILE_NAME = "dali-toolkit-default-theme.json";
43
44 const char* PACKAGE_PATH_KEY = "PACKAGE_PATH";
45 const char* APPLICATION_RESOURCE_PATH_KEY = "APPLICATION_RESOURCE_PATH";
46
47 const char* DEFAULT_TOOLKIT_PACKAGE_PATH = "/toolkit/";
48
49 #if defined(DEBUG_ENABLED)
50 Debug::Filter* gLogFilter = Debug::Filter::New( Debug::NoLogging, false, "LOG_STYLE");
51 #endif
52
53 } // namespace
54
55 namespace Dali
56 {
57
58 namespace Toolkit
59 {
60
61 namespace Internal
62 {
63
64 namespace
65 {
66
67 BaseHandle Create()
68 {
69   BaseHandle handle = StyleManager::Get();
70
71   if ( !handle )
72   {
73     SingletonService singletonService( SingletonService::Get() );
74     if ( singletonService )
75     {
76       Toolkit::StyleManager manager = Toolkit::StyleManager( new Internal::StyleManager() );
77       singletonService.Register( typeid( manager ), manager );
78       handle = manager;
79     }
80   }
81
82   return handle;
83 }
84
85 DALI_TYPE_REGISTRATION_BEGIN_CREATE( Toolkit::StyleManager, Dali::BaseHandle, Create, true )
86 DALI_TYPE_REGISTRATION_END()
87
88 } // namespace
89
90 Toolkit::StyleManager StyleManager::Get()
91 {
92   Toolkit::StyleManager manager;
93
94   SingletonService singletonService( SingletonService::Get() );
95   if ( singletonService )
96   {
97     // Check whether the style manager is already created
98     Dali::BaseHandle handle = singletonService.GetSingleton( typeid( Toolkit::StyleManager ) );
99     if( handle )
100     {
101       // If so, downcast the handle of singleton
102       manager = Toolkit::StyleManager( dynamic_cast< StyleManager* >( handle.GetObjectPtr() ) );
103     }
104   }
105
106   return manager;
107 }
108
109 StyleManager::StyleManager()
110 : mDefaultFontSize( -1 ),
111   mDefaultFontFamily(""),
112   mDefaultThemeFilePath(),
113   mFeedbackStyle( nullptr )
114 {
115   // Add theme builder constants
116   const std::string dataReadOnlyDir = AssetManager::GetDaliDataReadOnlyPath();
117   mThemeBuilderConstants[ PACKAGE_PATH_KEY ] = dataReadOnlyDir + DEFAULT_TOOLKIT_PACKAGE_PATH;
118   mThemeBuilderConstants[ APPLICATION_RESOURCE_PATH_KEY ] = Application::GetResourcePath();
119
120   mStyleMonitor = StyleMonitor::Get();
121   if( mStyleMonitor )
122   {
123     mStyleMonitor.StyleChangeSignal().Connect( this, &StyleManager::StyleMonitorChange );
124     mDefaultFontSize = mStyleMonitor.GetDefaultFontSize();
125   }
126
127   // Set the full path for the default style theme.
128   const std::string styleDirPath = AssetManager::GetDaliStylePath();
129   mDefaultThemeFilePath = styleDirPath + DEFAULT_THEME_FILE_NAME;
130
131   // Sound & haptic style
132   mFeedbackStyle = new FeedbackStyle();
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 void StyleManager::SetTheme( const std::string& themeFile )
239 {
240   bool themeLoaded = false;
241   bool loading = false;
242
243   // If we haven't loaded a theme, or the stored theme file is empty, or
244   // the previously loaded theme is different to the requested theme,
245   // first reset the builder and load the default theme.
246   if( ! mThemeBuilder || mThemeFile.empty() || mThemeFile.compare( themeFile ) != 0 )
247   {
248     loading = true;
249     mThemeBuilder = CreateBuilder( mThemeBuilderConstants );
250     themeLoaded = LoadJSON( mThemeBuilder, mDefaultThemeFilePath ); // Sets themeLoaded to true if theme exists
251   }
252
253   if( themeFile.compare(mDefaultThemeFilePath) != 0 )
254   {
255     // The theme is different to the default: Merge it
256     loading = true;
257     themeLoaded |= LoadJSON( mThemeBuilder, themeFile );
258   }
259
260   if( loading )
261   {
262     mThemeFile = themeFile;
263
264     if( themeLoaded )
265     {
266       // We've successfully loaded the theme file
267       if(mFeedbackStyle)
268       {
269         mFeedbackStyle->StyleChanged( mThemeFile, StyleChange::THEME_CHANGE );
270       }
271
272       EmitStyleChangeSignals(StyleChange::THEME_CHANGE);
273     }
274     else
275     {
276       // We tried to load a theme, but it failed. Ensure the builder is reset
277       mThemeBuilder.Reset();
278       mThemeFile.clear();
279     }
280   }
281 }
282
283 const Property::Map StyleManager::GetConfigurations()
284 {
285   DALI_LOG_STREAM( gLogFilter, Debug::Concise, "GetConfigurations()\n On entry, mThemeBuilder: " << (bool(mThemeBuilder)?"Created":"Empty") << "  mThemeFile: " << mThemeFile);
286
287   Property::Map result;
288   if( mThemeBuilder )
289   {
290     result = mThemeBuilder.GetConfigurations();
291   }
292   else
293   {
294     DALI_LOG_STREAM( gLogFilter, Debug::Concise, "GetConfigurations()  Loading default theme" );
295
296     bool themeLoaded = false;
297
298     mThemeBuilder = CreateBuilder( mThemeBuilderConstants );
299
300     // Load default theme because this is first try to load stylesheet.
301     themeLoaded = LoadJSON( mThemeBuilder, mDefaultThemeFilePath );
302     mThemeFile = mDefaultThemeFilePath;
303
304     if( themeLoaded )
305     {
306       result = mThemeBuilder.GetConfigurations();
307     }
308     DALI_LOG_STREAM( gLogFilter, Debug::Concise, "  themeLoaded" << (themeLoaded?"success":"failure") );
309   }
310
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
314   return result;
315 }
316
317 bool StyleManager::LoadFile( const std::string& filename, std::string& stringOut )
318 {
319   DALI_ASSERT_DEBUG( 0 != filename.length());
320
321   // as toolkit is platform agnostic, it cannot load files from filesystem
322   // ask style monitor to load the style sheet
323   if( mStyleMonitor )
324   {
325     return mStyleMonitor.LoadThemeFile( filename, stringOut );
326   }
327
328   return false;
329 }
330
331 Toolkit::Builder StyleManager::CreateBuilder( const Property::Map& constants )
332 {
333   Toolkit::Builder builder = Toolkit::Builder::New();
334   builder.AddConstants( constants );
335
336   return builder;
337 }
338
339 bool StyleManager::LoadJSON( Toolkit::Builder builder, const std::string& jsonFilePath )
340 {
341   std::string fileString;
342   if( LoadFile( jsonFilePath, fileString ) )
343   {
344     builder.LoadFromString( fileString );
345     return true;
346   }
347   else
348   {
349     DALI_LOG_WARNING("Error loading file '%s'\n", jsonFilePath.c_str());
350     return false;
351   }
352 }
353
354 static void CollectQualifiers( std::vector<std::string>& qualifiersOut )
355 {
356   // Append the relevant qualifier for orientation
357   // int orientation = 0; // Get the orientation from the system
358   /*
359   //// To Do /////
360   Getting orientation from the system, and determine Qualifie LANDSCAPE or PORTRAIT
361   orientation  0, 180 : PORTRAIT_QUALIFIER (default)
362   orientation 90, 270 : LANDSCAPE_QUALIFIER
363   */
364
365   qualifiersOut.push_back( std::string( PORTRAIT_QUALIFIER ) );
366
367 }
368
369 /**
370  * @brief Construct a qualified style name out of qualifiers
371  *
372  * A qualifed style name will be in the format: style-qualifier0-qualifier1-qualifierN
373  *
374  * @param[in] styleName The root name of the style
375  * @param[in] qualifiers List of qualifier names
376  * @param[out] qualifiedStyleOut The qualified style name
377  */
378 static void BuildQualifiedStyleName(
379   const std::string& styleName,
380   const std::vector<std::string>& qualifiers,
381   std::string& qualifiedStyleOut )
382 {
383   qualifiedStyleOut.append( styleName );
384
385   for( std::vector<std::string>::const_iterator it = qualifiers.begin(),
386          itEnd = qualifiers.end(); it != itEnd; ++it )
387   {
388     const std::string& str = *it;
389
390     qualifiedStyleOut.append( "-" );
391     qualifiedStyleOut.append( str );
392   }
393 }
394
395 static bool GetStyleNameForControl( Toolkit::Builder builder, Toolkit::Control control, std::string& styleName)
396 {
397   styleName = control.GetStyleName();
398
399   if( styleName.empty() )
400   {
401     styleName = control.GetTypeName();
402   }
403
404   // Apply the style after choosing the correct actual style (e.g. landscape or portrait)
405   std::vector<std::string> qualifiers;
406   CollectQualifiers( qualifiers );
407
408   bool found = 0;
409   std::string qualifiedStyleName;
410   do
411   {
412     qualifiedStyleName.clear();
413     BuildQualifiedStyleName( styleName, qualifiers, qualifiedStyleName );
414
415     // Break if style found or we have tried the root style name (qualifiers is empty)
416     if( GetImpl(builder).LookupStyleName( qualifiedStyleName ) )
417     {
418       found = true;
419       break;
420     }
421     if( qualifiers.size() == 0 )
422     {
423       break;
424     }
425     // Remove the last qualifier in an attempt to find a style that is valid
426     qualifiers.pop_back();
427   } while (!found);
428
429   if(found)
430   {
431     styleName = qualifiedStyleName;
432   }
433   return found;
434 }
435
436 void StyleManager::ApplyStyle( Toolkit::Builder builder, Toolkit::Control control )
437 {
438   std::string styleName = control.GetStyleName();
439   if( GetStyleNameForControl( builder, control, styleName ) )
440   {
441     builder.ApplyStyle( styleName, control );
442   }
443
444   if( mDefaultFontSize >= 0 )
445   {
446     // Apply the style for logical font size
447     std::stringstream fontSizeQualifier;
448     fontSizeQualifier << styleName << FONT_SIZE_QUALIFIER << mDefaultFontSize;
449     builder.ApplyStyle( fontSizeQualifier.str(), control );
450   }
451 }
452
453 const StylePtr StyleManager::GetRecordedStyle( Toolkit::Control control )
454 {
455   if( mThemeBuilder )
456   {
457     std::string styleName = control.GetStyleName();
458
459     if( GetStyleNameForControl( mThemeBuilder, control, styleName ) )
460     {
461       const StylePtr style = GetImpl(mThemeBuilder).GetStyle( styleName );
462       return style;
463     }
464   }
465   return StylePtr(NULL);
466 }
467
468 Toolkit::Builder StyleManager::FindCachedBuilder( const std::string& key )
469 {
470   BuilderMap::iterator builderIt = mBuilderCache.find( key );
471   if( builderIt != mBuilderCache.end() )
472   {
473     return builderIt->second;
474   }
475
476   return Toolkit::Builder();
477 }
478
479 void StyleManager::CacheBuilder( Toolkit::Builder builder, const std::string& key )
480 {
481   mBuilderCache[ key ] = builder;
482 }
483
484 void StyleManager::StyleMonitorChange( StyleMonitor styleMonitor, StyleChange::Type styleChange )
485 {
486   switch ( styleChange )
487   {
488     case StyleChange::DEFAULT_FONT_CHANGE:
489     {
490       mDefaultFontFamily = styleMonitor.GetDefaultFontFamily();
491       break;
492     }
493
494     case StyleChange::DEFAULT_FONT_SIZE_CHANGE:
495     {
496       mDefaultFontSize = styleMonitor.GetDefaultFontSize();
497       break;
498     }
499
500     case StyleChange::THEME_CHANGE:
501     {
502       SetTheme( styleMonitor.GetTheme() );
503       break;
504     }
505   }
506   EmitStyleChangeSignals( styleChange );
507 }
508
509 void StyleManager::EmitStyleChangeSignals( StyleChange::Type styleChange )
510 {
511   Toolkit::StyleManager styleManager = StyleManager::Get();
512
513   // Update Controls first
514   mControlStyleChangeSignal.Emit( styleManager, styleChange );
515
516   // Inform application last
517   mStyleChangedSignal.Emit( styleManager, styleChange );
518 }
519
520
521 } // namespace Internal
522
523 } // namespace Toolkit
524
525 } // namespace Dali