Browse Source

ResourceProvider upgrade (#16928)

* Implement AvaloniaObject on ResourceProvider

* ColorPaletteResources and ColorPaletteResourcesCollection should inherit ResourceProvider

* Use ResourceProvider in tests too

* Adjust API diff

---------

Co-authored-by: Steven Kirk <[email protected]>
Max Katz 1 year ago
parent
commit
c50e731d8d

+ 16 - 0
api/Avalonia.Themes.Fluent.nupkg.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
+<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Suppression>
+    <DiagnosticId>CP0007</DiagnosticId>
+    <Target>T:Avalonia.Themes.Fluent.ColorPaletteResources</Target>
+    <Left>baseline/netstandard2.0/Avalonia.Themes.Fluent.dll</Left>
+    <Right>target/netstandard2.0/Avalonia.Themes.Fluent.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:Avalonia.Themes.Fluent.ColorPaletteResources</Target>
+    <Left>baseline/netstandard2.0/Avalonia.Themes.Fluent.dll</Left>
+    <Right>target/netstandard2.0/Avalonia.Themes.Fluent.dll</Right>
+  </Suppression>
+</Suppressions>

+ 1 - 1
src/Avalonia.Base/Controls/ResourceProvider.cs

@@ -7,7 +7,7 @@ namespace Avalonia.Controls;
 /// Base implementation for IResourceProvider interface.
 /// Includes Owner property management.
 /// </summary>
-public abstract class ResourceProvider : IResourceProvider
+public abstract class ResourceProvider : AvaloniaObject, IResourceProvider
 {
     private IResourceHost? _owner;
 

+ 5 - 4
src/Avalonia.Themes.Fluent/ColorPaletteResources.cs

@@ -14,13 +14,13 @@ namespace Avalonia.Themes.Fluent;
 /// <remarks>
 /// This class can only be used in <see cref="FluentTheme.Palettes"/>.
 /// </remarks>
-public partial class ColorPaletteResources : AvaloniaObject, IResourceNode
+public partial class ColorPaletteResources : ResourceProvider
 {
     private readonly Dictionary<string, Color> _colors = new(StringComparer.InvariantCulture);
 
-    public bool HasResources => _hasAccentColor || _colors.Count > 0;
+    public override bool HasResources => _hasAccentColor || _colors.Count > 0;
 
-    public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
+    public override bool TryGetResource(object key, ThemeVariant? theme, out object? value)
     {
         if (key is string strKey)
         {
@@ -86,7 +86,7 @@ public partial class ColorPaletteResources : AvaloniaObject, IResourceNode
 
         return default;
     }
-    
+
     private void SetColor(string key, Color value)
     {
         if (value == default)
@@ -113,6 +113,7 @@ public partial class ColorPaletteResources : AvaloniaObject, IResourceNode
                         _accentColorLight1, _accentColorLight2, _accentColorLight3) =
                     SystemAccentColors.CalculateAccentShades(_accentColor);
             }
+            RaiseResourcesChanged();
         }
     }
 }

+ 97 - 22
src/Avalonia.Themes.Fluent/ColorPaletteResourcesCollection.cs

@@ -1,20 +1,26 @@
 using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using Avalonia.Collections;
 using Avalonia.Controls;
 using Avalonia.Styling;
 
 namespace Avalonia.Themes.Fluent;
 
-internal class ColorPaletteResourcesCollection : AvaloniaDictionary<ThemeVariant, ColorPaletteResources>, IResourceProvider
+internal sealed class ColorPaletteResourcesCollection : ResourceProvider, IDictionary<ThemeVariant, ColorPaletteResources>
 {
-    public ColorPaletteResourcesCollection() : base(2)
+    private readonly AvaloniaDictionary<ThemeVariant, ColorPaletteResources> _inner;
+
+    public ColorPaletteResourcesCollection()
     {
-        this.ForEachItem(
+        _inner = new AvaloniaDictionary<ThemeVariant, ColorPaletteResources>(2);
+        _inner.ForEachItem(
             (key, x) =>
             {
                 if (Owner is not null)
                 {
-                    x.PropertyChanged += Palette_PropertyChanged;
+                    ((IResourceProvider)x).AddOwner(Owner);
                 }
 
                 if (key != ThemeVariant.Dark && key != ThemeVariant.Light)
@@ -27,21 +33,21 @@ internal class ColorPaletteResourcesCollection : AvaloniaDictionary<ThemeVariant
             {
                 if (Owner is not null)
                 {
-                    x.PropertyChanged -= Palette_PropertyChanged;
+                    ((IResourceProvider)x).RemoveOwner(Owner);
                 }
             },
             () => throw new NotSupportedException("Dictionary reset not supported"));
     }
 
-    public bool HasResources => Count > 0;
-    public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
+    public override bool HasResources => _inner.Count > 0;
+    public override bool TryGetResource(object key, ThemeVariant? theme, out object? value)
     {
         if (theme == null || theme == ThemeVariant.Default)
         {
             theme = ThemeVariant.Light;
         }
 
-        if (base.TryGetValue(theme, out var themePaletteResources)
+        if (_inner.TryGetValue(theme, out var themePaletteResources)
             && themePaletteResources.TryGetResource(key, theme, out value))
         {
             return true;
@@ -51,25 +57,94 @@ internal class ColorPaletteResourcesCollection : AvaloniaDictionary<ThemeVariant
         return false;
     }
 
-    public IResourceHost? Owner { get; private set; }
-    public event EventHandler? OwnerChanged;
-    public void AddOwner(IResourceHost owner)
+    protected override void OnAddOwner(IResourceHost owner)
     {
-        Owner = owner;
-        OwnerChanged?.Invoke(this, EventArgs.Empty);
+        base.OnAddOwner(owner);
+        foreach (var palette in _inner.Values)
+        {
+            ((IResourceProvider)palette).AddOwner(owner);
+        }
     }
 
-    public void RemoveOwner(IResourceHost owner)
+    protected override void OnRemoveOwner(IResourceHost owner)
     {
-        Owner = null;
-        OwnerChanged?.Invoke(this, EventArgs.Empty);
-    }
-            
-    private void Palette_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
-    {
-        if (e.Property == ColorPaletteResources.AccentProperty)
+        base.OnRemoveOwner(owner);
+        foreach (var palette in _inner.Values)
         {
-            Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty);
+            ((IResourceProvider)palette).RemoveOwner(owner);
         }
     }
+
+    IEnumerator<KeyValuePair<ThemeVariant, ColorPaletteResources>> IEnumerable<KeyValuePair<ThemeVariant, ColorPaletteResources>>.GetEnumerator()
+    {
+        return _inner.GetEnumerator();
+    }
+
+    IEnumerator IEnumerable.GetEnumerator()
+    {
+        return ((IEnumerable)_inner).GetEnumerator();
+    }
+
+    void ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>.Add(KeyValuePair<ThemeVariant, ColorPaletteResources> item)
+    {
+        ((ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>)_inner).Add(item);
+    }
+
+    void ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>.Clear()
+    {
+        _inner.Clear();
+    }
+
+    bool ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>.Contains(KeyValuePair<ThemeVariant, ColorPaletteResources> item)
+    {
+        return ((ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>)_inner).Contains(item);
+    }
+
+    void ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>.CopyTo(KeyValuePair<ThemeVariant, ColorPaletteResources>[] array, int arrayIndex)
+    {
+        _inner.CopyTo(array, arrayIndex);
+    }
+
+    bool ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>.Remove(KeyValuePair<ThemeVariant, ColorPaletteResources> item)
+    {
+        return ((ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>)_inner).Remove(item);
+    }
+
+    int ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>.Count => _inner.Count;
+
+    bool ICollection<KeyValuePair<ThemeVariant, ColorPaletteResources>>.IsReadOnly => _inner.IsReadOnly;
+
+    void IDictionary<ThemeVariant, ColorPaletteResources>.Add(ThemeVariant key, ColorPaletteResources value)
+    {
+        _inner.Add(key, value);
+    }
+
+    bool IDictionary<ThemeVariant, ColorPaletteResources>.ContainsKey(ThemeVariant key)
+    {
+        return _inner.ContainsKey(key);
+    }
+
+    bool IDictionary<ThemeVariant, ColorPaletteResources>.Remove(ThemeVariant key)
+    {
+        return _inner.Remove(key);
+    }
+
+    bool IDictionary<ThemeVariant, ColorPaletteResources>.TryGetValue(ThemeVariant key,
+#if NET6_0_OR_GREATER
+        [MaybeNullWhen(false)]
+#endif
+        out ColorPaletteResources value)
+    {
+        return _inner.TryGetValue(key, out value);
+    }
+
+    ColorPaletteResources IDictionary<ThemeVariant, ColorPaletteResources>.this[ThemeVariant key]
+    {
+        get => _inner[key];
+        set => _inner[key] = value;
+    }
+
+    ICollection<ThemeVariant> IDictionary<ThemeVariant, ColorPaletteResources>.Keys => _inner.Keys;
+
+    ICollection<ColorPaletteResources> IDictionary<ThemeVariant, ColorPaletteResources>.Values => _inner.Values;
 }

+ 1 - 1
src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs

@@ -54,7 +54,7 @@ namespace Avalonia.Themes.Fluent
             get => _densityStyle;
             set => SetAndRaise(DensityStyleProperty, ref _densityStyle, value);
         }
-        
+
         public IDictionary<ThemeVariant, ColorPaletteResources> Palettes { get; }
 
         protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)

+ 3 - 10
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs

@@ -991,18 +991,11 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
         }
     }
 
-    public class TrackingResourceProvider : IResourceProvider
+    public class TrackingResourceProvider : ResourceProvider
     {
-        public IResourceHost Owner { get; private set; }
-        public bool HasResources => true;
+        public override bool HasResources => true;
         public List<object> RequestedResources { get; } = new List<object>();
-
-        public event EventHandler OwnerChanged { add { } remove { } }
-
-        public void AddOwner(IResourceHost owner) => Owner = owner;
-        public void RemoveOwner(IResourceHost owner) => Owner = null;
-
-        public bool TryGetResource(object key, ThemeVariant themeVariant, out object value)
+        public override bool TryGetResource(object key, ThemeVariant themeVariant, out object value)
         {
             RequestedResources.Add(key);
             value = key;

+ 4 - 16
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/ResourceIncludeTests.cs

@@ -127,25 +127,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
     }
     
     // See https://github.com/AvaloniaUI/Avalonia/issues/11172
-    public class LocaleCollection : IResourceProvider
+    public class LocaleCollection : ResourceProvider
     {
         private readonly Dictionary<object, IResourceProvider> _langs = new();
+        
+        public override bool HasResources => true;
 
-        public IResourceHost? Owner { get; private set; }
-
-        public bool HasResources => true;
-
-        public event EventHandler? OwnerChanged
-        {
-            add { }
-            remove { }
-        }
-
-        public void AddOwner(IResourceHost owner) => Owner = owner;
-
-        public void RemoveOwner(IResourceHost owner) => Owner = null;
-
-        public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
+        public override bool TryGetResource(object key, ThemeVariant? theme, out object? value)
         {
             if (_langs.TryGetValue("English", out var res))
             {