From a96fb540ff03819f9dad48826dc88621942f2e8e Mon Sep 17 00:00:00 2001 From: Max Katz Date: Sun, 22 May 2022 01:49:16 -0400 Subject: [PATCH] Update IResourceNode interface --- samples/IntegrationTestApp/App.axaml | 2 +- samples/Sandbox/App.axaml | 2 +- .../Controls/IResourceDictionary.cs | 3 + src/Avalonia.Base/Controls/IResourceNode.cs | 8 +-- .../Controls/ResourceDictionary.cs | 32 ++------- .../Controls/ResourceNodeExtensions.cs | 66 ++++++++++++------- src/Avalonia.Base/StyledElement.cs | 13 +--- src/Avalonia.Base/Styling/Style.cs | 13 ++-- src/Avalonia.Base/Styling/Styles.cs | 25 +------ src/Avalonia.Base/Themes/ElementTheme.cs | 2 +- src/Avalonia.Controls/Application.cs | 13 +--- .../ResourceSelectorConverter.cs | 2 +- .../MarkupExtensions/ResourceInclude.cs | 17 +---- .../StaticResourceExtension.cs | 6 +- .../Styling/StyleInclude.cs | 15 +---- .../Styling/StylesTests.cs | 2 +- .../Themes/ThemeBenchmark.cs | 4 +- .../DynamicResourceExtensionTests.cs | 9 +-- .../Xaml/BasicTests.cs | 4 +- 19 files changed, 85 insertions(+), 153 deletions(-) diff --git a/samples/IntegrationTestApp/App.axaml b/samples/IntegrationTestApp/App.axaml index a833e096dfe1..eb34222d1409 100644 --- a/samples/IntegrationTestApp/App.axaml +++ b/samples/IntegrationTestApp/App.axaml @@ -2,6 +2,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="IntegrationTestApp.App"> - + diff --git a/samples/Sandbox/App.axaml b/samples/Sandbox/App.axaml index f601f9f78fd4..cf3e5e445ab2 100644 --- a/samples/Sandbox/App.axaml +++ b/samples/Sandbox/App.axaml @@ -3,6 +3,6 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Sandbox.App"> - + diff --git a/src/Avalonia.Base/Controls/IResourceDictionary.cs b/src/Avalonia.Base/Controls/IResourceDictionary.cs index ddfaae89e7e5..7f9c7f43179e 100644 --- a/src/Avalonia.Base/Controls/IResourceDictionary.cs +++ b/src/Avalonia.Base/Controls/IResourceDictionary.cs @@ -14,6 +14,9 @@ public interface IResourceDictionary : IResourceProvider, IDictionary IList MergedDictionaries { get; } + /// + /// Gets a collection of merged resource dictionaries that are specifically keyed and composed to address theme scenarios. + /// IDictionary ThemeDictionaries { get; } } } diff --git a/src/Avalonia.Base/Controls/IResourceNode.cs b/src/Avalonia.Base/Controls/IResourceNode.cs index 971dbdc345e7..6d5715386947 100644 --- a/src/Avalonia.Base/Controls/IResourceNode.cs +++ b/src/Avalonia.Base/Controls/IResourceNode.cs @@ -1,5 +1,4 @@ -using System; -using Avalonia.Metadata; +using Avalonia.Metadata; namespace Avalonia.Controls { @@ -23,6 +22,7 @@ public interface IResourceNode /// Tries to find a resource within the object. /// /// The resource key. + /// Theme used to select theme dictionary. /// /// When this method returns, contains the value associated with the specified key, /// if the key is found; otherwise, null. @@ -30,8 +30,6 @@ public interface IResourceNode /// /// True if the resource if found, otherwise false. /// - bool TryGetResource(object key, out object? value); - - bool TryGetResource(ElementTheme theme, object key, out object? value); + bool TryGetResource(object key, ElementTheme? theme, out object? value); } } diff --git a/src/Avalonia.Base/Controls/ResourceDictionary.cs b/src/Avalonia.Base/Controls/ResourceDictionary.cs index 2333993afdf1..0d51c4476baa 100644 --- a/src/Avalonia.Base/Controls/ResourceDictionary.cs +++ b/src/Avalonia.Base/Controls/ResourceDictionary.cs @@ -132,44 +132,24 @@ bool IResourceNode.HasResources public event EventHandler? OwnerChanged; - public bool TryGetResource(object key, out object? value) + /// + public bool TryGetResource(object key, ElementTheme? theme, out object? value) { if (TryGetValue(key, out value)) { return true; } - if (_mergedDictionaries != null) - { - for (var i = _mergedDictionaries.Count - 1; i >= 0; --i) - { - if (_mergedDictionaries[i].TryGetResource(key, out value)) - { - return true; - } - } - } - - return false; - } - - public bool TryGetResource(ElementTheme theme, object key, out object? value) - { - if (TryGetValue(key, out value)) - { - return true; - } - - if (_themeDictionary is not null) + if (_themeDictionary is not null && theme is not null) { if (_themeDictionary.TryGetValue(theme, out var themeResourceProvider) - && themeResourceProvider.TryGetResource(key, out value)) + && themeResourceProvider.TryGetResource(key, theme, out value)) { return true; } if (theme.InheritTheme is {} themeInherit && _themeDictionary.TryGetValue(themeInherit, out themeResourceProvider) - && themeResourceProvider.TryGetResource(key, out value)) + && themeResourceProvider.TryGetResource(key, theme, out value)) { return true; } @@ -179,7 +159,7 @@ public bool TryGetResource(ElementTheme theme, object key, out object? value) { for (var i = _mergedDictionaries.Count - 1; i >= 0; --i) { - if (_mergedDictionaries[i].TryGetResource(theme, key, out value)) + if (_mergedDictionaries[i].TryGetResource(key, theme, out value)) { return true; } diff --git a/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs index 84e3f742546b..3858dc8d90e8 100644 --- a/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs +++ b/src/Avalonia.Base/Controls/ResourceNodeExtensions.cs @@ -1,5 +1,4 @@ using System; -using Avalonia.LogicalTree; using Avalonia.Reactive; #nullable enable @@ -39,38 +38,51 @@ public static bool TryFindResource(this IResourceHost control, object key, out o control = control ?? throw new ArgumentNullException(nameof(control)); key = key ?? throw new ArgumentNullException(nameof(key)); - IResourceNode? current = control; + var theme = AvaloniaLocator.Current.GetService()?.Theme; - while (current != null) - { - if (current.TryGetResource(key, out value)) - { - return true; - } + return control.TryFindResource(key, theme, out value); + } + + /// + /// Finds the specified resource by searching up the logical tree and then global styles. + /// + /// The control. + /// Theme used to select theme dictionary. + /// The resource key. + /// The resource, or if not found. + public static object? FindResource(this IResourceHost control, ElementTheme? theme, object key) + { + control = control ?? throw new ArgumentNullException(nameof(control)); + key = key ?? throw new ArgumentNullException(nameof(key)); - current = (current as IStyledElement)?.StylingParent as IResourceNode; + if (control.TryFindResource(key, theme, out var value)) + { + return value; } - value = null; - return false; + return AvaloniaProperty.UnsetValue; } - - public static bool TryFindThemeResource(this IResourceHost control, ElementTheme theme, object key, out object? value) + + /// + /// Tries to the specified resource by searching up the logical tree and then global styles. + /// + /// The control. + /// The resource key. + /// Theme used to select theme dictionary. + /// On return, contains the resource if found, otherwise null. + /// True if the resource was found; otherwise false. + public static bool TryFindResource(this IResourceHost control, object key, ElementTheme? theme, out object? value) { control = control ?? throw new ArgumentNullException(nameof(control)); - theme = theme ?? throw new ArgumentNullException(nameof(theme)); key = key ?? throw new ArgumentNullException(nameof(key)); IResourceHost? current = control; while (current != null) { - if (current is IResourceHost host) + if (current.TryGetResource(key, theme, out value)) { - if (host.TryGetResource(theme, key, out value)) - { - return true; - } + return true; } current = (current as IStyledElement)?.StylingParent as IResourceHost; @@ -79,6 +91,17 @@ public static bool TryFindThemeResource(this IResourceHost control, ElementTheme value = null; return false; } + + /// + public static bool TryGetResource(this IResourceHost control, object key, out object? value) + { + control = control ?? throw new ArgumentNullException(nameof(control)); + key = key ?? throw new ArgumentNullException(nameof(key)); + + var theme = AvaloniaLocator.Current.GetService()?.Theme; + + return control.TryGetResource(key, theme, out value); + } public static IObservable GetResourceObservable( this IResourceHost control, @@ -151,8 +174,7 @@ private void ThemeChanged(object? sender, EventArgs e) private object? GetValue() { if (!(_target is IThemeStyleable themeStyleable) - || themeStyleable.Theme is null - || !themeStyleable.TryFindThemeResource(themeStyleable.Theme, _key, out var value)) + || !themeStyleable.TryFindResource(_key, themeStyleable.Theme, out var value)) { value = _target.FindResource(_key) ?? AvaloniaProperty.UnsetValue; } @@ -242,7 +264,7 @@ private void ThemeChanged(object? sender, EventArgs e) { if (!(_target.Owner is IThemeStyleable themeStyleable) || themeStyleable.Theme is null - || !themeStyleable.TryFindThemeResource(themeStyleable.Theme, _key, out var value)) + || !themeStyleable.TryFindResource(_key, themeStyleable.Theme, out var value)) { value = _target.Owner?.FindResource(_key) ?? AvaloniaProperty.UnsetValue; } diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index e13367909f9f..6b9f779df73b 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -396,18 +396,11 @@ void ILogical.NotifyDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) void IResourceHost.NotifyHostedResourcesChanged(ResourcesChangedEventArgs e) => NotifyResourcesChanged(e); /// - bool IResourceNode.TryGetResource(object key, out object? value) + public bool TryGetResource(object key, ElementTheme? theme, out object? value) { value = null; - return (_resources?.TryGetResource(key, out value) ?? false) || - (_styles?.TryGetResource(key, out value) ?? false); - } - - public bool TryGetResource(ElementTheme theme, object key, out object? value) - { - value = null; - return (_resources?.TryGetResource(theme, key, out value) ?? false) || - (_styles?.TryGetResource(theme, key, out value) ?? false); + return (_resources?.TryGetResource(key, theme, out value) ?? false) || + (_styles?.TryGetResource(key, theme, out value) ?? false); } /// diff --git a/src/Avalonia.Base/Styling/Style.cs b/src/Avalonia.Base/Styling/Style.cs index 2ee9c60d47b9..b99cd3337aa2 100644 --- a/src/Avalonia.Base/Styling/Style.cs +++ b/src/Avalonia.Base/Styling/Style.cs @@ -110,17 +110,12 @@ public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) return match.Result; } - - public bool TryGetResource(object key, out object? result) - { - result = null; - return _resources?.TryGetResource(key, out result) ?? false; - } - - public bool TryGetResource(ElementTheme theme, object key, out object? result) + + /// + public bool TryGetResource(object key, ElementTheme? theme, out object? result) { result = null; - return _resources?.TryGetResource(theme, key, out result) ?? false; + return _resources?.TryGetResource(key, theme, out result) ?? false; } /// diff --git a/src/Avalonia.Base/Styling/Styles.cs b/src/Avalonia.Base/Styling/Styles.cs index fc5bf7dbc04b..5b5248d75677 100644 --- a/src/Avalonia.Base/Styling/Styles.cs +++ b/src/Avalonia.Base/Styling/Styles.cs @@ -151,35 +151,16 @@ public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) } /// - public bool TryGetResource(object key, out object? value) + public bool TryGetResource(object key, ElementTheme? theme, out object? value) { - if (_resources != null && _resources.TryGetResource(key, out value)) + if (_resources != null && _resources.TryGetResource(key, theme, out value)) { return true; } for (var i = Count - 1; i >= 0; --i) { - if (this[i].TryGetResource(key, out value)) - { - return true; - } - } - - value = null; - return false; - } - - public bool TryGetResource(ElementTheme theme, object key, out object? value) - { - if (_resources != null && _resources.TryGetResource(theme, key, out value)) - { - return true; - } - - for (var i = Count - 1; i >= 0; --i) - { - if (this[i] is IResourceProvider p && p.TryGetResource(theme, key, out value)) + if (this[i] is IResourceProvider p && p.TryGetResource(key, theme, out value)) { return true; } diff --git a/src/Avalonia.Base/Themes/ElementTheme.cs b/src/Avalonia.Base/Themes/ElementTheme.cs index 99b03a358745..147ffae4582b 100644 --- a/src/Avalonia.Base/Themes/ElementTheme.cs +++ b/src/Avalonia.Base/Themes/ElementTheme.cs @@ -11,7 +11,7 @@ public ElementTheme(object key) Key = key ?? throw new ArgumentNullException(nameof(key)); } - public ElementTheme(object key, ElementTheme inheritTheme) + public ElementTheme(object key, ElementTheme? inheritTheme) { Key = key ?? throw new ArgumentNullException(nameof(key)); InheritTheme = inheritTheme; diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 09421e5bb8c8..cb89a7081629 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -210,18 +210,11 @@ event Action>? IGlobalStyles.GlobalStylesRemoved public virtual void Initialize() { } /// - bool IResourceNode.TryGetResource(object key, out object? value) + public bool TryGetResource(object key, ElementTheme? theme, out object? value) { value = null; - return (_resources?.TryGetResource(key, out value) ?? false) || - Styles.TryGetResource(key, out value); - } - - public bool TryGetResource(ElementTheme theme, object key, out object? value) - { - value = null; - return (_resources?.TryGetResource(theme, key, out value) ?? false) || - Styles.TryGetResource(theme, key, out value); + return (_resources?.TryGetResource(key, theme, out value) ?? false) || + Styles.TryGetResource(key, theme, out value); } void IResourceHost.NotifyHostedResourcesChanged(ResourcesChangedEventArgs e) diff --git a/src/Avalonia.Dialogs/ResourceSelectorConverter.cs b/src/Avalonia.Dialogs/ResourceSelectorConverter.cs index c8226b98e4af..69896fda6ad4 100644 --- a/src/Avalonia.Dialogs/ResourceSelectorConverter.cs +++ b/src/Avalonia.Dialogs/ResourceSelectorConverter.cs @@ -9,7 +9,7 @@ public class ResourceSelectorConverter : ResourceDictionary, IValueConverter { public object Convert(object key, Type targetType, object parameter, CultureInfo culture) { - TryGetResource((string)key, out var value); + TryGetResource((string)key, null, out var value); return value; } diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs index 9be2643bccd2..de7e9993a7c7 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs @@ -47,23 +47,12 @@ public event EventHandler OwnerChanged add => Loaded.OwnerChanged += value; remove => Loaded.OwnerChanged -= value; } - - bool IResourceNode.TryGetResource(object key, out object? value) - { - if (!_isLoading) - { - return Loaded.TryGetResource(key, out value); - } - - value = null; - return false; - } - - public bool TryGetResource(ElementTheme theme, object key, out object? value) + + public bool TryGetResource(object key, ElementTheme? theme, out object? value) { if (!_isLoading) { - return Loaded.TryGetResource(theme, key, out value); + return Loaded.TryGetResource(key, theme, out value); } value = null; diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs index 9c522e91a004..b7a314e71882 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StaticResourceExtension.cs @@ -46,11 +46,7 @@ public object ProvideValue(IServiceProvider serviceProvider) { if (parent is IResourceNode node) { - if (appTheme is not null && node.TryGetResource(appTheme, ResourceKey, out var value)) - { - return ColorToBrushConverter.Convert(value, targetType); - } - if (node.TryGetResource(ResourceKey, out value)) + if (node.TryGetResource(ResourceKey, appTheme, out var value)) { return ColorToBrushConverter.Convert(value, targetType); } diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index 708b42c5f908..c6b3cd952f6d 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -84,22 +84,11 @@ public event EventHandler OwnerChanged public SelectorMatchResult TryAttach(IStyleable target, IStyleHost? host) => Loaded.TryAttach(target, host); - public bool TryGetResource(object key, out object? value) + public bool TryGetResource(object key, ElementTheme? theme, out object? value) { if (!_isLoading) { - return Loaded.TryGetResource(key, out value); - } - - value = null; - return false; - } - - public bool TryGetResource(ElementTheme theme, object key, out object? value) - { - if (!_isLoading && Loaded is IResourceProvider p) - { - return p.TryGetResource(theme, key, out value); + return Loaded.TryGetResource(key, theme, out value); } value = null; diff --git a/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs index a6777c9466a6..f612fe4e8ef2 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs @@ -108,7 +108,7 @@ public void Finds_Resource_In_Merged_Dictionary() } }; - Assert.True(target.TryGetResource("foo", out var result)); + Assert.True(target.TryGetResource("foo", ElementTheme.Dark, out var result)); Assert.Equal("bar", result); } } diff --git a/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs index 8377931cf46d..9f3fd1cb2d5a 100644 --- a/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs +++ b/tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs @@ -30,14 +30,14 @@ public ThemeBenchmark() public bool InitFluentTheme() { UnitTestApplication.Current.Styles[0] = new FluentTheme(new Uri("resm:Styles?assembly=Avalonia.Benchmarks")); - return ((IResourceHost)UnitTestApplication.Current).TryGetResource(ElementTheme.Dark, "SystemAccentColor", out _); + return ((IResourceHost)UnitTestApplication.Current).TryGetResource("SystemAccentColor", ElementTheme.Dark, out _); } [Benchmark] public bool InitDefaultTheme() { UnitTestApplication.Current.Styles[0] = new SimpleTheme(new Uri("resm:Styles?assembly=Avalonia.Benchmarks")); - return ((IResourceHost)UnitTestApplication.Current).TryGetResource(ElementTheme.Dark, "ThemeAccentColor", out _); + return ((IResourceHost)UnitTestApplication.Current).TryGetResource("ThemeAccentColor", ElementTheme.Dark, out _); } public void Dispose() diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs index 79b4dbf45d69..59224c8d1593 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs @@ -928,14 +928,7 @@ 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, out object value) - { - RequestedResources.Add(key); - value = key; - return true; - } - - public bool TryGetResource(ElementTheme theme, object key, out object value) + public bool TryGetResource(object key, ElementTheme theme, out object value) { RequestedResources.Add(key); value = key; diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs index f12785a7ce75..fa79fa535262 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs @@ -510,13 +510,13 @@ public void Style_Resources_Are_Built() Assert.True(style.Resources.Count > 0); - style.TryGetResource("Brush", out var brush); + style.TryGetResource("Brush", null, out var brush); Assert.NotNull(brush); Assert.IsAssignableFrom(brush); Assert.Equal(Colors.White, ((ISolidColorBrush)brush).Color); - style.TryGetResource("Double", out var d); + style.TryGetResource("Double", null, out var d); Assert.Equal(10.0, d); }