* Implement AvaloniaObject on ResourceProvider

* ColorPaletteResources and ColorPaletteResourcesCollection should inherit ResourceProvider

* Use ResourceProvider in tests too

* Adjust API diff

---------

Co-authored-by: Steven Kirk <grokys@users.noreply.github.com>
This commit is contained in:
Max Katz 2024-09-12 16:07:13 -07:00 коммит произвёл GitHub
Родитель 932f54316b
Коммит c50e731d8d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
7 изменённых файлов: 130 добавлений и 57 удалений

Просмотреть файл

@ -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>

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -1,20 +1,26 @@
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Styling; using Avalonia.Styling;
namespace Avalonia.Themes.Fluent; 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) => (key, x) =>
{ {
if (Owner is not null) if (Owner is not null)
{ {
x.PropertyChanged += Palette_PropertyChanged; ((IResourceProvider)x).AddOwner(Owner);
} }
if (key != ThemeVariant.Dark && key != ThemeVariant.Light) if (key != ThemeVariant.Dark && key != ThemeVariant.Light)
@ -27,21 +33,21 @@ internal class ColorPaletteResourcesCollection : AvaloniaDictionary<ThemeVariant
{ {
if (Owner is not null) if (Owner is not null)
{ {
x.PropertyChanged -= Palette_PropertyChanged; ((IResourceProvider)x).RemoveOwner(Owner);
} }
}, },
() => throw new NotSupportedException("Dictionary reset not supported")); () => throw new NotSupportedException("Dictionary reset not supported"));
} }
public bool HasResources => Count > 0; public override bool HasResources => _inner.Count > 0;
public bool TryGetResource(object key, ThemeVariant? theme, out object? value) public override bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{ {
if (theme == null || theme == ThemeVariant.Default) if (theme == null || theme == ThemeVariant.Default)
{ {
theme = ThemeVariant.Light; theme = ThemeVariant.Light;
} }
if (base.TryGetValue(theme, out var themePaletteResources) if (_inner.TryGetValue(theme, out var themePaletteResources)
&& themePaletteResources.TryGetResource(key, theme, out value)) && themePaletteResources.TryGetResource(key, theme, out value))
{ {
return true; return true;
@ -51,25 +57,94 @@ internal class ColorPaletteResourcesCollection : AvaloniaDictionary<ThemeVariant
return false; return false;
} }
public IResourceHost? Owner { get; private set; } protected override void OnAddOwner(IResourceHost owner)
public event EventHandler? OwnerChanged;
public void AddOwner(IResourceHost owner)
{ {
Owner = owner; base.OnAddOwner(owner);
OwnerChanged?.Invoke(this, EventArgs.Empty); foreach (var palette in _inner.Values)
}
public void RemoveOwner(IResourceHost owner)
{
Owner = null;
OwnerChanged?.Invoke(this, EventArgs.Empty);
}
private void Palette_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
if (e.Property == ColorPaletteResources.AccentProperty)
{ {
Owner?.NotifyHostedResourcesChanged(ResourcesChangedEventArgs.Empty); ((IResourceProvider)palette).AddOwner(owner);
} }
} }
protected override void OnRemoveOwner(IResourceHost owner)
{
base.OnRemoveOwner(owner);
foreach (var palette in _inner.Values)
{
((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;
} }

Просмотреть файл

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

Просмотреть файл

@ -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 override bool HasResources => true;
public bool HasResources => true;
public List<object> RequestedResources { get; } = new List<object>(); public List<object> RequestedResources { get; } = new List<object>();
public override bool TryGetResource(object key, ThemeVariant themeVariant, out object value)
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)
{ {
RequestedResources.Add(key); RequestedResources.Add(key);
value = key; value = key;

Просмотреть файл

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