Browse Source

add OnFormFactor extension.

release/11.0.0-preview3
Dan Walmsley 4 years ago
parent
commit
59e12e4d30
  1. 36
      samples/ControlCatalog/Pages/PlatformInfoPage.xaml
  2. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  3. 128
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/OnFormFactorTransformer.cs
  4. 1
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  5. 78
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs
  6. 100
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnPlatformExtension.cs

36
samples/ControlCatalog/Pages/PlatformInfoPage.xaml

@ -6,8 +6,40 @@
d:DesignHeight="800"
d:DesignWidth="400"
mc:Ignorable="d">
<StackPanel>
<TextBlock Text="{OnPlatform Unknown, Mobile=Mobile, Desktop=Desktop}" />
<StackPanel Spacing="20">
<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="{OnPlatform Gray, Windows=Green}">
<TextBlock Text="Windows" />
</Border>
<Border Height="100" Width="100" Background="{OnPlatform Gray, macOS=Green}">
<TextBlock Text="macOS" />
</Border>
<Border Height="100" Width="100" Background="{OnPlatform Gray, Linux=Green}">
<TextBlock Text="Linux" />
</Border>
<Border Height="100" Width="100" Background="{OnPlatform Gray, Browser=Green}">
<TextBlock Text="Browser" />
</Border>
<Border Height="100" Width="100" Background="{OnPlatform Gray, iOS=Green}">
<TextBlock Text="iOS" />
</Border>
<Border Height="100" Width="100" Background="{OnPlatform Gray, Android=Green}">
<TextBlock Text="Android" />
</Border>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

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

@ -57,6 +57,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer()
);
InsertBefore<TypeReferenceResolver>(
new OnFormFactorTransformer());
InsertBefore<TypeReferenceResolver>(
new OnPlatformTransformer());
InsertAfter<TypeReferenceResolver>(

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

@ -0,0 +1,128 @@
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 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 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 parentOnFormFactorObject = 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"));
// parentOnFormFactorObject.Children.Remove(onobj);
// foreach (var platform in platformStr.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries))
// {
// var propertyNode = new XamlAstXamlPropertyValueNode(onobj,
// new XamlAstNamePropertyReference(onobj, parentOnFormFactorObject.Type, platform.Trim(),
// parentOnFormFactorObject.Type), content);
// parentOnFormFactorObject.Children.Add(propertyNode);
// }
//
// return parentOnFormFactorObject.Children.Last();
// }
// if (node is XamlAstXamlPropertyValueNode propNode)
// {
// var type = (propNode.Property as XamlAstNamePropertyReference).TargetType as XamlAstXmlTypeReference;
//
// propNode.VisitChildren(new OnFormFactorGenericTypeVisitor(type));
//
// return node;
// }
return node;
}
private class OnFormFactorGenericTypeVisitor : IXamlAstVisitor
{
private readonly XamlAstXmlTypeReference _type;
public OnFormFactorGenericTypeVisitor(IXamlAstTypeReference type)
{
}
public IXamlAstNode Visit(IXamlAstNode node)
{
if (node is XamlAstXmlTypeReference { Name: "OnFormFactor" } xmlref)
{
xmlref.GenericArguments.Add(_type);
}
return node;
}
public void Push(IXamlAstNode node)
{
}
public void Pop()
{
}
}
}

1
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -28,6 +28,7 @@
<Compile Include="MarkupExtensions\CompiledBindings\StrongTypeCastNode.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\TaskStreamPlugin.cs" />
<Compile Include="MarkupExtensions\DynamicResourceExtension.cs" />
<Compile Include="MarkupExtensions\OnFormFactorExtension.cs" />
<Compile Include="MarkupExtensions\OnPlatformExtension.cs" />
<Compile Include="MarkupExtensions\ResolveByNameExtension.cs" />
<Compile Include="MarkupExtensions\ResourceInclude.cs" />

78
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/OnFormFactorExtension.cs

@ -0,0 +1,78 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Platform;
namespace Avalonia.Markup.Xaml.MarkupExtensions;
public class OnFormFactorExtension<TReturn>
{
private readonly Dictionary<string, TReturn?> _values = new();
public OnFormFactorExtension(TReturn defaultValue)
{
Default = defaultValue;
}
public TReturn? Default
{
get => _values.TryGetValue(nameof(Default), out var value) ? value : default;
set { _values[nameof(Default)] = value; }
}
public TReturn? Desktop
{
get => _values.TryGetValue(nameof(Desktop), out var value) ? value : default;
set { _values[nameof(Desktop)] = value; }
}
public TReturn? Mobile
{
get => _values.TryGetValue(nameof(Mobile), out var value) ? value : default;
set { _values[nameof(Mobile)] = value; }
}
public object? ProvideValue()
{
if (!_values.Any())
{
throw new InvalidOperationException(
"OnPlatformExtension requires a value to be specified for at least one platform or Default.");
}
var (value, hasValue) = TryGetValueForPlatform();
return !hasValue ? AvaloniaProperty.UnsetValue : value;
}
private (object? value, bool hasValue) TryGetValueForPlatform()
{
var runtimeInfo = AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>().GetRuntimeInfo();
TReturn val;
if (runtimeInfo.IsDesktop)
{
if (_values.TryGetValue(nameof(Desktop), out val))
{
return (val, true);
}
}
if (runtimeInfo.IsMobile)
{
if (_values.TryGetValue(nameof(Mobile), out val))
{
return (val, true);
}
}
if (_values.TryGetValue(nameof(Default), out val))
{
return (val, true);
}
return default;
}
}

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

@ -24,6 +24,11 @@ public class OnPlatformExtension<TReturn> : IAddChild<On>
Default = defaultValue;
}
public OnPlatformExtension()
{
}
public TReturn? Default { get => _values.TryGetValue(nameof(Default), out var value) ? value : default; set { _values[nameof(Default)] = value; } }
public TReturn? Windows { get => _values.TryGetValue(nameof(Windows), out var value) ? value : default; set { _values[nameof(Windows)] = value; } }
public TReturn? macOS { get => _values.TryGetValue(nameof(macOS), out var value) ? value : default; set { _values[nameof(macOS)] = value; } }
@ -31,11 +36,6 @@ public class OnPlatformExtension<TReturn> : IAddChild<On>
public TReturn? Android { get => _values.TryGetValue(nameof(Android), out var value) ? value : default; set { _values[nameof(Android)] = value; } }
public TReturn? iOS { get => _values.TryGetValue(nameof(iOS), out var value) ? value : default; set { _values[nameof(iOS)] = value; } }
public TReturn? Browser { get => _values.TryGetValue(nameof(Browser), out var value) ? value : default; set { _values[nameof(Browser)] = value; } }
public TReturn? Desktop { get => _values.TryGetValue(nameof(Desktop), out var value) ? value : default; set { _values[nameof(Desktop)] = value; } }
public TReturn? Mobile { get => _values.TryGetValue(nameof(Mobile), out var value) ? value : default; set { _values[nameof(Mobile)] = value; } }
public object? ProvideValue()
{
if (!_values.Any())
@ -47,53 +47,63 @@ public class OnPlatformExtension<TReturn> : IAddChild<On>
return !hasValue ? AvaloniaProperty.UnsetValue : value;
}
private (TReturn? value, bool hasValue) TryGetFormFactorValues(RuntimePlatformInfo runtimeInfo)
private (TReturn? value, bool hasValue) TryGetValueForPlatform()
{
if (runtimeInfo.IsDesktop)
{
if (_values.TryGetValue(nameof(Desktop), out var val1))
{
return (val1, true);
}
}
var runtimeInfo = AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>().GetRuntimeInfo();
if (runtimeInfo.IsMobile)
{
if (_values.TryGetValue(nameof(Mobile), out var val1))
{
return (val1, true);
}
}
TReturn val;
return default;
}
switch (runtimeInfo.OperatingSystem)
{
case OperatingSystemType.WinNT:
if (_values.TryGetValue(nameof(Windows), out val))
{
return (val, true);
}
break;
private (TReturn? value, bool hasValue) TryGetValueForPlatform()
{
var runtimeInfo = AvaloniaLocator.Current.GetRequiredService<IRuntimePlatform>().GetRuntimeInfo();
case OperatingSystemType.OSX:
if (_values.TryGetValue(nameof(macOS), out val))
{
return (val, true);
}
break;
return runtimeInfo.OperatingSystem switch
case OperatingSystemType.Linux:
if (_values.TryGetValue(nameof(macOS), out val))
{
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))
{
return (val, true);
}
break;
}
if (_values.TryGetValue(nameof(Default), out val))
{
OperatingSystemType.WinNT => _values.TryGetValue(nameof(Windows), out var val) ?
(val, true) :
TryGetFormFactorValues(runtimeInfo),
OperatingSystemType.OSX => _values.TryGetValue(nameof(macOS), out var val) ?
(val, true) :
TryGetFormFactorValues(runtimeInfo),
OperatingSystemType.Linux => _values.TryGetValue(nameof(Linux), out var val) ?
(val, true) :
TryGetFormFactorValues(runtimeInfo),
OperatingSystemType.Android => _values.TryGetValue(nameof(Android), out var val) ?
(val, true) :
TryGetFormFactorValues(runtimeInfo),
OperatingSystemType.iOS => _values.TryGetValue(nameof(iOS), out var val) ?
(val, true) :
TryGetFormFactorValues(runtimeInfo),
OperatingSystemType.Browser => _values.TryGetValue(nameof(Browser), out var val) ?
(val, true) :
TryGetFormFactorValues(runtimeInfo),
_ => _values.TryGetValue(nameof(Default), out var val) ? (val, true) : TryGetFormFactorValues(runtimeInfo)
return (val, true);
};
return default;
}
public void AddChild(On child)

Loading…
Cancel
Save