2 * Copyright(c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 extern alias TizenSystemInformation;
18 using TizenSystemInformation.Tizen.System;
20 using System.Collections.Generic;
21 using System.ComponentModel;
24 using Tizen.NUI.BaseComponents;
28 /// <summary></summary>
29 [EditorBrowsable(EditorBrowsableState.Never)]
30 public static class ThemeManager
40 private static readonly string[] nuiThemeProjects =
43 "Tizen.NUI.Components",
48 /// Table that indicates default theme id by device profile.
49 /// Note that, the fallback of null value is Common value.
51 private static readonly string[] profileDefaultTheme =
53 /* Common */ "Tizen.NUI.Theme.Common",
54 /* Mobile */ "Tizen.NUI.Theme.Common",
56 /* Wearable */ "Tizen.NUI.Theme.Wearable",
59 private static Theme currentTheme;
60 private static Theme defaultTheme;
61 private static bool isLoadingDefault = false;
62 private static Profile? currentProfile;
63 private static List<Theme> builtinThemes = new List<Theme>(); // Themes provided by framework.
64 internal static List<Theme> customThemes = new List<Theme>(); // Themes registered by user.
66 static ThemeManager() {}
70 [EditorBrowsable(EditorBrowsableState.Never)]
71 public static event EventHandler<ThemeChangedEventArgs> ThemeChanged;
74 /// Internal one should be called before calling public ThemeChanged
76 internal static event EventHandler<ThemeChangedEventArgs> ThemeChangedInternal;
78 internal static Theme CurrentTheme
82 if (currentTheme == null)
84 currentTheme = DefaultTheme;
95 internal static Theme DefaultTheme
102 set => defaultTheme = (Theme)value?.Clone();
105 internal static bool ThemeApplied => (CurrentTheme.Count > 0 || DefaultTheme.Count > 0);
107 private static Profile CurrentProfile
111 if (currentProfile == null)
113 currentProfile = Profile.Common;
114 string profileString = "";
118 Information.TryGetValue<string>("tizen.org/feature/profile", out profileString);
119 Tizen.Log.Info("NUI", "Profile for initial theme found : " + profileString);
123 Tizen.Log.Info("NUI", "Unknown device profile\n");
127 if (string.Equals(profileString, "mobile"))
129 currentProfile = Profile.Mobile;
131 else if (string.Equals(profileString, "tv"))
133 currentProfile = Profile.TV;
135 else if (string.Equals(profileString, "wearable"))
137 currentProfile = Profile.Wearable;
141 return (Profile)currentProfile;
146 /// Set a theme to be used as fallback.
147 /// The fallback theme is set to profile specified theme by default.
149 /// <param name="fallbackTheme">The theme instance to be applied as a fallback.</param>
150 /// <exception cref="ArgumentNullException">The given theme is null.</exception>
151 [EditorBrowsable(EditorBrowsableState.Never)]
152 public static void ApplyFallbackTheme(Theme fallbackTheme)
154 DefaultTheme = fallbackTheme ?? throw new ArgumentNullException("Invalid theme.");
158 /// Apply theme to the NUI.
159 /// This will change the appreance of the existing components with property <seealso cref="View.ThemeChangeSensitive"/> on.
160 /// This also affects all components created afterwards.
162 /// <param name="theme">The theme instance to be applied.</param>
163 /// <exception cref="ArgumentNullException">Thrown when the given theme is null.</exception>
164 [EditorBrowsable(EditorBrowsableState.Never)]
165 public static void ApplyTheme(Theme theme)
167 var newTheme = (Theme)theme?.Clone() ?? throw new ArgumentNullException("Invalid theme.");
169 if (string.IsNullOrEmpty(newTheme.Id))
171 newTheme.Id = "NONAME";
174 CurrentTheme = newTheme;
178 /// Note that this API is to support legacy Tizen.NUI.Components.StyleManager.
179 /// Please use <seealso cref="ApplyTheme(Theme)"/> instead.
181 /// Apply theme to the NUI using theme id.
182 /// The id of theme should be either a registered custom theme or a built-in theme.
183 /// You can register custom theme using <seealso cref="RegisterTheme(Theme)"/>.
184 /// This will change the appreance of the existing components with property <seealso cref="View.ThemeChangeSensitive"/> on.
185 /// This also affects all components created afterwards.
187 /// <param name="themeId">The theme Id.</param>
188 /// <exception cref="ArgumentNullException">Thrown when the given themeId is null.</exception>
189 [EditorBrowsable(EditorBrowsableState.Never)]
190 public static void ApplyTheme(string themeId)
192 if (themeId == null) throw new ArgumentNullException("Invalid themeId");
194 int index = customThemes.FindIndex(x => x.Id.Equals(themeId, StringComparison.OrdinalIgnoreCase));
197 CurrentTheme = customThemes[index];
201 index = builtinThemes.FindIndex(x => string.Equals(x.Id, themeId, StringComparison.OrdinalIgnoreCase));
204 CurrentTheme = builtinThemes[index];
208 Tizen.Log.Info("NUI", $"No Theme found with given id : {themeId}");
213 /// Note that this API is to support legacy Tizen.NUI.Components.StyleManager.
215 /// Register a custom theme that can be used as an id when calling <seealso cref="ApplyTheme(string)"/>.
217 /// <param name="theme">The theme instance.</param>
218 /// <exception cref="ArgumentException">Thrown when the given theme is null or invalid.</exception>
219 [EditorBrowsable(EditorBrowsableState.Never)]
220 public static void RegisterTheme(Theme theme)
222 if (theme == null || string.IsNullOrEmpty(theme.Id)) throw new ArgumentException("Invalid theme.");
224 int index = customThemes.FindIndex(x => x.Id.Equals(theme.Id, StringComparison.OrdinalIgnoreCase));
227 customThemes[index] = (Theme)theme.Clone();
231 customThemes.Add((Theme)theme.Clone());
236 /// Load a style with style name in the current theme.
237 /// For components, the style name is a component name (e.g. Button) in normal case.
239 /// <param name="styleName">The style name.</param>
240 /// <exception cref="ArgumentNullException">Thrown when the given styleName is null.</exception>
241 [EditorBrowsable(EditorBrowsableState.Never)]
242 public static ViewStyle GetStyle(string styleName)
244 if (styleName == null) throw new ArgumentNullException("Invalid style name");
246 if (!ThemeApplied) return null;
248 return (CurrentTheme.GetStyle(styleName) ?? DefaultTheme.GetStyle(styleName))?.Clone();
252 /// Load a style with View type in the current theme.
254 /// <param name="viewType">The type of View.</param>
255 /// <exception cref="ArgumentNullException">Thrown when the given viewType is null.</exception>
256 [EditorBrowsable(EditorBrowsableState.Never)]
257 public static ViewStyle GetStyle(Type viewType)
259 if (viewType == null) throw new ArgumentNullException("Invalid viewType");
261 if (!ThemeApplied) return null;
263 return (CurrentTheme.GetStyle(viewType) ?? DefaultTheme.GetStyle(viewType))?.Clone();
267 /// Get a cloned built-in theme.
269 /// <param name="themeId">The built-in theme id.</param>
270 /// <exception cref="ArgumentNullException">Thrown when the given themeId is null.</exception>
271 [EditorBrowsable(EditorBrowsableState.Never)]
272 public static Theme GetBuiltinTheme(string themeId)
274 if (themeId == null) throw new ArgumentNullException("Invalid themeId");
277 int index = builtinThemes.FindIndex(x => string.Equals(x.Id, themeId, StringComparison.OrdinalIgnoreCase));
280 result = builtinThemes[index];
284 var theme = LoadBuiltinTheme(themeId);
285 builtinThemes.Add(theme);
288 return (Theme)result?.Clone();
291 internal static void EnsureDefaultTheme()
293 if (defaultTheme == null && !isLoadingDefault)
295 isLoadingDefault = true;
296 defaultTheme = LoadBuiltinTheme(profileDefaultTheme[(int)CurrentProfile]);
297 isLoadingDefault = false;
301 private static Theme LoadBuiltinTheme(string id)
303 var loaded = new Theme()
308 if (string.IsNullOrEmpty(id)) return loaded;
310 foreach (var project in nuiThemeProjects)
312 string path = FrameworkInformation.ResourcePath + "/Theme/" + project + "_" + id + ".xaml";
314 if (!File.Exists(path))
316 Tizen.Log.Info("NUI", $"\"{path}\" is not found in this profile.\n");
324 Tizen.Log.Info("NUI", $"Done to load \"{path}\".\n");
328 Tizen.Log.Debug("NUI", $"Could not load \"{path}\"\n");
329 Tizen.Log.Debug("NUI", "Message: " + e + "\n");
336 private static void NotifyThemeChanged()
338 ThemeChangedInternal?.Invoke(null, new ThemeChangedEventArgs(CurrentTheme?.Id));
339 ThemeChanged?.Invoke(null, new ThemeChangedEventArgs(CurrentTheme?.Id));