[NUI] Improve behaviors of ThemeManager and add an event
authorJiyun Yang <ji.yang@samsung.com>
Wed, 15 Feb 2023 04:35:27 +0000 (13:35 +0900)
committerJiyun Yang <ji.yang@samsung.com>
Thu, 16 Feb 2023 11:59:18 +0000 (20:59 +0900)
* ThemeManager.AppendTheme: While ApplyTheme() clears previous applied user theme, AppendTheme() appends it to the previous one.
* ThemeChanging event: An event called right after the theme changed and before applying it to the view.
* Open Theme.indexer public: This is for C_XAML.
* Theme implements IResourcesProvider: to support XAMLResources in xaml code

Signed-off-by: Jiyun Yang <ji.yang@samsung.com>
src/Tizen.NUI/src/public/Theme/Theme.cs
src/Tizen.NUI/src/public/Theme/ThemeManager.cs

index b0f1d4c..b4379bc 100755 (executable)
@@ -38,7 +38,7 @@ namespace Tizen.NUI
     /// </para>
     /// </summary>
     /// <since_tizen> 9 </since_tizen>
-    public class Theme : BindableObject
+    public class Theme : BindableObject, IResourcesProvider
     {
         private readonly Dictionary<string, ViewStyle> map;
         private IEnumerable<KeyValuePair<string, string>> changedResources = null;
@@ -162,7 +162,7 @@ namespace Tizen.NUI
 
         /// <inheritdoc/>
         [EditorBrowsable(EditorBrowsableState.Never)]
-        internal ResourceDictionary Resources
+        public ResourceDictionary XamlResources
         {
             get
             {
@@ -195,7 +195,8 @@ namespace Tizen.NUI
         /// Note that it is not a normal indexer.
         /// Setter will merge the new value with existing item.
         /// </summary>
-        internal ViewStyle this[string styleName]
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public ViewStyle this[string styleName]
         {
             get => map[styleName];
             set
@@ -307,7 +308,7 @@ namespace Tizen.NUI
             var result = new Theme()
             {
                 Id = this.Id,
-                Resources = Resources,
+                XamlResources = this.XamlResources,
                 SmallBrokenImageUrl = this.SmallBrokenImageUrl,
                 BrokenImageUrl = this.BrokenImageUrl,
                 LargeBrokenImageUrl = this.LargeBrokenImageUrl
@@ -364,6 +365,14 @@ namespace Tizen.NUI
                     map[item.Key] = item.Value.Clone();
                 }
             }
+
+            if (theme.resources != null)
+            {
+                foreach (var res in theme.resources)
+                {
+                    XamlResources[res.Key] = res.Value;
+                }
+            }
         }
 
         internal void MergeWithoutClone(Theme theme)
@@ -408,7 +417,7 @@ namespace Tizen.NUI
             {
                 foreach (var res in theme.resources)
                 {
-                    Resources[res.Key] = res.Value;
+                    XamlResources[res.Key] = res.Value;
                 }
             }
         }
index e3f37e2..2ff12cb 100755 (executable)
@@ -38,14 +38,27 @@ namespace Tizen.NUI
     /// <since_tizen> 9 </since_tizen>
     public static class ThemeManager
     {
+        /// <summary>
+        /// The default light theme name preloaded in platform.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public const string DefaultLightThemeName = "org.tizen.default-light-theme";
+
+        /// <summary>
+        /// The default dark theme name preloaded in platform.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public const string DefaultDarkThemeName = "org.tizen.default-dark-theme";
+
         private static Theme baseTheme; // The base theme. It includes all styles including structures (Size, Position, Policy) of components.
         private static Theme platformTheme; // The platform theme. This may include color and image information without structure detail.
         private static Theme userTheme; // The user custom theme.
         private static Theme themeForUpdate; // platformTheme + userTheme. It is used when the component need to update according to theme change.
         private static Theme themeForInitialize; // baseTheme + platformTheme + userTheme. It is used when the component is created.
         private static readonly List<Theme> cachedPlatformThemes = new List<Theme>(); // Themes provided by framework.
-        private static readonly List<IThemeCreator> packages = new List<IThemeCreator>();// This is to store base theme creators by packages.
+        private static readonly List<string> packages = new List<string>();// This is to store base theme creators by packages.
         private static bool platformThemeEnabled = false;
+        private static bool isInEventProgress = false;
 
         static ThemeManager()
         {
@@ -56,6 +69,12 @@ namespace Tizen.NUI
         }
 
         /// <summary>
+        /// An event invoked when the theme is about to change (not applied to the views yet).
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static event EventHandler<ThemeChangedEventArgs> ThemeChanging;
+
+        /// <summary>
         /// An event invoked after the theme has changed by <see cref="ApplyTheme(Theme)"/>.
         /// </summary>
         /// <since_tizen> 9 </since_tizen>
@@ -184,8 +203,42 @@ namespace Tizen.NUI
         }
 
         /// <summary>
+        /// Append a theme to the current theme and apply it.
+        /// This will change the appearance of the existing components with property <seealso cref="View.ThemeChangeSensitive"/> on.
+        /// This also affects all components created afterwards.
+        /// </summary>
+        /// <param name="theme">The theme instance to be appended.</param>
+        /// <exception cref="ArgumentNullException">Thrown when the given theme is null.</exception>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        public static void AppendTheme(Theme theme)
+        {
+            var newTheme = (Theme)theme?.Clone() ?? throw new ArgumentNullException(nameof(theme));
+
+            if (string.IsNullOrEmpty(newTheme.Id))
+            {
+                newTheme.Id = "NONAME";
+            }
+
+            StyleManager.Instance.SetBrokenImageUrl(StyleManager.BrokenImageType.Small, newTheme.SmallBrokenImageUrl ?? "");
+            StyleManager.Instance.SetBrokenImageUrl(StyleManager.BrokenImageType.Normal, newTheme.BrokenImageUrl ?? "");
+            StyleManager.Instance.SetBrokenImageUrl(StyleManager.BrokenImageType.Large, newTheme.LargeBrokenImageUrl ?? "");
+
+            if (userTheme == null) userTheme = theme;
+            else
+            {
+                userTheme = (Theme)userTheme.Clone();
+                userTheme.MergeWithoutClone(theme);
+            }
+
+            UpdateThemeForInitialize();
+            UpdateThemeForUpdate();
+            NotifyThemeChanged();
+        }
+
+        /// <summary>
         /// Change tizen theme.
         /// User may change this to one of platform installed one.
+        /// Note that this is global theme changing which effects all applications.
         /// </summary>
         /// <param name="themeId">The installed theme Id.</param>
         /// <returns>true on success, false when it failed to find installed theme with given themeId.</returns>
@@ -360,13 +413,14 @@ namespace Tizen.NUI
 
         internal static void AddPackageTheme(IThemeCreator themeCreator)
         {
-            if (InitialThemeDisabled || packages.Contains(themeCreator))
+            string packageName;
+            if (InitialThemeDisabled || packages.Contains(packageName = themeCreator.GetType().Assembly.GetName().Name))
             {
                 return;
             }
 
             Tizen.Log.Debug("NUI", $"AddPackageTheme({themeCreator.GetType().Assembly.GetName().Name})");
-            packages.Add(themeCreator);
+            packages.Add(packageName);
 
             // Base theme
             var packageBaseTheme = themeCreator.Create();
@@ -463,7 +517,7 @@ namespace Tizen.NUI
 
             for (var i = theme.PackageCount; i < packages.Count; i++)
             {
-                theme.MergeWithoutClone(CreatePlatformTheme(sharedResourcePath, packages[i].GetType().Assembly.GetName().Name));
+                theme.MergeWithoutClone(CreatePlatformTheme(sharedResourcePath, packages[i]));
             }
             theme.PackageCount = packages.Count;
         }
@@ -482,9 +536,9 @@ namespace Tizen.NUI
                 Id = id
             };
 
-            foreach (var packageCreator in packages)
+            foreach (var packageName in packages)
             {
-                newTheme.MergeWithoutClone(CreatePlatformTheme(sharedResourcePath, packageCreator.GetType().Assembly.GetName().Name));
+                newTheme.MergeWithoutClone(CreatePlatformTheme(sharedResourcePath, packageName));
             }
             newTheme.PackageCount = packages.Count;
 
@@ -528,10 +582,16 @@ namespace Tizen.NUI
 
         private static void NotifyThemeChanged(bool platformThemeUpdated = false)
         {
+            if (isInEventProgress) return;
+            isInEventProgress = true;
+
             var platformThemeId = PlatformThemeId;
             var userThemeId = userTheme?.Id;
+            ThemeChanging?.Invoke(null, new ThemeChangedEventArgs(userThemeId, platformThemeId, platformThemeUpdated));
             ThemeChangedInternal.Invoke(null, new ThemeChangedEventArgs(userThemeId, platformThemeId, platformThemeUpdated));
             ThemeChanged?.Invoke(null, new ThemeChangedEventArgs(userThemeId, platformThemeId, platformThemeUpdated));
+
+            isInEventProgress = false;
         }
     }
 }