Jelajahi Sumber

Add AvaloniaXamlIlSetterTargetTypeMetadataTransformer to allow SetterTargetType

Max Katz 3 tahun lalu
induk
melakukan
0c84ae68b9

+ 2 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Linq;
+
 using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
 using XamlX;
 using XamlX.Ast;
@@ -51,6 +52,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                 new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),                 
                 new AvaloniaXamlIlBindingPathParser(),
                 new AvaloniaXamlIlPropertyPathTransformer(),
+                new AvaloniaXamlIlSetterTargetTypeMetadataTransformer(),
                 new AvaloniaXamlIlSetterTransformer(),
                 new AvaloniaXamlIlConstructorServiceProviderTransformer(),
                 new AvaloniaXamlIlTransitionsTypeMetadataTransformer(),

+ 34 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTargetTypeMetadataTransformer.cs

@@ -0,0 +1,34 @@
+using System.Linq;
+using XamlX;
+using XamlX.Ast;
+using XamlX.Transform;
+using XamlX.Transform.Transformers;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+
+internal class AvaloniaXamlIlSetterTargetTypeMetadataTransformer : IXamlAstTransformer
+{
+    public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
+    {
+        if (node is XamlAstObjectNode on
+            && on.Children.FirstOrDefault(c => c is XamlAstXmlDirective
+            {
+                Namespace: XamlNamespaces.Xaml2006,
+                Name: "SetterTargetType"
+            }) is { } typeDirective)
+        {
+            var value = ((XamlAstXmlDirective)typeDirective).Values.Single();
+            var type = value is XamlTypeExtensionNode typeNode ? typeNode.Value
+                : value is XamlAstTextNode tn ? TypeReferenceResolver.ResolveType(context, tn.Text, false, tn, true)
+                : null;
+            on.Children.Remove(typeDirective);
+
+            if (type is null)
+            {
+                throw new XamlParseException("Unable to resolve SetterTargetType type", typeDirective);
+            }
+            return new AvaloniaXamlIlTargetTypeMetadataNode(on, type, AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
+        }
+        return node;
+    }
+}

+ 1 - 22
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSetterTransformer.cs

@@ -9,7 +9,6 @@ using XamlX.TypeSystem;
 
 namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
 {
-    using XamlParseException = XamlX.XamlParseException;
     class AvaloniaXamlIlSetterTransformer : IXamlAstTransformer
     {
         public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
@@ -28,29 +27,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             if (styleParent != null)
             {
                 targetType = styleParent.TargetType.GetClrType()
-                             ?? throw new XamlParseException("Can not resolve parent Style Selector type", node);
+                             ?? throw new XamlParseException("Can not resolve parent Style Selector type. If setter is not part of the style, you can set x:SetterTargetType directive on its parent.", node);
                 lineInfo = on;
             }
-            else
-            {
-                foreach (var p in context.ParentNodes().OfType<XamlAstObjectNode>())
-                {
-                    for (var index = 0; index < p.Children.Count; index++)
-                    {
-                        if (p.Children[index] is XamlAstXmlDirective d &&
-                            d.Namespace == XamlNamespaces.Xaml2006 &&
-                            d.Name == "SetterTargetType")
-                        {
-                            targetType = context.Configuration.TypeSystem.GetType(((XamlAstTextNode)d.Values[0]).Text);
-                            lineInfo = d;
-
-                            break;
-                        }
-                    }
-
-                    if (targetType != null) break;
-                }
-            }
 
             if (targetType == null)
             {

+ 33 - 14
tests/Avalonia.Markup.Xaml.UnitTests/SetterTests.cs

@@ -1,20 +1,19 @@
-using System.Linq;
-using Avalonia.Data;
+using Avalonia.Controls;
 using Avalonia.Styling;
 using Avalonia.UnitTests;
 using Xunit;
 
-namespace Avalonia.Markup.Xaml.UnitTests
+namespace Avalonia.Markup.Xaml.UnitTests;
+
+public class SetterTests : XamlTestBase
 {
-    public class SetterTests : XamlTestBase
+    [Fact]
+    public void SetterTargetType_Should_Understand_xType_Extensions()
     {
-        [Fact]
-        public void Setter_Should_Work_Outside_Of_Style_With_SetterTargetType_Attribute()
+        using (UnitTestApplication.Start(TestServices.StyledWindow))
         {
-            using (UnitTestApplication.Start(TestServices.StyledWindow))
-            {
-                var xaml = @"
-<Animation xmlns='https://github.com/avaloniaui' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' x:SetterTargetType='Avalonia.Controls.Button'>
+            var xaml = @"
+<Animation xmlns='https://github.com/avaloniaui' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' x:SetterTargetType='{x:Type ContentControl}'>
     <KeyFrame>
         <Setter Property='Content' Value='{Binding}'/>
     </KeyFrame>
@@ -22,11 +21,31 @@ namespace Avalonia.Markup.Xaml.UnitTests
         <Setter Property='Content' Value='{Binding}'/>
     </KeyFrame> 
 </Animation>";
-                var animation = (Animation.Animation)AvaloniaRuntimeXamlLoader.Load(xaml);
-                var setter = (Setter)animation.Children[0].Setters[0];
+            var animation = (Animation.Animation)AvaloniaRuntimeXamlLoader.Load(xaml);
+            var setter = (Setter)animation.Children[0].Setters[0];
+
+            Assert.Equal(typeof(ContentControl), setter.Property.OwnerType);
+        }
+    }
+
+    [Fact]
+    public void SetterTargetType_Should_Understand_Type_From_Xmlns()
+    {
+        using (UnitTestApplication.Start(TestServices.StyledWindow))
+        {
+            var xaml = @"
+<av:Animation xmlns:av='https://github.com/avaloniaui' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' x:SetterTargetType='av:ContentControl'>
+    <av:KeyFrame>
+        <av:Setter Property='Content' Value='{av:Binding}'/>
+    </av:KeyFrame>
+    <av:KeyFrame>
+        <av:Setter Property='Content' Value='{av:Binding}'/>
+    </av:KeyFrame> 
+</av:Animation>";
+            var animation = (Animation.Animation)AvaloniaRuntimeXamlLoader.Load(xaml);
+            var setter = (Setter)animation.Children[0].Setters[0];
 
-                Assert.IsType<Binding>(setter.Value);
-            }
+            Assert.Equal(typeof(ContentControl), setter.Property.OwnerType);
         }
     }
 }