Browse Source

Reimplement OnPlatform to inject IL code instead of markup extension

release/11.0.0-preview3
Max Katz 4 years ago
parent
commit
9e41a88f46
  1. 22
      samples/ControlCatalog/Pages/PlatformInfoPage.xaml
  2. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  3. 205
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs
  4. 88
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/OnFormFactorTransformer.cs
  5. 128
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/OnPlatformTransformer.cs
  6. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  7. 43
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs
  8. 78
      tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs

22
samples/ControlCatalog/Pages/PlatformInfoPage.xaml

@ -7,19 +7,19 @@
d:DesignWidth="400"
mc:Ignorable="d">
<StackPanel Spacing="20">
<TextBlock Text="{OnFormFactor Unknown, Mobile=Mobile, Desktop=Desktop}" />
<!-- <TextBlock Text="{OnFormFactor Unknown, Mobile=Mobile, Desktop=Desktop}" /> -->
<TextBlock Text="{Binding PlatformInfo}" />
<StackPanel TextElement.Foreground="White">
<StackPanel Orientation="Horizontal">
<Border Height="100" Width="100" Background="{OnFormFactor Gray, Desktop=Green}">
<TextBlock Text="Desktop" />
</Border>
<Border Height="100" Width="100" Background="{OnFormFactor Gray, Mobile=Green}">
<TextBlock Text="Mobile" />
</Border>
</StackPanel>
<!-- <StackPanel Orientation="Horizontal"> -->
<!-- <Border Height="100" Width="100" Background="{OnFormFactor Gray, Desktop=Green}"> -->
<!-- <TextBlock Text="Desktop" /> -->
<!-- </Border> -->
<!-- <Border Height="100" Width="100" Background="{OnFormFactor Gray, Mobile=Green}"> -->
<!-- <TextBlock Text="Mobile" /> -->
<!-- </Border> -->
<!-- </StackPanel> -->
<StackPanel Orientation="Horizontal">
<Border Height="100" Width="100" Background="{OnPlatform Gray, Windows=Green}">
<TextBlock Text="Windows" />

4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -56,11 +56,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlTransitionsTypeMetadataTransformer(),
new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer()
);
InsertAfter<ResolveContentPropertyTransformer>(
new AvaloniaXamlIlOnPlatformTransformer());
InsertBefore<TypeReferenceResolver>(
new OnFormFactorTransformer());
InsertBefore<TypeReferenceResolver>(
new OnPlatformTransformer());
InsertAfter<TypeReferenceResolver>(
new XDataTypeTransformer());

205
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlOnPlatformTransformer.cs

@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using XamlX;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.IL.Emitters;
using XamlX.Transform;
using XamlX.Transform.Transformers;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
internal class AvaloniaXamlIlOnPlatformTransformer : IXamlAstTransformer
{
private const string OnPlatformFqn = "Avalonia.Markup.Xaml:Avalonia.Markup.Xaml.MarkupExtensions.OnPlatformExtension";
private const string OnFqn = "Avalonia.Markup.Xaml:Avalonia.Markup.Xaml.MarkupExtensions.On";
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (node is XamlAstXamlPropertyValueNode targetPropertyNode
&& targetPropertyNode.Values.OfType<XamlMarkupExtensionNode>().FirstOrDefault() is
{
Value: XamlAstObjectNode { Type: XamlAstClrTypeReference { Type: { } type } } objectNode
}
&& type.GetFqn().StartsWith(OnPlatformFqn))
{
var typeArgument = type.GenericArguments?.FirstOrDefault();
var targetType = typeArgument ?? objectNode.Type.GetClrType();
if (targetType is null)
{
throw new XamlParseException(
"Unable to find OnPlatform property type. Try to set x:TypeArguments on the markup extension.",
node);
}
IXamlAstNode defaultValue = null;
var values = new Dictionary<string, IXamlAstNode>();
var directives = objectNode.Children.OfType<XamlAstXmlDirective>().ToArray();
foreach (var child in objectNode.Arguments.Take(1))
{
defaultValue = new XamlAstXamlPropertyValueNode(child, targetPropertyNode.Property, child);
}
foreach (var extProp in objectNode.Children.OfType<XamlAstXamlPropertyValueNode>())
{
var onObjs = extProp.Values.OfType<XamlAstObjectNode>()
.Where(o => o.Type.GetClrType().GetFqn() == OnFqn).ToArray();
if (onObjs.Any())
{
foreach (var onObj in onObjs)
{
var platformStr = (onObj.Children.OfType<XamlAstXamlPropertyValueNode>()
.SingleOrDefault(v => v.Property.GetClrProperty().Name == "Platform")
?.Values.Single() as XamlAstTextNode)?
.Text;
if (string.IsNullOrWhiteSpace(platformStr))
{
throw new XamlParseException("On.Platform string must be set", onObj);
}
var content = onObj.Children.OfType<XamlAstXamlPropertyValueNode>()
.SingleOrDefault(v => v.Property.GetClrProperty().Name == "Content");
if (content is null)
{
throw new XamlParseException("On content object must be set", onObj);
}
var transformed = TransformNode(targetPropertyNode.Property, content.Values,
typeArgument, directives, content);
foreach (var platform in platformStr.Split(new[] { ',' },
StringSplitOptions.RemoveEmptyEntries))
{
values.Add(platform.Trim().ToUpperInvariant(), transformed);
}
}
}
else
{
var platformStr = extProp.Property.GetClrProperty().Name.Trim().ToUpperInvariant();
var transformed = TransformNode(targetPropertyNode.Property, extProp.Values,
typeArgument, directives, extProp);
if (platformStr.Equals("default", StringComparison.OrdinalIgnoreCase))
{
defaultValue = transformed;
}
else
{
values.Add(platformStr, transformed);
}
}
}
return new XamlIlOnPlatformExtensionNode(
defaultValue, values,
new XamlAstClrTypeReference(node, targetType, false),
node);
}
return node;
XamlAstXamlPropertyValueNode TransformNode(
IXamlAstPropertyReference property,
IReadOnlyCollection<IXamlAstValueNode> values,
IXamlType suggestedType,
IReadOnlyCollection<XamlAstXmlDirective> directives,
IXamlLineInfo line)
{
if (suggestedType is not null)
{
values = values
.Select(v => XamlTransformHelpers
.TryGetCorrectlyTypedValue(context, v, suggestedType, out var converted)
? converted : v)
.ToArray();
}
if (directives.Any())
{
foreach (var value in values)
{
if (value is XamlAstObjectNode xamlAstObjectNode)
{
xamlAstObjectNode.Children.AddRange(directives);
}
}
}
return new XamlAstXamlPropertyValueNode(line, property, values);
}
}
private sealed class XamlIlOnPlatformExtensionNode : XamlAstNode, IXamlAstValueNode,
IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult>, IXamlAstManipulationNode
{
private IXamlAstNode _defaultValue;
private readonly IXamlAstNode[] _values;
private readonly string[] _valuePlatforms;
public XamlIlOnPlatformExtensionNode(
IXamlAstNode defaultValue,
IDictionary<string, IXamlAstNode> values,
IXamlAstTypeReference targetType,
IXamlLineInfo info) : base(info)
{
_defaultValue = defaultValue;
_values = values.Values.ToArray();
_valuePlatforms = values.Keys.ToArray();
Type = targetType;
}
public override void VisitChildren(IXamlAstVisitor visitor)
{
_defaultValue = _defaultValue?.Visit(visitor);
VisitList(_values, visitor);
}
public IXamlAstTypeReference Type { get; }
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context,
IXamlILEmitter codeGen)
{
var operatingSystemClass =
context.Configuration.TypeSystem.GetType(
"Avalonia.Markup.Xaml.MarkupExtensions.OnPlatformExtensionHelper");
var isOSPlatformMethod = operatingSystemClass
.FindMethod(m => m.IsStatic && m.Parameters.Count == 1 && m.Name == "IsOSPlatform");
var ret = codeGen.DefineLabel();
for (var index = 0; index < _valuePlatforms.Length; index++)
{
var platform = _valuePlatforms[index];
var propertyNode = _values[index];
var next = codeGen.DefineLabel();
codeGen.Ldstr(platform);
codeGen.EmitCall(isOSPlatformMethod);
codeGen.Brfalse(next);
context.Emit(propertyNode, codeGen, null);
codeGen.Br(ret);
codeGen.MarkLabel(next);
}
if (_defaultValue is not null)
{
codeGen.Emit(OpCodes.Nop);
context.Emit(_defaultValue, codeGen, null);
}
else
{
codeGen.Pop();
}
codeGen.MarkLabel(ret);
return XamlILNodeEmitResult.Void(1);
}
}
}

88
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/OnFormFactorTransformer.cs

@ -11,50 +11,50 @@ internal class OnFormFactorTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (node is XamlAstObjectNode xmlobj
&& xmlobj.Type is XamlAstXmlTypeReference xmlref
&& xmlref.Name.StartsWith("OnFormFactor")
&& !xmlref.GenericArguments.Any())
{
IXamlType propertyType = null;
if (context.ParentNodes().FirstOrDefault() is XamlAstXamlPropertyValueNode parentPropertyValueNode)
{
var property = (XamlAstNamePropertyReference)parentPropertyValueNode.Property;
var declaringType = TypeReferenceResolver.ResolveType(context, (XamlAstXmlTypeReference)property.DeclaringType, context.StrictMode);
propertyType = declaringType.Type.GetAllProperties().First(p => p.Name == property.Name).PropertyType;
}
else if (context.ParentNodes().FirstOrDefault() is XamlAstObjectNode parentNode)
{
var parentType = parentNode.Type is XamlAstClrTypeReference clrType
? clrType.Type
: TypeReferenceResolver.ResolveType(context, (XamlAstXmlTypeReference)parentNode.Type, context.StrictMode).Type;
var contentProperty = context.Configuration.FindContentProperty(parentType);
propertyType = contentProperty.PropertyType;
}
if (propertyType is null)
{
throw new InvalidOperationException("Unable to find OnFormFactor property type");
}
xmlobj.Type = TypeReferenceResolver.ResolveType(context, xmlref.XmlNamespace, xmlref.Name,
xmlref.IsMarkupExtension, new[] { new XamlAstClrTypeReference(xmlref, propertyType, false) },
xmlref, context.StrictMode);
}
if (node is XamlAstNamePropertyReference xmlprop
&& xmlprop.DeclaringType is XamlAstXmlTypeReference propxmlref
&& propxmlref.Name.StartsWith("OnFormFactor")
&& !propxmlref.GenericArguments.Any())
{
var expectedType = context.ParentNodes().OfType<XamlAstObjectNode>()
.First(n => n.Type is XamlAstClrTypeReference clrRef && clrRef.Type.Name.StartsWith("OnFormFactor")
|| n.Type is XamlAstXmlTypeReference xmlRef && xmlRef.Name.StartsWith("OnFormFactor"))
.Type;
xmlprop.DeclaringType = expectedType;
xmlprop.TargetType = expectedType;
}
// if (node is XamlAstObjectNode xmlobj
// && xmlobj.Type is XamlAstXmlTypeReference xmlref
// && xmlref.Name.StartsWith("OnFormFactor")
// && !xmlref.GenericArguments.Any())
// {
// IXamlType propertyType = null;
//
// if (context.ParentNodes().FirstOrDefault() is XamlAstXamlPropertyValueNode parentPropertyValueNode)
// {
// var property = (XamlAstNamePropertyReference)parentPropertyValueNode.Property;
// var declaringType = TypeReferenceResolver.ResolveType(context, (XamlAstXmlTypeReference)property.DeclaringType, context.StrictMode);
// propertyType = declaringType.Type.GetAllProperties().First(p => p.Name == property.Name).PropertyType;
// }
// else if (context.ParentNodes().FirstOrDefault() is XamlAstObjectNode parentNode)
// {
// var parentType = parentNode.Type is XamlAstClrTypeReference clrType
// ? clrType.Type
// : TypeReferenceResolver.ResolveType(context, (XamlAstXmlTypeReference)parentNode.Type, context.StrictMode).Type;
// var contentProperty = context.Configuration.FindContentProperty(parentType);
// propertyType = contentProperty.PropertyType;
// }
//
// if (propertyType is null)
// {
// throw new InvalidOperationException("Unable to find OnFormFactor property type");
// }
//
// xmlobj.Type = TypeReferenceResolver.ResolveType(context, xmlref.XmlNamespace, xmlref.Name,
// xmlref.IsMarkupExtension, new[] { new XamlAstClrTypeReference(xmlref, propertyType, false) },
// xmlref, context.StrictMode);
// }
//
// if (node is XamlAstNamePropertyReference xmlprop
// && xmlprop.DeclaringType is XamlAstXmlTypeReference propxmlref
// && propxmlref.Name.StartsWith("OnFormFactor")
// && !propxmlref.GenericArguments.Any())
// {
// var expectedType = context.ParentNodes().OfType<XamlAstObjectNode>()
// .First(n => n.Type is XamlAstClrTypeReference clrRef && clrRef.Type.Name.StartsWith("OnFormFactor")
// || n.Type is XamlAstXmlTypeReference xmlRef && xmlRef.Name.StartsWith("OnFormFactor"))
// .Type;
// xmlprop.DeclaringType = expectedType;
// xmlprop.TargetType = expectedType;
// }
// if (node is XamlAstObjectNode onobj
// && onobj.Type is XamlAstXmlTypeReference onref

128
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/OnPlatformTransformer.cs

@ -1,128 +0,0 @@
using System;
using System.Linq;
using XamlX.Ast;
using XamlX.Transform;
using XamlX.Transform.Transformers;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
internal class OnPlatformTransformer : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (node is XamlAstObjectNode xmlobj
&& xmlobj.Type is XamlAstXmlTypeReference xmlref
&& xmlref.Name.StartsWith("OnPlatform")
&& !xmlref.GenericArguments.Any())
{
IXamlType propertyType = null;
if (context.ParentNodes().FirstOrDefault() is XamlAstXamlPropertyValueNode parentPropertyValueNode)
{
var property = (XamlAstNamePropertyReference)parentPropertyValueNode.Property;
var declaringType = TypeReferenceResolver.ResolveType(context, (XamlAstXmlTypeReference)property.DeclaringType, context.StrictMode);
propertyType = declaringType.Type.GetAllProperties().First(p => p.Name == property.Name).PropertyType;
}
else if (context.ParentNodes().FirstOrDefault() is XamlAstObjectNode parentNode)
{
var parentType = parentNode.Type is XamlAstClrTypeReference clrType
? clrType.Type
: TypeReferenceResolver.ResolveType(context, (XamlAstXmlTypeReference)parentNode.Type, context.StrictMode).Type;
var contentProperty = context.Configuration.FindContentProperty(parentType);
propertyType = contentProperty.PropertyType;
}
if (propertyType is null)
{
throw new InvalidOperationException("Unable to find OnPlatform property type");
}
xmlobj.Type = TypeReferenceResolver.ResolveType(context, xmlref.XmlNamespace, xmlref.Name,
xmlref.IsMarkupExtension, new[] { new XamlAstClrTypeReference(xmlref, propertyType, false) },
xmlref, context.StrictMode);
}
if (node is XamlAstNamePropertyReference xmlprop
&& xmlprop.DeclaringType is XamlAstXmlTypeReference propxmlref
&& propxmlref.Name.StartsWith("OnPlatform")
&& !propxmlref.GenericArguments.Any())
{
var expectedType = context.ParentNodes().OfType<XamlAstObjectNode>()
.First(n => n.Type is XamlAstClrTypeReference clrRef && clrRef.Type.Name.StartsWith("OnPlatform")
|| n.Type is XamlAstXmlTypeReference xmlRef && xmlRef.Name.StartsWith("OnPlatform"))
.Type;
xmlprop.DeclaringType = expectedType;
xmlprop.TargetType = expectedType;
}
// if (node is XamlAstObjectNode onobj
// && onobj.Type is XamlAstXmlTypeReference onref
// && onref.Name == "On")
// {
// var platformStr = (onobj.Children.OfType<XamlAstXamlPropertyValueNode>()
// .FirstOrDefault(v => ((XamlAstNamePropertyReference)v.Property).Name == "Platform")?.Values.Single() as XamlAstTextNode)?
// .Text;
// if (string.IsNullOrWhiteSpace(platformStr))
// {
// throw new InvalidOperationException("On.Platform string must be set");
// }
// var content = onobj.Children.OfType<XamlAstObjectNode>().FirstOrDefault();
// if (content is null)
// {
// throw new InvalidOperationException("On content object must be set");
// }
//
// var parentOnPlatformObject = context.ParentNodes().OfType<XamlAstObjectNode>()
// .First(n => n.Type is XamlAstClrTypeReference clrRef && clrRef.Type.Name.StartsWith("OnPlatform")
// || n.Type is XamlAstXmlTypeReference xmlRef && xmlRef.Name.StartsWith("OnPlatform"));
// parentOnPlatformObject.Children.Remove(onobj);
// foreach (var platform in platformStr.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
// {
// var propertyNode = new XamlAstXamlPropertyValueNode(onobj,
// new XamlAstNamePropertyReference(onobj, parentOnPlatformObject.Type, platform.Trim(),
// parentOnPlatformObject.Type), content);
// parentOnPlatformObject.Children.Add(propertyNode);
// }
//
// return parentOnPlatformObject.Children.Last();
// }
// if (node is XamlAstXamlPropertyValueNode propNode)
// {
// var type = (propNode.Property as XamlAstNamePropertyReference).TargetType as XamlAstXmlTypeReference;
//
// propNode.VisitChildren(new OnPlatformGenericTypeVisitor(type));
//
// return node;
// }
return node;
}
private class OnPlatformGenericTypeVisitor : IXamlAstVisitor
{
private readonly XamlAstXmlTypeReference _type;
public OnPlatformGenericTypeVisitor(IXamlAstTypeReference type)
{
}
public IXamlAstNode Visit(IXamlAstNode node)
{
if (node is XamlAstXmlTypeReference { Name: "OnPlatform" } xmlref)
{
xmlref.GenericArguments.Add(_type);
}
return node;
}
public void Push(IXamlAstNode node)
{
}
public void Pop()
{
}
}
}

2
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

@ -1 +1 @@
Subproject commit 2c3b53a3db61018db620418fab509ba1d168ff24
Subproject commit f1bfeb0c575cf05175debbd518153d2674dd90b7

43
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs

@ -1,12 +1,30 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Metadata;
using Avalonia.Platform;
namespace Avalonia.Markup.Xaml.MarkupExtensions;
public static class OnPlatformExtensionHelper
{
// TEMPORARY, to replace with XAML compiler namespace helpers
public static bool IsOSPlatform(string platform)
{
var runtimeInfo = AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>().GetRuntimeInfo();
return platform switch
{
"WINDOWS" => runtimeInfo.OperatingSystem == OperatingSystemType.WinNT,
"MACOS" => runtimeInfo.OperatingSystem == OperatingSystemType.OSX,
_ => runtimeInfo.OperatingSystem.ToString().Equals(platform, StringComparison.OrdinalIgnoreCase)
};
//return RuntimeInformation.IsOSPlatform(OSPlatform.Create(platform));
}
}
public class On
{
public string Platform { get; set; } = "Unknown";
@ -15,6 +33,18 @@ public class On
public object? Content { get; set; }
}
public class OnPlatformExtension : OnPlatformExtension<object>
{
public OnPlatformExtension()
{
}
public OnPlatformExtension(object defaultValue) : base(defaultValue)
{
}
}
public class OnPlatformExtension<TReturn> : IAddChild<On>
{
private readonly Dictionary<string, TReturn?> _values = new();
@ -26,7 +56,7 @@ public class OnPlatformExtension<TReturn> : IAddChild<On>
public OnPlatformExtension()
{
}
public TReturn? Default { get => _values.TryGetValue(nameof(Default), out var value) ? value : default; set { _values[nameof(Default)] = value; } }
@ -38,6 +68,7 @@ public class OnPlatformExtension<TReturn> : IAddChild<On>
public TReturn? Browser { get => _values.TryGetValue(nameof(Browser), out var value) ? value : default; set { _values[nameof(Browser)] = value; } }
public object? ProvideValue()
{
throw new NotSupportedException();
if (!_values.Any())
{
throw new InvalidOperationException("OnPlatformExtension requires a value to be specified for at least one platform or Default.");
@ -52,7 +83,7 @@ public class OnPlatformExtension<TReturn> : IAddChild<On>
var runtimeInfo = AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>().GetRuntimeInfo();
TReturn val;
switch (runtimeInfo.OperatingSystem)
{
case OperatingSystemType.WinNT:
@ -75,21 +106,21 @@ public class OnPlatformExtension<TReturn> : IAddChild<On>
return (val, true);
}
break;
case OperatingSystemType.Android:
if (_values.TryGetValue(nameof(Android), out val))
{
return (val, true);
}
break;
case OperatingSystemType.iOS:
if (_values.TryGetValue(nameof(iOS), out val))
{
return (val, true);
}
break;
case OperatingSystemType.Browser:
if (_values.TryGetValue(nameof(Browser), out val))
{
@ -97,7 +128,7 @@ public class OnPlatformExtension<TReturn> : IAddChild<On>
}
break;
}
if (_values.TryGetValue(nameof(Default), out val))
{
return (val, true);

78
tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/OnPlatformExtensionTests.cs

@ -42,7 +42,8 @@ public class OnPlatformExtensionTests : XamlTestBase
var xaml = @"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<TextBlock Text='{OnPlatform ""Hello World"", Android=""Im Android""}'/>
<TextBlock Text='{OnPlatform ""Hello World"", Android=""Im Android""}'
Margin='10' />
</UserControl>";
var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
@ -125,7 +126,7 @@ public class OnPlatformExtensionTests : XamlTestBase
}
}
[Fact(Skip = "Fix me")]
[Fact]
public void Should_Respect_Custom_TypeArgument()
{
using (AvaloniaLocator.EnterScope())
@ -170,6 +171,27 @@ public class OnPlatformExtensionTests : XamlTestBase
}
}
[Fact]
public void Should_Allow_Nester_On_Platform_Markup_Extensions()
{
using (AvaloniaLocator.EnterScope())
{
AvaloniaLocator.CurrentMutable.Bind<IRuntimePlatform>()
.ToConstant(new TestRuntimePlatform(OperatingSystemType.WinNT));
var xaml = @"
<UserControl xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Border Margin='{OnPlatform Windows={OnPlatform Linux=5, Windows=""10,10,10,10"", x:TypeArguments=Thickness}}'/>
</UserControl>";
var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var border = (Border)userControl.Content!;
Assert.Equal(new Thickness(10), border.Margin);
}
}
[Fact]
public void Should_Support_Xml_Syntax()
{
@ -224,13 +246,15 @@ public class OnPlatformExtensionTests : XamlTestBase
}
}
[Fact]
public void Should_Support_Special_On_Syntax()
[Theory]
[InlineData(OperatingSystemType.OSX, "#ff506070")]
[InlineData(OperatingSystemType.Linux, "#000")]
public void Should_Support_Special_On_Syntax(OperatingSystemType os, string color)
{
using (AvaloniaLocator.EnterScope())
{
AvaloniaLocator.CurrentMutable.Bind<IRuntimePlatform>()
.ToConstant(new TestRuntimePlatform(OperatingSystemType.OSX));
.ToConstant(new TestRuntimePlatform(os));
var xaml = @"
<UserControl xmlns='https://github.com/avaloniaui'
@ -252,7 +276,7 @@ public class OnPlatformExtensionTests : XamlTestBase
var userControl = (UserControl)AvaloniaRuntimeXamlLoader.Load(xaml);
var border = (Border)userControl.Content!;
Assert.Equal(Color.Parse("#ff506070"), ((ISolidColorBrush)border.Background!).Color);
Assert.Equal(Color.Parse(color), ((ISolidColorBrush)border.Background!).Color);
}
}
@ -281,6 +305,31 @@ public class OnPlatformExtensionTests : XamlTestBase
}
}
[Fact]
public void Should_Support_Complex_Property_Setters()
{
using (AvaloniaLocator.EnterScope())
{
AvaloniaLocator.CurrentMutable.Bind<IRuntimePlatform>()
.ToConstant(new TestRuntimePlatform(OperatingSystemType.WinNT));
var xaml = @"
<ResourceDictionary xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<OnPlatform x:Key='MyKey'>
<OnPlatform.Windows>
<Button Content='Hello World' />
</OnPlatform.Windows>
</OnPlatform>
</ResourceDictionary>";
var resourceDictionary = (ResourceDictionary)AvaloniaRuntimeXamlLoader.Load(xaml);
var button = Assert.IsType<Button>(resourceDictionary["MyKey"]);
Assert.Equal("Hello World", button.Content);
}
}
private class TestRuntimePlatform : StandardRuntimePlatform
{
private readonly OperatingSystemType _operatingSystemType;
@ -296,20 +345,3 @@ public class OnPlatformExtensionTests : XamlTestBase
}
}
}
public class TestOnPlatformConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Assert.Equal("My Parameter", parameter);
Assert.Equal("My Value", value);
Assert.Equal(typeof(Thickness), targetType);
return new Thickness(4);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Loading…
Cancel
Save