Browse Source

Merge branch 'master' into fixes/thumb-drag-delta

pull/11087/head
Max Katz 4 years ago
committed by GitHub
parent
commit
7794e361a3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      src/Avalonia.Base/Media/PolyLineSegment.cs
  2. 18
      src/Avalonia.Base/Media/PolylineGeometry.cs
  3. 14
      src/Avalonia.Base/Points.cs
  4. 9
      src/Avalonia.Controls/Shapes/Polygon.cs
  5. 9
      src/Avalonia.Controls/Shapes/Polyline.cs
  6. 71
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlArrayConstantAstNode.cs
  7. 138
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
  8. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  9. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  10. 4
      tests/Avalonia.RenderTests/Shapes/PolygonTests.cs
  11. 4
      tests/Avalonia.RenderTests/Shapes/PolylineTests.cs

10
src/Avalonia.Base/Media/PolyLineSegment.cs

@ -10,8 +10,8 @@ namespace Avalonia.Media
/// <summary>
/// Defines the <see cref="Points"/> property.
/// </summary>
public static readonly StyledProperty<Points> PointsProperty
= AvaloniaProperty.Register<PolyLineSegment, Points>(nameof(Points));
public static readonly StyledProperty<IList<Point>> PointsProperty
= AvaloniaProperty.Register<PolyLineSegment, IList<Point>>(nameof(Points));
/// <summary>
/// Gets or sets the points.
@ -19,7 +19,7 @@ namespace Avalonia.Media
/// <value>
/// The points.
/// </value>
public Points Points
public IList<Point> Points
{
get => GetValue(PointsProperty);
set => SetValue(PointsProperty, value);
@ -37,9 +37,9 @@ namespace Avalonia.Media
/// Initializes a new instance of the <see cref="PolyLineSegment"/> class.
/// </summary>
/// <param name="points">The points.</param>
public PolyLineSegment(IEnumerable<Point> points) : this()
public PolyLineSegment(IEnumerable<Point> points)
{
Points.AddRange(points);
Points = new Points(points);
}
protected internal override void ApplyTo(StreamGeometryContext ctx)

18
src/Avalonia.Base/Media/PolylineGeometry.cs

@ -14,8 +14,8 @@ namespace Avalonia.Media
/// <summary>
/// Defines the <see cref="Points"/> property.
/// </summary>
public static readonly DirectProperty<PolylineGeometry, Points> PointsProperty =
AvaloniaProperty.RegisterDirect<PolylineGeometry, Points>(nameof(Points), g => g.Points, (g, f) => g.Points = f);
public static readonly DirectProperty<PolylineGeometry, IList<Point>> PointsProperty =
AvaloniaProperty.RegisterDirect<PolylineGeometry, IList<Point>>(nameof(Points), g => g.Points, (g, f) => g.Points = f);
/// <summary>
/// Defines the <see cref="IsFilled"/> property.
@ -23,13 +23,13 @@ namespace Avalonia.Media
public static readonly StyledProperty<bool> IsFilledProperty =
AvaloniaProperty.Register<PolylineGeometry, bool>(nameof(IsFilled));
private Points _points;
private IList<Point> _points;
private IDisposable? _pointsObserver;
static PolylineGeometry()
{
AffectsGeometry(IsFilledProperty);
PointsProperty.Changed.AddClassHandler<PolylineGeometry>((s, e) => s.OnPointsChanged(e.NewValue as Points));
PointsProperty.Changed.AddClassHandler<PolylineGeometry>((s, e) => s.OnPointsChanged(e.NewValue as IList<Point>));
}
/// <summary>
@ -43,9 +43,9 @@ namespace Avalonia.Media
/// <summary>
/// Initializes a new instance of the <see cref="PolylineGeometry"/> class.
/// </summary>
public PolylineGeometry(IEnumerable<Point> points, bool isFilled) : this()
public PolylineGeometry(IEnumerable<Point> points, bool isFilled)
{
Points.AddRange(points);
_points = new Points(points);
IsFilled = isFilled;
}
@ -56,7 +56,7 @@ namespace Avalonia.Media
/// The points.
/// </value>
[Content]
public Points Points
public IList<Point> Points
{
get => _points;
set => SetAndRaise(PointsProperty, ref _points, value);
@ -97,10 +97,10 @@ namespace Avalonia.Media
return geometry;
}
private void OnPointsChanged(Points? newValue)
private void OnPointsChanged(IList<Point>? newValue)
{
_pointsObserver?.Dispose();
_pointsObserver = newValue?.ForEachItem(
_pointsObserver = (newValue as IAvaloniaList<Point>)?.ForEachItem(
_ => InvalidateGeometry(),
_ => InvalidateGeometry(),
InvalidateGeometry);

14
src/Avalonia.Base/Points.cs

@ -1,6 +1,18 @@
using System.Collections.Generic;
using Avalonia.Collections;
namespace Avalonia
{
public sealed class Points : AvaloniaList<Point> { }
public sealed class Points : AvaloniaList<Point>
{
public Points()
{
}
public Points(IEnumerable<Point> points) : base(points)
{
}
}
}

9
src/Avalonia.Controls/Shapes/Polygon.cs

@ -13,10 +13,15 @@ namespace Avalonia.Controls.Shapes
AffectsGeometry<Polygon>(PointsProperty);
}
public Polygon()
{
Points = new Points();
}
public IList<Point> Points
{
get { return GetValue(PointsProperty); }
set { SetValue(PointsProperty, value); }
get => GetValue(PointsProperty);
set => SetValue(PointsProperty, value);
}
protected override Geometry CreateDefiningGeometry()

9
src/Avalonia.Controls/Shapes/Polyline.cs

@ -14,10 +14,15 @@ namespace Avalonia.Controls.Shapes
AffectsGeometry<Polyline>(PointsProperty);
}
public Polyline()
{
Points = new Points();
}
public IList<Point> Points
{
get { return GetValue(PointsProperty); }
set { SetValue(PointsProperty, value); }
get => GetValue(PointsProperty);
set => SetValue(PointsProperty, value);
}
protected override Geometry CreateDefiningGeometry()

71
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AstNodes/AvaloniaXamlIlArrayConstantAstNode.cs

@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Reflection.Emit;
using Avalonia.Controls;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using XamlX;
using XamlX.Ast;
using XamlX.Emit;
using XamlX.IL;
using XamlX.Transform;
using XamlX.TypeSystem;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.AstNodes
{
class AvaloniaXamlIlArrayConstantAstNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
{
private readonly IXamlType _elementType;
private readonly IReadOnlyList<IXamlAstValueNode> _values;
public AvaloniaXamlIlArrayConstantAstNode(IXamlLineInfo lineInfo, IXamlType arrayType, IXamlType elementType, IReadOnlyList<IXamlAstValueNode> values) : base(lineInfo)
{
_elementType = elementType;
_values = values;
Type = new XamlAstClrTypeReference(lineInfo, arrayType, false);
foreach (var element in values)
{
if (!elementType.IsAssignableFrom(element.Type.GetClrType()))
{
throw new XamlParseException("x:Array element is not assignable to the array element type!", lineInfo);
}
}
}
public IXamlAstTypeReference Type { get; }
public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
{
codeGen.Ldc_I4(_values.Count)
.Newarr(_elementType);
for (var index = 0; index < _values.Count; index++)
{
var value = _values[index];
codeGen
.Dup()
.Ldc_I4(index);
context.Emit(value, codeGen, _elementType);
if (value.Type.GetClrType() is { IsValueType: true } valTypeInObjArr)
{
if (!_elementType.IsValueType)
{
codeGen.Box(valTypeInObjArr);
}
// It seems like ASM codegen for "stelem valuetype" and "stelem.i4" is identical,
// so we don't need to try to optimize it here.
codeGen.Emit(OpCodes.Stelem, valTypeInObjArr);
}
else
{
codeGen.Stelem_ref();
}
}
return XamlILNodeEmitResult.Type(0, Type.GetClrType());
}
}
}

138
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs

@ -198,6 +198,29 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
}
}
if (type.Equals(types.ColumnDefinition) || type.Equals(types.RowDefinition))
{
try
{
var gridLength = GridLength.Parse(text);
result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength);
var definitionConstructorGridLength = type.GetConstructor(new List<IXamlType> {types.GridLength});
var lengthNode = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength);
var definitionTypeRef = new XamlAstClrTypeReference(node, type, false);
result = new XamlAstNewClrObjectNode(node, definitionTypeRef,
definitionConstructorGridLength, new List<IXamlAstValueNode> {lengthNode});
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node);
}
}
if (type.Equals(types.Cursor))
{
@ -211,16 +234,6 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
if (type.Equals(types.ColumnDefinitions))
{
return ConvertDefinitionList(node, text, types, types.ColumnDefinitions, types.ColumnDefinition, "column definitions", out result);
}
if (type.Equals(types.RowDefinitions))
{
return ConvertDefinitionList(node, text, types, types.RowDefinitions, types.RowDefinition, "row definitions", out result);
}
if (types.IBrush.IsAssignableFrom(type))
{
if (Color.TryParse(text, out Color color))
@ -295,46 +308,89 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
result = null;
return false;
}
private static bool ConvertDefinitionList(
IXamlAstValueNode node,
string text,
AvaloniaXamlIlWellKnownTypes types,
IXamlType listType,
IXamlType elementType,
string errorDisplayName,
out IXamlAstValueNode result)
{
try
// Keep it in the end, so more specific parsers can be applied.
var elementType = GetElementType(type, context.Configuration.WellKnownTypes);
if (elementType is not null)
{
var lengths = GridLength.ParseLengths(text);
var definitionTypeRef = new XamlAstClrTypeReference(node, elementType, false);
string[] items;
// Normalize special case of Points collection.
if (elementType == types.Point)
{
var pointParts = text.Split(new[] { ",", " " }, StringSplitOptions.RemoveEmptyEntries);
if (pointParts.Length % 2 == 0)
{
items = new string[pointParts.Length / 2];
for (int i = 0; i < pointParts.Length; i += 2)
{
items[i / 2] = string.Format(CultureInfo.InvariantCulture, "{0} {1}", pointParts[i],
pointParts[i + 1]);
}
}
else
{
throw new XamlX.XamlLoadException($"Invalid PointsList.", node);
}
}
else
{
const StringSplitOptions trimOption = (StringSplitOptions)2; // StringSplitOptions.TrimEntries
var separators = new[] { "," };
var splitOptions = StringSplitOptions.RemoveEmptyEntries | trimOption;
var definitionConstructorGridLength = elementType.GetConstructor(new List<IXamlType> {types.GridLength});
items = text.Split(separators, splitOptions ^ trimOption);
// Compiler targets netstandard, so we need to emulate StringSplitOptions.TrimEntries, if it was requested.
if (splitOptions.HasFlag(trimOption))
{
items = items.Select(i => i.Trim()).ToArray();
}
}
IXamlAstValueNode CreateDefinitionNode(GridLength length)
var nodes = new IXamlAstValueNode[items.Length];
for (var index = 0; index < items.Length; index++)
{
var lengthNode = new AvaloniaXamlIlGridLengthAstNode(node, types, length);
var success = XamlTransformHelpers.TryGetCorrectlyTypedValue(
context,
new XamlAstTextNode(node, items[index], true, context.Configuration.WellKnownTypes.String),
elementType, out var itemNode);
if (!success)
{
result = null;
return false;
}
return new XamlAstNewClrObjectNode(node, definitionTypeRef,
definitionConstructorGridLength, new List<IXamlAstValueNode> {lengthNode});
nodes[index] = itemNode;
}
var definitionNodes =
new List<IXamlAstValueNode>(lengths.Select(CreateDefinitionNode));
result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, listType, elementType, definitionNodes);
if (types.AvaloniaList.MakeGenericType(elementType).IsAssignableFrom(type))
{
result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, type, elementType, nodes);
return true;
}
else if (type.IsArray)
{
result = new AvaloniaXamlIlArrayConstantAstNode(node, elementType.MakeArrayType(1), elementType, nodes);
return true;
}
else if (type == context.Configuration.WellKnownTypes.IListOfT.MakeGenericType(elementType))
{
var listType = context.Configuration.WellKnownTypes.IListOfT.MakeGenericType(elementType);
result = new AvaloniaXamlIlArrayConstantAstNode(node, listType, elementType, nodes);
return true;
}
return true;
}
catch
{
throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a {errorDisplayName}", node);
result = null;
return false;
}
result = null;
return false;
}
private static IXamlType GetElementType(IXamlType type, XamlTypeWellKnownTypes types)
{
return type.GetAllInterfaces().FirstOrDefault(i =>
i.FullName.StartsWith(types.IEnumerableT.FullName))?
.GenericArguments[0];
}
}
}

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

@ -33,6 +33,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType InheritDataTypeFromItemsAttribute { get; }
public IXamlType MarkupExtensionOptionAttribute { get; }
public IXamlType MarkupExtensionDefaultOptionAttribute { get; }
public IXamlType AvaloniaList { get; }
public IXamlType OnExtensionType { get; }
public IXamlType UnsetValueType { get; }
public IXamlType StyledElement { get; }
@ -141,6 +142,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
InheritDataTypeFromItemsAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.InheritDataTypeFromItemsAttribute");
MarkupExtensionOptionAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.MarkupExtensionOptionAttribute");
MarkupExtensionDefaultOptionAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.MarkupExtensionDefaultOptionAttribute");
AvaloniaList = cfg.TypeSystem.GetType("Avalonia.Collections.AvaloniaList`1");
OnExtensionType = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.MarkupExtensions.On");
AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, AvaloniaObject,
AvaloniaProperty,

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

@ -1 +1 @@
Subproject commit 5dd0b042e144e677638224c49fec16dab66143e8
Subproject commit 5d1025f30d0ed6d8f419d82959c148276301f393

4
tests/Avalonia.RenderTests/Shapes/PolygonTests.cs

@ -30,7 +30,7 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
Stroke = Brushes.DarkBlue,
Stretch = Stretch.Uniform,
Fill = Brushes.Violet,
Points = new [] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) },
Points = new Points { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) },
StrokeThickness = 1
}
};
@ -52,7 +52,7 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
Stroke = Brushes.DarkBlue,
Stretch = Stretch.Fill,
Fill = Brushes.Violet,
Points = new[] { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) },
Points = new Points { new Point(5, 0), new Point(8, 8), new Point(0, 3), new Point(10, 3), new Point(2, 8) },
StrokeThickness = 5,
}
};

4
tests/Avalonia.RenderTests/Shapes/PolylineTests.cs

@ -20,7 +20,7 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
[Fact]
public async Task Polyline_1px_Stroke()
{
var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3),
var polylinePoints = new Points { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3),
new Point(9, 1), new Point(10, 0), new Point(15, 0) };
Decorator target = new Decorator
@ -44,7 +44,7 @@ namespace Avalonia.Direct2D1.RenderTests.Shapes
[Fact]
public async Task Polyline_10px_Stroke_PenLineJoin()
{
var polylinePoints = new Point[] { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3),
var polylinePoints = new Points { new Point(0, 0), new Point(5, 0), new Point(6, -2), new Point(7, 3), new Point(8, -3),
new Point(9, 1), new Point(10, 0), new Point(15, 0) };
Decorator target = new Decorator

Loading…
Cancel
Save