Browse Source

feat: x:Shared (#16644)

* feat: x:Shared

* test: Add x:Shared test

* fix: Address review

* fix: Address review

* fix: Address review
pull/16712/head
workgroupengineering 2 years ago
committed by GitHub
parent
commit
228ecc5003
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 16
      src/Avalonia.Base/Controls/ResourceDictionary.cs
  2. 45
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs
  3. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  4. 54
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XSharedDirectiveTests.cs

16
src/Avalonia.Base/Controls/ResourceDictionary.cs

@ -147,6 +147,9 @@ namespace Avalonia.Controls
public void AddDeferred(object key, IDeferredContent deferredContent)
=> Add(key, deferredContent);
public void AddNotSharedDeferred(object key, IDeferredContent deferredContent)
=> Add(key, new NotSharedDeferredItem(deferredContent));
public void Clear()
{
if (_inner?.Count > 0)
@ -236,12 +239,16 @@ namespace Avalonia.Controls
try
{
_lastDeferredItemKey = key;
_inner[key] = value = deferred.Build(null) switch
value = deferred.Build(null) switch
{
ITemplateResult t => t.Result,
{ } v => v,
_ => null,
};
if (deferred is not NotSharedDeferredItem)
{
_inner[key] = value;
}
}
finally
{
@ -250,7 +257,6 @@ namespace Avalonia.Controls
}
return true;
}
value = null;
return false;
}
@ -376,5 +382,11 @@ namespace Avalonia.Controls
public DeferredItem(Func<IServiceProvider?, object?> factory) => _factory = factory;
public object? Build(IServiceProvider? serviceProvider) => _factory(serviceProvider);
}
private sealed class NotSharedDeferredItem(IDeferredContent deferredContent) : IDeferredContent
{
private readonly IDeferredContent _deferredContent = deferredContent ;
public object? Build(IServiceProvider? serviceProvider) => _deferredContent.Build(serviceProvider);
}
}
}

45
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDeferredResourceTransformer.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Visitors;
using XamlX;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
@ -22,23 +23,61 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
if (pa.Property.DeclaringType == types.ResourceDictionary && pa.Property.Name == "Content"
&& ShouldBeDeferred(pa.Values[1]))
{
IXamlMethod addMethod = TryGetSharedValue(pa.Values[1], out var isShared) && !isShared
? types.ResourceDictionaryNotSharedDeferredAdd
: types.ResourceDictionaryDeferredAdd;
pa.Values[1] = new XamlDeferredContentNode(pa.Values[1], types.XamlIlTypes.Object, context.Configuration);
pa.PossibleSetters = new List<IXamlPropertySetter>
{
new XamlDirectCallPropertySetter(types.ResourceDictionaryDeferredAdd),
new XamlDirectCallPropertySetter(addMethod),
};
}
else if (pa.Property.Name == "Resources" && pa.Property.Getter?.ReturnType.Equals(types.IResourceDictionary) == true
&& ShouldBeDeferred(pa.Values[1]))
{
IXamlMethod addMethod = TryGetSharedValue(pa.Values[1], out var isShared) && !isShared
? types.ResourceDictionaryNotSharedDeferredAdd
: types.ResourceDictionaryDeferredAdd;
pa.Values[1] = new XamlDeferredContentNode(pa.Values[1], types.XamlIlTypes.Object, context.Configuration);
pa.PossibleSetters = new List<IXamlPropertySetter>
{
new AdderSetter(pa.Property.Getter, types.ResourceDictionaryDeferredAdd),
new AdderSetter(pa.Property.Getter, addMethod),
};
}
return node;
bool TryGetSharedValue(IXamlAstValueNode valueNode, out bool value)
{
value = default;
if (valueNode is XamlAstConstructableObjectNode co)
{
// Try find x:Share directive
if (co.Children.Find(d => d is XamlAstXmlDirective { Namespace: XamlNamespaces.Xaml2006, Name: "Shared" }) is XamlAstXmlDirective sharedDirective)
{
if (sharedDirective.Values.Count == 1 && sharedDirective.Values[0] is XamlAstTextNode text)
{
if (bool.TryParse(text.Text, out var parseValue))
{
// If the parser succeeds, remove the x:Share directive
co.Children.Remove(sharedDirective);
return true;
}
else
{
context.ReportTransformError("Invalid argument type for x:Shared directive.", node);
}
}
else
{
context.ReportTransformError("Invalid number of arguments for x:Shared directive.", node);
}
}
}
return false;
}
}
private static bool ShouldBeDeferred(IXamlAstValueNode node)
@ -71,7 +110,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
return true;
}
class AdderSetter : IXamlILOptimizedEmitablePropertySetter, IEquatable<AdderSetter>
{
private readonly IXamlMethod _getter;

4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -118,6 +118,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType IResourceDictionary { get; }
public IXamlType ResourceDictionary { get; }
public IXamlMethod ResourceDictionaryDeferredAdd { get; }
public IXamlMethod ResourceDictionaryNotSharedDeferredAdd { get; }
public IXamlMethod ResourceDictionaryEnsureCapacity { get; }
public IXamlMethod ResourceDictionaryGetCount { get; }
public IXamlType IThemeVariantProvider { get; }
@ -312,6 +313,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
ResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.ResourceDictionary");
ResourceDictionaryDeferredAdd = ResourceDictionary.GetMethod("AddDeferred", XamlIlTypes.Void, true, XamlIlTypes.Object,
cfg.TypeSystem.GetType("Avalonia.Controls.IDeferredContent"));
ResourceDictionaryNotSharedDeferredAdd = ResourceDictionary.GetMethod("AddNotSharedDeferred", XamlIlTypes.Void, true, XamlIlTypes.Object,
cfg.TypeSystem.GetType("Avalonia.Controls.IDeferredContent"));
ResourceDictionaryEnsureCapacity = ResourceDictionary.GetMethod("EnsureCapacity", XamlIlTypes.Void, true, XamlIlTypes.Int32);
ResourceDictionaryGetCount = ResourceDictionary.GetMethod("get_Count", XamlIlTypes.Int32, true);
IThemeVariantProvider = cfg.TypeSystem.GetType("Avalonia.Controls.IThemeVariantProvider");

54
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XSharedDirectiveTests.cs

@ -0,0 +1,54 @@
using Avalonia.Controls;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml;
public class XSharedDirectiveTests : XamlTestBase
{
[Fact]
public void Should_Create_New_Instance_Where_x_Share_Is_False()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
const string xaml = $$"""
<Window xmlns="https://github.com/avaloniaui"
xmlns:sys="clr-namespace:System;assembly=netstandard"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<ColumnDefinitions x:Key="InplicitSharedResource">
<ColumnDefinition Width="150" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="Auto" />
</ColumnDefinitions>
<ColumnDefinitions x:Key="NotSharedResource"
x:Shared="false">
<ColumnDefinition Width="150" />
<ColumnDefinition Width="10" />
<ColumnDefinition Width="Auto" />
</ColumnDefinitions>
</Window.Resources>
</Window>
""";
var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
window.ApplyTemplate();
var implicitSharedInstance1 = window.FindResource("ImplicitSharedResource");
Assert.NotNull(implicitSharedInstance1);
var implicitSharedInstance2 = window.FindResource("ImplicitSharedResource");
Assert.NotNull(implicitSharedInstance2);
Assert.Same(implicitSharedInstance1, implicitSharedInstance2);
var notSharedResource1 = window.FindResource("NotSharedResource");
Assert.NotNull(notSharedResource1);
var notSharedResource2 = window.FindResource("NotSharedResource");
Assert.NotNull(notSharedResource2);
Assert.NotSame(notSharedResource1, notSharedResource2);
Assert.Equal(notSharedResource1.ToString(), notSharedResource2.ToString());
}
}
}
Loading…
Cancel
Save