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;
23 using Tizen.NUI.BaseComponents;
28 /// This static module provides methods that can manage NUI <seealso cref="Theme"/>.
31 /// To apply custom theme to the application, try <seealso cref="ApplyTheme(Theme)"/>.
33 /// var customTheme = new Theme(res + "customThemeFile.xaml");
34 /// ThemeManager.ApplyTheme(customTheme);
37 [EditorBrowsable(EditorBrowsableState.Never)]
38 public static class ThemeManager
48 private static readonly string[] nuiThemeProjects =
51 "Tizen.NUI.Components",
56 /// Table that indicates default theme id by device profile.
57 /// Note that, the fallback of null value is Common value.
59 private static readonly string[] profileDefaultTheme =
61 /* Common */ "Tizen.NUI.Theme.Common",
62 /* Mobile */ "Tizen.NUI.Theme.Common",
64 /* Wearable */ "Tizen.NUI.Theme.Wearable",
67 private static Theme currentTheme;
68 private static Theme defaultTheme;
69 private static bool isLoadingDefault = false;
70 private static Profile? currentProfile;
71 private static readonly List<Theme> builtinThemes = new List<Theme>(); // Themes provided by framework.
72 internal static List<Theme> customThemes = new List<Theme>(); // Themes registered by user.
74 static ThemeManager() { }
77 /// An event invoked after the theme has changed by <seealso cref="ApplyTheme(Theme)"/>.
79 [EditorBrowsable(EditorBrowsableState.Never)]
80 public static event EventHandler<ThemeChangedEventArgs> ThemeChanged;
83 /// Internal one should be called before calling public ThemeChanged
85 internal static event EventHandler<ThemeChangedEventArgs> ThemeChangedInternal;
87 internal static Theme CurrentTheme
89 get => currentTheme ?? (currentTheme = DefaultTheme);
97 internal static Theme DefaultTheme
101 if (defaultTheme == null && !isLoadingDefault)
103 isLoadingDefault = true;
104 defaultTheme = LoadBuiltinTheme(profileDefaultTheme[(int)CurrentProfile]);
105 isLoadingDefault = false;
109 set => defaultTheme = (Theme)value?.Clone();
112 internal static bool ThemeApplied => (CurrentTheme.Count > 0 || DefaultTheme.Count > 0);
114 private static Profile CurrentProfile
118 if (currentProfile == null)
120 currentProfile = Profile.Common;
121 string profileString = "";
125 Information.TryGetValue<string>("tizen.org/feature/profile", out profileString);
126 Tizen.Log.Info("NUI", "Profile for initial theme found : " + profileString);
131 // Do not rethrow exception.
132 // In some machine, profile may not exits.
133 Tizen.Log.Info("NUI", "Unknown device profile\n");
137 if (string.Equals(profileString, "mobile"))
139 currentProfile = Profile.Mobile;
141 else if (string.Equals(profileString, "tv"))
143 currentProfile = Profile.TV;
145 else if (string.Equals(profileString, "wearable"))
147 currentProfile = Profile.Wearable;
151 return (Profile)currentProfile;
156 /// Set a theme to be used as fallback.
157 /// The fallback theme is set to profile specified theme by default.
159 /// <param name="fallbackTheme">The theme instance to be applied as a fallback.</param>
160 /// <exception cref="ArgumentNullException">The given theme is null.</exception>
161 [EditorBrowsable(EditorBrowsableState.Never)]
162 public static void ApplyFallbackTheme(Theme fallbackTheme)
164 DefaultTheme = fallbackTheme ?? throw new ArgumentNullException(nameof(fallbackTheme));
168 /// Apply theme to the NUI.
169 /// This will change the appreance of the existing components with property <seealso cref="View.ThemeChangeSensitive"/> on.
170 /// This also affects all components created afterwards.
172 /// <param name="theme">The theme instance to be applied.</param>
173 /// <exception cref="ArgumentNullException">Thrown when the given theme is null.</exception>
174 [EditorBrowsable(EditorBrowsableState.Never)]
175 public static void ApplyTheme(Theme theme)
177 var newTheme = (Theme)theme?.Clone() ?? throw new ArgumentNullException(nameof(theme));
179 if (string.IsNullOrEmpty(newTheme.Id))
181 newTheme.Id = "NONAME";
184 CurrentTheme = newTheme;
189 /// Note that this API is to support legacy Tizen.NUI.Components.StyleManager.
190 /// Please use <seealso cref="ApplyTheme(Theme)"/> instead.
193 /// Apply theme to the NUI using theme id.
194 /// The id of theme should be either a registered custom theme or a built-in theme.
195 /// You can register custom theme using <seealso cref="RegisterTheme(Theme)"/>.
196 /// This will change the appreance of the existing components with property <seealso cref="View.ThemeChangeSensitive"/> on.
197 /// This also affects all components created afterwards.
200 /// <param name="themeId">The theme Id.</param>
201 /// <exception cref="ArgumentNullException">Thrown when the given themeId is null.</exception>
202 [EditorBrowsable(EditorBrowsableState.Never)]
203 public static void ApplyTheme(string themeId)
205 if (themeId == null) throw new ArgumentNullException(nameof(themeId));
207 int index = customThemes.FindIndex(x => x.Id.Equals(themeId, StringComparison.OrdinalIgnoreCase));
210 CurrentTheme = customThemes[index];
214 index = builtinThemes.FindIndex(x => string.Equals(x.Id, themeId, StringComparison.OrdinalIgnoreCase));
217 CurrentTheme = builtinThemes[index];
221 Tizen.Log.Info("NUI", $"No Theme found with given id : {themeId}");
226 /// <para> Note that this API is to support legacy Tizen.NUI.Components.StyleManager. </para>
227 /// <para> Register a custom theme that can be used as an id when calling <seealso cref="ApplyTheme(string)"/>. </para>
229 /// <param name="theme">The theme instance.</param>
230 /// <exception cref="ArgumentNullException">Thrown when the given theme is null.</exception>
231 /// <exception cref="ArgumentException">Thrown when the given theme id is invalid.</exception>
232 [EditorBrowsable(EditorBrowsableState.Never)]
233 public static void RegisterTheme(Theme theme)
235 if (theme == null) throw new ArgumentNullException(nameof(theme));
236 if (string.IsNullOrEmpty(theme.Id)) throw new ArgumentException("Invalid theme id.");
238 int index = customThemes.FindIndex(x => x.Id.Equals(theme.Id, StringComparison.OrdinalIgnoreCase));
241 customThemes[index] = (Theme)theme.Clone();
245 customThemes.Add((Theme)theme.Clone());
250 /// Load a style with style name in the current theme.
251 /// For components, the style name is a component name (e.g. Button) in normal case.
253 /// <param name="styleName">The style name.</param>
254 /// <exception cref="ArgumentNullException">Thrown when the given styleName is null.</exception>
255 [EditorBrowsable(EditorBrowsableState.Never)]
256 public static ViewStyle GetStyle(string styleName)
258 if (styleName == null) throw new ArgumentNullException(nameof(styleName));
260 if (!ThemeApplied) return null;
262 return (CurrentTheme.GetStyle(styleName) ?? DefaultTheme.GetStyle(styleName))?.Clone();
266 /// Load a style with View type in the current theme.
268 /// <param name="viewType">The type of View.</param>
269 /// <exception cref="ArgumentNullException">Thrown when the given viewType is null.</exception>
270 [EditorBrowsable(EditorBrowsableState.Never)]
271 public static ViewStyle GetStyle(Type viewType)
273 if (viewType == null) throw new ArgumentNullException(nameof(viewType));
275 if (!ThemeApplied) return null;
277 return (CurrentTheme.GetStyle(viewType) ?? DefaultTheme.GetStyle(viewType))?.Clone();
281 /// Get a cloned built-in theme.
283 /// <param name="themeId">The built-in theme id.</param>
284 /// <exception cref="ArgumentNullException">Thrown when the given themeId is null.</exception>
285 [EditorBrowsable(EditorBrowsableState.Never)]
286 public static Theme GetBuiltinTheme(string themeId)
288 if (themeId == null) throw new ArgumentNullException(nameof(themeId));
291 int index = builtinThemes.FindIndex(x => string.Equals(x.Id, themeId, StringComparison.OrdinalIgnoreCase));
294 result = builtinThemes[index];
298 var theme = LoadBuiltinTheme(themeId);
299 builtinThemes.Add(theme);
302 return (Theme)result?.Clone();
305 private static Theme LoadBuiltinTheme(string id)
307 var loaded = new Theme()
312 if (string.IsNullOrEmpty(id)) return loaded;
314 foreach (var project in nuiThemeProjects)
316 string path = StyleManager.FrameworkResourcePath + "/Theme/" + project + "_" + id + ".xaml";
318 if (!File.Exists(path))
320 Tizen.Log.Info("NUI", $"\"{path}\" is not found in this profile.\n");
328 Tizen.Log.Info("NUI", $"Done to load \"{path}\".\n");
332 Tizen.Log.Debug("NUI", $"Could not load \"{path}\"\n");
333 Tizen.Log.Debug("NUI", "Message: " + e + "\n");
340 private static void NotifyThemeChanged()
342 ThemeChangedInternal?.Invoke(null, new ThemeChangedEventArgs(CurrentTheme?.Id));
343 ThemeChanged?.Invoke(null, new ThemeChangedEventArgs(CurrentTheme?.Id));