6 changed files with 298 additions and 47 deletions
@ -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() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue