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