5178020ddd838ff6681c3b9ccf756d09372e3d29
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / styling / style-manager-impl.cpp
1 /*
2  * Copyright (c) 2014 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/devel-api/object/type-registry-helper.h>
24 #include <dali/integration-api/debug.h>
25
26 // INTERNAL INCLUDES
27 #include <dali-toolkit/public-api/controls/control.h>
28 #include <dali-toolkit/public-api/controls/control-impl.h>
29 #include <dali-toolkit/devel-api/styling/style-manager.h>
30 #include <dali-toolkit/internal/feedback/feedback-style.h>
31
32 namespace
33 {
34
35 const char* LANDSCAPE_QUALIFIER = "landscape";
36 const char* PORTRAIT_QUALIFIER  = "portrait";
37 const char* FONT_SIZE_QUALIFIER = "FontSize";
38
39 const char* DEFAULT_THEME = DALI_STYLE_DIR "dali-toolkit-default-theme.json";
40
41 const char* PACKAGE_PATH_KEY = "PACKAGE_PATH";
42 const char* DEFAULT_PACKAGE_PATH = DALI_DATA_READ_ONLY_DIR "/toolkit/";
43
44 } // namespace
45
46 namespace Dali
47 {
48
49 namespace Toolkit
50 {
51
52 namespace Internal
53 {
54
55 namespace
56 {
57
58 BaseHandle Create()
59 {
60   BaseHandle handle = StyleManager::Get();
61
62   if ( !handle )
63   {
64     SingletonService singletonService( SingletonService::Get() );
65     if ( singletonService )
66     {
67       Toolkit::StyleManager manager = Toolkit::StyleManager( new Internal::StyleManager() );
68       singletonService.Register( typeid( manager ), manager );
69       handle = manager;
70     }
71   }
72
73   return handle;
74 }
75
76 DALI_TYPE_REGISTRATION_BEGIN_CREATE( Toolkit::StyleManager, Dali::BaseHandle, Create, true )
77 DALI_TYPE_REGISTRATION_END()
78
79 } // namespace
80
81 Toolkit::StyleManager StyleManager::Get()
82 {
83   Toolkit::StyleManager manager;
84
85   SingletonService singletonService( SingletonService::Get() );
86   if ( singletonService )
87   {
88     // Check whether the style manager is already created
89     Dali::BaseHandle handle = singletonService.GetSingleton( typeid( Toolkit::StyleManager ) );
90     if( handle )
91     {
92       // If so, downcast the handle of singleton
93       manager = Toolkit::StyleManager( dynamic_cast< StyleManager* >( handle.GetObjectPtr() ) );
94     }
95   }
96
97   return manager;
98 }
99
100 StyleManager::StyleManager()
101 : mOrientationDegrees( 0 ),  // Portrait
102   mDefaultFontSize( -1 ),
103   mDefaultFontFamily(""),
104   mThemeFile( DEFAULT_THEME ),
105   mFeedbackStyle( NULL )
106 {
107   // Add theme builder constants
108   mThemeBuilderConstants[ PACKAGE_PATH_KEY ] = DEFAULT_PACKAGE_PATH;
109
110   mStyleMonitor = StyleMonitor::Get();
111   if( mStyleMonitor )
112   {
113     mStyleMonitor.StyleChangeSignal().Connect( this, &StyleManager::StyleMonitorChange );
114
115     mDefaultFontSize = mStyleMonitor.GetDefaultFontSize();
116   }
117
118   // Sound & haptic style
119   mFeedbackStyle = new FeedbackStyle();
120 }
121
122 StyleManager::~StyleManager()
123 {
124   delete mFeedbackStyle;
125 }
126
127 void StyleManager::SetOrientationValue( int orientation )
128 {
129   if( orientation !=  mOrientationDegrees )
130   {
131     mOrientationDegrees = orientation;
132     // TODO: if orientation changed, apply the new style to all controls
133     // dont want to really do the whole load from file again if the bundle contains both portrait & landscape
134     SetTheme();
135   }
136 }
137
138 int StyleManager::GetOrientationValue()
139 {
140   return mOrientationDegrees;
141 }
142
143 void StyleManager::SetOrientation( Orientation orientation )
144 {
145   if( mOrientation )
146   {
147     mOrientation.ChangedSignal().Disconnect( this, &StyleManager::OnOrientationChanged );
148   }
149
150   OnOrientationChanged( orientation );
151
152   if( mOrientation )
153   {
154     mOrientation.ChangedSignal().Connect( this, &StyleManager::OnOrientationChanged );
155   }
156 }
157
158 Orientation StyleManager::GetOrientation()
159 {
160   return mOrientation;
161 }
162
163 std::string StyleManager::GetDefaultFontFamily() const
164 {
165   return mDefaultFontFamily;
166 }
167
168 void StyleManager::SetStyleConstant( const std::string& key, const Property::Value& value )
169 {
170   mStyleBuilderConstants[ key ] = value;
171 }
172
173 bool StyleManager::GetStyleConstant( const std::string& key, Property::Value& valueOut )
174 {
175   Property::Value* value = mStyleBuilderConstants.Find( key );
176   if( value )
177   {
178     valueOut = *value;
179     return true;
180   }
181
182   return false;
183 }
184
185 void StyleManager::RequestThemeChange( const std::string& themeFile )
186 {
187   mThemeFile = themeFile;
188
189   // need to do style change synchronously as app might create a UI control on the next line
190   SetTheme();
191 }
192
193 void StyleManager::RequestDefaultTheme()
194 {
195   RequestThemeChange( DEFAULT_THEME );
196 }
197
198 void StyleManager::ApplyThemeStyle( Toolkit::Control control )
199 {
200   if( !mThemeBuilder )
201   {
202     RequestDefaultTheme();
203   }
204
205   if( mThemeBuilder )
206   {
207     ApplyStyle( mThemeBuilder, control );
208   }
209 }
210
211 void StyleManager::ApplyThemeStyleAtInit( Toolkit::Control control )
212 {
213   ApplyThemeStyle( control );
214
215   if(mFeedbackStyle)
216   {
217     mFeedbackStyle->ObjectCreated( control );
218   }
219 }
220
221 void StyleManager::ApplyStyle( Toolkit::Control control, const std::string& jsonFileName, const std::string& styleName )
222 {
223   bool builderReady = false;
224
225   // First look in the cache
226   Toolkit::Builder builder = FindCachedBuilder( jsonFileName );
227   if( builder )
228   {
229     builderReady = true;
230   }
231   else
232   {
233     // Merge theme and style constants
234     Property::Map constants( mThemeBuilderConstants );
235     constants.Merge( mStyleBuilderConstants );
236
237     // Create it
238     builder = CreateBuilder( constants );
239
240     if( LoadJSON( builder, jsonFileName ) )
241     {
242       CacheBuilder( builder, jsonFileName );
243       builderReady = true;
244     }
245   }
246
247   // Apply the style to the control
248   if( builderReady )
249   {
250     builder.ApplyStyle( styleName, control );
251   }
252 }
253
254 Toolkit::StyleManager::StyleChangeSignalType& StyleManager::StyleChangeSignal()
255 {
256   return mStyleChangeSignal;
257 }
258
259
260 void StyleManager::SetTheme()
261 {
262   mThemeBuilder = CreateBuilder( mThemeBuilderConstants );
263
264   if( LoadJSON( mThemeBuilder, mThemeFile ) )
265   {
266     if(mFeedbackStyle)
267     {
268       mFeedbackStyle->StyleChanged( mThemeFile, StyleChange::THEME_CHANGE );
269     }
270
271     mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), StyleChange::THEME_CHANGE );
272   }
273   else
274   {
275     mThemeBuilder.Reset();
276   }
277 }
278
279 bool StyleManager::LoadFile( const std::string& filename, std::string& stringOut )
280 {
281   DALI_ASSERT_DEBUG( 0 != filename.length());
282
283   // as toolkit is platform agnostic, it cannot load files from filesystem
284   // ask style monitor to load the style sheet
285   if( mStyleMonitor )
286   {
287     return mStyleMonitor.LoadThemeFile( filename, stringOut );
288   }
289
290   return false;
291 }
292
293 Toolkit::Builder StyleManager::CreateBuilder( const Property::Map& constants )
294 {
295   Toolkit::Builder builder = Toolkit::Builder::New();
296   builder.AddConstants( constants );
297
298   return builder;
299 }
300
301 bool StyleManager::LoadJSON( Toolkit::Builder builder, const std::string& jsonFilePath )
302 {
303   std::string fileString;
304   if( LoadFile( jsonFilePath, fileString ) )
305   {
306     builder.LoadFromString( fileString );
307     return true;
308   }
309   else
310   {
311     DALI_LOG_WARNING("Error loading file '%s'\n", jsonFilePath.c_str());
312     return false;
313   }
314 }
315
316 void StyleManager::CollectQualifiers( StringList& qualifiersOut )
317 {
318   // Append the relevant qualifier for orientation
319   int orientation = mOrientationDegrees;
320
321   if( mOrientation )
322   {
323     orientation = mOrientation.GetDegrees();
324   }
325
326   switch( orientation )
327   {
328     case 90:
329     case 270:
330     {
331       qualifiersOut.push_back( std::string( LANDSCAPE_QUALIFIER ) );
332       break;
333     }
334     case 180:
335     case 0: // fall through
336     default:
337     {
338       qualifiersOut.push_back( std::string( PORTRAIT_QUALIFIER ) );
339       break;
340     }
341   }
342 }
343
344 void StyleManager::BuildQualifiedStyleName( const std::string& styleName, const StringList& qualifiers, std::string& qualifiedStyleOut )
345 {
346   qualifiedStyleOut.append( styleName );
347
348   for( StringList::const_iterator it = qualifiers.begin(), itEnd = qualifiers.end(); it != itEnd; ++it )
349   {
350     const std::string& str = *it;
351
352     qualifiedStyleOut.append( "-" );
353     qualifiedStyleOut.append( str );
354   }
355 }
356
357 void StyleManager::ApplyStyle( Toolkit::Builder builder, Toolkit::Control control )
358 {
359   std::string styleName = control.GetStyleName();
360
361   if( styleName.empty() )
362   {
363     // Convert control name to lower case
364     styleName = control.GetTypeName();
365     std::transform( styleName.begin(), styleName.end(), styleName.begin(), ::tolower );
366   }
367
368   // Apply the style after choosing the correct actual style (e.g. landscape or portrait)
369   StringList qualifiers;
370   CollectQualifiers( qualifiers );
371
372   while( true )
373   {
374     std::string qualifiedStyleName;
375     BuildQualifiedStyleName( styleName, qualifiers, qualifiedStyleName );
376
377     // Break if style found or we have tried the root style name (qualifiers is empty)
378     if( builder.ApplyStyle( qualifiedStyleName, control ) || qualifiers.size() == 0 )
379     {
380       break;
381     }
382
383     // Remove the last qualifier in an attempt to find a style that is valid
384     qualifiers.pop_back();
385   }
386
387   if( mDefaultFontSize >= 0 )
388   {
389     // Apply the style for logical font size
390     std::stringstream fontSizeQualifier;
391     fontSizeQualifier << styleName << FONT_SIZE_QUALIFIER << mDefaultFontSize;
392     builder.ApplyStyle( fontSizeQualifier.str(), control );
393   }
394 }
395
396 void StyleManager::OnOrientationChanged( Orientation orientation )
397 {
398   mOrientation = orientation;
399   // TODO: if orientation changed, apply the new style to all controls
400   // dont want to really do the whole load from file again if the bundle contains both portrait & landscape
401   SetTheme();
402 }
403
404
405 Toolkit::Builder StyleManager::FindCachedBuilder( const std::string& key )
406 {
407   BuilderMap::iterator builderIt = mBuilderCache.find( key );
408   if( builderIt != mBuilderCache.end() )
409   {
410     return builderIt->second;
411   }
412
413   return Toolkit::Builder();
414 }
415
416 void StyleManager::CacheBuilder( Toolkit::Builder builder, const std::string& key )
417 {
418   mBuilderCache[ key ] = builder;
419 }
420
421 void StyleManager::StyleMonitorChange( StyleMonitor styleMonitor, StyleChange::Type styleChange )
422 {
423   switch ( styleChange )
424   {
425     case StyleChange::DEFAULT_FONT_CHANGE:
426     {
427       mDefaultFontFamily = styleMonitor.GetDefaultFontFamily();
428       break;
429     }
430
431     case StyleChange::DEFAULT_FONT_SIZE_CHANGE:
432     {
433       mDefaultFontSize = styleMonitor.GetDefaultFontSize();
434       break;
435     }
436
437     case StyleChange::THEME_CHANGE:
438     {
439       const std::string& newTheme = styleMonitor.GetTheme();
440       if( ! newTheme.empty() )
441       {
442         mThemeFile = newTheme;
443       }
444       else
445       {
446         mThemeFile = DEFAULT_THEME;
447       }
448
449       SetTheme();
450       break;
451     }
452   }
453
454   mStyleChangeSignal.Emit( Toolkit::StyleManager::Get(), styleChange );
455 }
456
457 } // namespace Internal
458
459 } // namespace Toolkit
460
461 } // namespace Dali