/*
* Copyright(c) 2020 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
extern alias TizenSystemInformation;
using TizenSystemInformation.Tizen.System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using Tizen.NUI.Xaml;
using Tizen.NUI.BaseComponents;
namespace Tizen.NUI
{
///
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ThemeManager
{
private enum Profile
{
Common = 0,
Mobile = 1,
TV = 2,
Wearable = 3
}
private static readonly string[] nuiThemeProjects =
{
"Tizen.NUI",
"Tizen.NUI.Components",
"Tizen.NUI.Wearable"
};
///
/// Table that indicates default theme id by device profile.
/// Note that, the fallback of null value is Common value.
///
private static readonly string[] profileDefaultTheme =
{
/* Common */ "Tizen.NUI.Theme.Common",
/* Mobile */ "Tizen.NUI.Theme.Common",
/* TV */ null,
/* Wearable */ "Tizen.NUI.Theme.Wearable",
};
private static Theme currentTheme;
private static Theme defaultTheme;
private static bool isLoadingDefault = false;
private static Profile? currentProfile;
private static List builtinThemes = new List(); // Themes provided by framework.
internal static List customThemes = new List(); // Themes registered by user.
static ThemeManager() {}
///
///
[EditorBrowsable(EditorBrowsableState.Never)]
public static event EventHandler ThemeChanged;
///
/// Internal one should be called before calling public ThemeChanged
///
internal static event EventHandler ThemeChangedInternal;
internal static Theme CurrentTheme
{
get
{
if (currentTheme == null)
{
currentTheme = DefaultTheme;
}
return currentTheme;
}
set
{
currentTheme = value;
NotifyThemeChanged();
}
}
internal static Theme DefaultTheme
{
get
{
if (defaultTheme == null && !isLoadingDefault)
{
isLoadingDefault = true;
defaultTheme = LoadBuiltinTheme(profileDefaultTheme[(int)CurrentProfile]);
isLoadingDefault = false;
}
return defaultTheme;
}
set => defaultTheme = (Theme)value?.Clone();
}
internal static bool ThemeApplied => (CurrentTheme.Count > 0 || DefaultTheme.Count > 0);
private static Profile CurrentProfile
{
get
{
if (currentProfile == null)
{
currentProfile = Profile.Common;
string profileString = "";
try
{
Information.TryGetValue("tizen.org/feature/profile", out profileString);
Tizen.Log.Info("NUI", "Profile for initial theme found : " + profileString);
}
catch
{
Tizen.Log.Info("NUI", "Unknown device profile\n");
}
finally
{
if (string.Equals(profileString, "mobile"))
{
currentProfile = Profile.Mobile;
}
else if (string.Equals(profileString, "tv"))
{
currentProfile = Profile.TV;
}
else if (string.Equals(profileString, "wearable"))
{
currentProfile = Profile.Wearable;
}
}
}
return (Profile)currentProfile;
}
}
///
/// Set a theme to be used as fallback.
/// The fallback theme is set to profile specified theme by default.
///
/// The theme instance to be applied as a fallback.
/// The given theme is null.
[EditorBrowsable(EditorBrowsableState.Never)]
public static void ApplyFallbackTheme(Theme fallbackTheme)
{
DefaultTheme = fallbackTheme ?? throw new ArgumentNullException("Invalid theme.");
}
///
/// Apply theme to the NUI.
/// This will change the appreance of the existing components with property on.
/// This also affects all components created afterwards.
///
/// The theme instance to be applied.
/// Thrown when the given theme is null.
[EditorBrowsable(EditorBrowsableState.Never)]
public static void ApplyTheme(Theme theme)
{
var newTheme = (Theme)theme?.Clone() ?? throw new ArgumentNullException("Invalid theme.");
if (string.IsNullOrEmpty(newTheme.Id))
{
newTheme.Id = "NONAME";
}
CurrentTheme = newTheme;
}
///
/// Note that this API is to support legacy Tizen.NUI.Components.StyleManager.
/// Please use instead.
///
/// Apply theme to the NUI using theme id.
/// The id of theme should be either a registered custom theme or a built-in theme.
/// You can register custom theme using .
/// This will change the appreance of the existing components with property on.
/// This also affects all components created afterwards.
///
/// The theme Id.
/// Thrown when the given themeId is null.
[EditorBrowsable(EditorBrowsableState.Never)]
public static void ApplyTheme(string themeId)
{
if (themeId == null) throw new ArgumentNullException("Invalid themeId");
int index = customThemes.FindIndex(x => x.Id.Equals(themeId, StringComparison.OrdinalIgnoreCase));
if (index >= 0)
{
CurrentTheme = customThemes[index];
return;
}
index = builtinThemes.FindIndex(x => string.Equals(x.Id, themeId, StringComparison.OrdinalIgnoreCase));
if (index >= 0)
{
CurrentTheme = builtinThemes[index];
}
else
{
Tizen.Log.Info("NUI", $"No Theme found with given id : {themeId}");
}
}
///
/// Note that this API is to support legacy Tizen.NUI.Components.StyleManager.
///
/// Register a custom theme that can be used as an id when calling .
///
/// The theme instance.
/// Thrown when the given theme is null or invalid.
[EditorBrowsable(EditorBrowsableState.Never)]
public static void RegisterTheme(Theme theme)
{
if (theme == null || string.IsNullOrEmpty(theme.Id)) throw new ArgumentException("Invalid theme.");
int index = customThemes.FindIndex(x => x.Id.Equals(theme.Id, StringComparison.OrdinalIgnoreCase));
if (index >= 0)
{
customThemes[index] = (Theme)theme.Clone();
}
else
{
customThemes.Add((Theme)theme.Clone());
}
}
///
/// Load a style with style name in the current theme.
/// For components, the style name is a component name (e.g. Button) in normal case.
///
/// The style name.
/// Thrown when the given styleName is null.
[EditorBrowsable(EditorBrowsableState.Never)]
public static ViewStyle GetStyle(string styleName)
{
if (styleName == null) throw new ArgumentNullException("Invalid style name");
if (!ThemeApplied) return null;
return (CurrentTheme.GetStyle(styleName) ?? DefaultTheme.GetStyle(styleName))?.Clone();
}
///
/// Load a style with View type in the current theme.
///
/// The type of View.
/// Thrown when the given viewType is null.
[EditorBrowsable(EditorBrowsableState.Never)]
public static ViewStyle GetStyle(Type viewType)
{
if (viewType == null) throw new ArgumentNullException("Invalid viewType");
if (!ThemeApplied) return null;
return (CurrentTheme.GetStyle(viewType) ?? DefaultTheme.GetStyle(viewType))?.Clone();
}
///
/// Get a cloned built-in theme.
///
/// The built-in theme id.
/// Thrown when the given themeId is null.
[EditorBrowsable(EditorBrowsableState.Never)]
public static Theme GetBuiltinTheme(string themeId)
{
if (themeId == null) throw new ArgumentNullException("Invalid themeId");
Theme result = null;
int index = builtinThemes.FindIndex(x => string.Equals(x.Id, themeId, StringComparison.OrdinalIgnoreCase));
if (index >= 0)
{
result = builtinThemes[index];
}
else
{
var theme = LoadBuiltinTheme(themeId);
builtinThemes.Add(theme);
result = theme;
}
return (Theme)result?.Clone();
}
private static Theme LoadBuiltinTheme(string id)
{
var loaded = new Theme()
{
Id = id,
};
if (string.IsNullOrEmpty(id)) return loaded;
foreach (var project in nuiThemeProjects)
{
string path = StyleManager.FrameworkResourcePath + "/Theme/" + project + "_" + id + ".xaml";
if (!File.Exists(path))
{
Tizen.Log.Info("NUI", $"\"{path}\" is not found in this profile.\n");
continue;
}
try
{
loaded.Merge(path);
loaded.Id = id;
Tizen.Log.Info("NUI", $"Done to load \"{path}\".\n");
}
catch (Exception e)
{
Tizen.Log.Debug("NUI", $"Could not load \"{path}\"\n");
Tizen.Log.Debug("NUI", "Message: " + e + "\n");
}
}
return loaded;
}
private static void NotifyThemeChanged()
{
ThemeChangedInternal?.Invoke(null, new ThemeChangedEventArgs(CurrentTheme?.Id));
ThemeChanged?.Invoke(null, new ThemeChangedEventArgs(CurrentTheme?.Id));
}
}
}