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
99 if (defaultTheme == null && !isLoadingDefault)
101 isLoadingDefault = true;
102 defaultTheme = LoadBuiltinTheme(profileDefaultTheme[(int)CurrentProfile]);
103 isLoadingDefault = false;
107 set => defaultTheme = (Theme)value?.Clone();
110 internal static bool ThemeApplied => (CurrentTheme.Count > 0 || DefaultTheme.Count > 0);
112 private static Profile CurrentProfile
116 if (currentProfile == null)
118 currentProfile = Profile.Common;
119 string profileString = "";
123 Information.TryGetValue<string>("tizen.org/feature/profile", out profileString);
124 Tizen.Log.Info("NUI", "Profile for initial theme found : " + profileString);
128 Tizen.Log.Info("NUI", "Unknown device profile\n");
132 if (string.Equals(profileString, "mobile"))
134 currentProfile = Profile.Mobile;
136 else if (string.Equals(profileString, "tv"))
138 currentProfile = Profile.TV;
140 else if (string.Equals(profileString, "wearable"))
142 currentProfile = Profile.Wearable;
146 return (Profile)currentProfile;
151 /// Set a theme to be used as fallback.
152 /// The fallback theme is set to profile specified theme by default.
154 /// <param name="fallbackTheme">The theme instance to be applied as a fallback.</param>
155 /// <exception cref="ArgumentNullException">The given theme is null.</exception>
156 [EditorBrowsable(EditorBrowsableState.Never)]
157 public static void ApplyFallbackTheme(Theme fallbackTheme)
159 DefaultTheme = fallbackTheme ?? throw new ArgumentNullException("Invalid theme.");
163 /// Apply theme to the NUI.
164 /// This will change the appreance of the existing components with property <seealso cref="View.ThemeChangeSensitive"/> on.
165 /// This also affects all components created afterwards.
167 /// <param name="theme">The theme instance to be applied.</param>
168 /// <exception cref="ArgumentNullException">Thrown when the given theme is null.</exception>
169 [EditorBrowsable(EditorBrowsableState.Never)]
170 public static void ApplyTheme(Theme theme)
172 var newTheme = (Theme)theme?.Clone() ?? throw new ArgumentNullException("Invalid theme.");
174 if (string.IsNullOrEmpty(newTheme.Id))
176 newTheme.Id = "NONAME";
179 CurrentTheme = newTheme;
183 /// Note that this API is to support legacy Tizen.NUI.Components.StyleManager.
184 /// Please use <seealso cref="ApplyTheme(Theme)"/> instead.
186 /// Apply theme to the NUI using theme id.
187 /// The id of theme should be either a registered custom theme or a built-in theme.
188 /// You can register custom theme using <seealso cref="RegisterTheme(Theme)"/>.
189 /// This will change the appreance of the existing components with property <seealso cref="View.ThemeChangeSensitive"/> on.
190 /// This also affects all components created afterwards.
192 /// <param name="themeId">The theme Id.</param>
193 /// <exception cref="ArgumentNullException">Thrown when the given themeId is null.</exception>
194 [EditorBrowsable(EditorBrowsableState.Never)]
195 public static void ApplyTheme(string themeId)
197 if (themeId == null) throw new ArgumentNullException("Invalid themeId");
199 int index = customThemes.FindIndex(x => x.Id.Equals(themeId, StringComparison.OrdinalIgnoreCase));
202 CurrentTheme = customThemes[index];
206 index = builtinThemes.FindIndex(x => string.Equals(x.Id, themeId, StringComparison.OrdinalIgnoreCase));
209 CurrentTheme = builtinThemes[index];
213 Tizen.Log.Info("NUI", $"No Theme found with given id : {themeId}");
218 /// Note that this API is to support legacy Tizen.NUI.Components.StyleManager.
220 /// Register a custom theme that can be used as an id when calling <seealso cref="ApplyTheme(string)"/>.
222 /// <param name="theme">The theme instance.</param>
223 /// <exception cref="ArgumentException">Thrown when the given theme is null or invalid.</exception>
224 [EditorBrowsable(EditorBrowsableState.Never)]
225 public static void RegisterTheme(Theme theme)
227 if (theme == null || string.IsNullOrEmpty(theme.Id)) throw new ArgumentException("Invalid theme.");
229 int index = customThemes.FindIndex(x => x.Id.Equals(theme.Id, StringComparison.OrdinalIgnoreCase));
232 customThemes[index] = (Theme)theme.Clone();
236 customThemes.Add((Theme)theme.Clone());
241 /// Load a style with style name in the current theme.
242 /// For components, the style name is a component name (e.g. Button) in normal case.
244 /// <param name="styleName">The style name.</param>
245 /// <exception cref="ArgumentNullException">Thrown when the given styleName is null.</exception>
246 [EditorBrowsable(EditorBrowsableState.Never)]
247 public static ViewStyle GetStyle(string styleName)
249 if (styleName == null) throw new ArgumentNullException("Invalid style name");
251 if (!ThemeApplied) return null;
253 return (CurrentTheme.GetStyle(styleName) ?? DefaultTheme.GetStyle(styleName))?.Clone();
257 /// Load a style with View type in the current theme.
259 /// <param name="viewType">The type of View.</param>
260 /// <exception cref="ArgumentNullException">Thrown when the given viewType is null.</exception>
261 [EditorBrowsable(EditorBrowsableState.Never)]
262 public static ViewStyle GetStyle(Type viewType)
264 if (viewType == null) throw new ArgumentNullException("Invalid viewType");
266 if (!ThemeApplied) return null;
268 return (CurrentTheme.GetStyle(viewType) ?? DefaultTheme.GetStyle(viewType))?.Clone();
272 /// Get a cloned built-in theme.
274 /// <param name="themeId">The built-in theme id.</param>
275 /// <exception cref="ArgumentNullException">Thrown when the given themeId is null.</exception>
276 [EditorBrowsable(EditorBrowsableState.Never)]
277 public static Theme GetBuiltinTheme(string themeId)
279 if (themeId == null) throw new ArgumentNullException("Invalid themeId");
282 int index = builtinThemes.FindIndex(x => string.Equals(x.Id, themeId, StringComparison.OrdinalIgnoreCase));
285 result = builtinThemes[index];
289 var theme = LoadBuiltinTheme(themeId);
290 builtinThemes.Add(theme);
293 return (Theme)result?.Clone();
296 private static Theme LoadBuiltinTheme(string id)
298 var loaded = new Theme()
303 if (string.IsNullOrEmpty(id)) return loaded;
305 foreach (var project in nuiThemeProjects)
307 string path = StyleManager.FrameworkResourcePath + "/Theme/" + project + "_" + id + ".xaml";
309 if (!File.Exists(path))
311 Tizen.Log.Info("NUI", $"\"{path}\" is not found in this profile.\n");
319 Tizen.Log.Info("NUI", $"Done to load \"{path}\".\n");
323 Tizen.Log.Debug("NUI", $"Could not load \"{path}\"\n");
324 Tizen.Log.Debug("NUI", "Message: " + e + "\n");
331 private static void NotifyThemeChanged()
333 ThemeChangedInternal?.Invoke(null, new ThemeChangedEventArgs(CurrentTheme?.Id));
334 ThemeChanged?.Invoke(null, new ThemeChangedEventArgs(CurrentTheme?.Id));