diff --git a/samples/RenderTest/MainWindow.xaml b/samples/RenderTest/MainWindow.xaml
index 1d3001f1b1..9e9a600161 100644
--- a/samples/RenderTest/MainWindow.xaml
+++ b/samples/RenderTest/MainWindow.xaml
@@ -27,6 +27,7 @@
+
\ No newline at end of file
diff --git a/samples/RenderTest/Pages/DrawingPage.xaml b/samples/RenderTest/Pages/DrawingPage.xaml
new file mode 100644
index 0000000000..81181e01fc
--- /dev/null
+++ b/samples/RenderTest/Pages/DrawingPage.xaml
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/RenderTest/Pages/DrawingPage.xaml.cs b/samples/RenderTest/Pages/DrawingPage.xaml.cs
new file mode 100644
index 0000000000..3bf9bd545d
--- /dev/null
+++ b/samples/RenderTest/Pages/DrawingPage.xaml.cs
@@ -0,0 +1,18 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace RenderTest.Pages
+{
+ public class DrawingPage : UserControl
+ {
+ public DrawingPage()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+ }
+}
diff --git a/samples/RenderTest/RenderTest.csproj b/samples/RenderTest/RenderTest.csproj
index ea5c0bcc58..b7e64f4dae 100644
--- a/samples/RenderTest/RenderTest.csproj
+++ b/samples/RenderTest/RenderTest.csproj
@@ -51,6 +51,9 @@
App.xaml
+
+ DrawingPage.xaml
+
ClippingPage.xaml
@@ -178,6 +181,11 @@
Designer
+
+
+ Designer
+
+
diff --git a/src/Avalonia.Controls/DrawingPresenter.cs b/src/Avalonia.Controls/DrawingPresenter.cs
new file mode 100644
index 0000000000..af3665fabc
--- /dev/null
+++ b/src/Avalonia.Controls/DrawingPresenter.cs
@@ -0,0 +1,59 @@
+using Avalonia.Controls.Shapes;
+using Avalonia.Media;
+using Avalonia.Metadata;
+
+namespace Avalonia.Controls
+{
+ public class DrawingPresenter : Control
+ {
+ static DrawingPresenter()
+ {
+ AffectsMeasure(DrawingProperty);
+ AffectsRender(DrawingProperty);
+ }
+
+ public static readonly StyledProperty DrawingProperty =
+ AvaloniaProperty.Register(nameof(Drawing));
+
+ public static readonly StyledProperty StretchProperty =
+ AvaloniaProperty.Register(nameof(Stretch), Stretch.Uniform);
+
+ [Content]
+ public Drawing Drawing
+ {
+ get => GetValue(DrawingProperty);
+ set => SetValue(DrawingProperty, value);
+ }
+
+ public Stretch Stretch
+ {
+ get => GetValue(StretchProperty);
+ set => SetValue(StretchProperty, value);
+ }
+
+ private Matrix _transform = Matrix.Identity;
+
+ protected override Size MeasureOverride(Size availableSize)
+ {
+ if (Drawing == null) return new Size();
+
+ var (size, transform) = Shape.CalculateSizeAndTransform(availableSize, Drawing.GetBounds(), Stretch);
+
+ _transform = transform;
+
+ return size;
+ }
+
+ public override void Render(DrawingContext context)
+ {
+ if (Drawing != null)
+ {
+ using (context.PushPreTransform(_transform))
+ using (context.PushClip(Bounds))
+ {
+ Drawing.Draw(context);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Controls/Shapes/Shape.cs b/src/Avalonia.Controls/Shapes/Shape.cs
index 427749263a..c03f4dc563 100644
--- a/src/Avalonia.Controls/Shapes/Shape.cs
+++ b/src/Avalonia.Controls/Shapes/Shape.cs
@@ -155,11 +155,21 @@ namespace Avalonia.Controls.Shapes
{
// This should probably use GetRenderBounds(strokeThickness) but then the calculations
// will multiply the stroke thickness as well, which isn't correct.
- Rect shapeBounds = DefiningGeometry.Bounds;
+ var (size, transform) = CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch);
+
+ if (_transform != transform)
+ {
+ _transform = transform;
+ _renderedGeometry = null;
+ }
+
+ return size;
+ }
+
+ internal static (Size, Matrix) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch)
+ {
Size shapeSize = new Size(shapeBounds.Right, shapeBounds.Bottom);
Matrix translate = Matrix.Identity;
- double width = Width;
- double height = Height;
double desiredX = availableSize.Width;
double desiredY = availableSize.Height;
double sx = 0.0;
@@ -226,15 +236,9 @@ namespace Avalonia.Controls.Shapes
break;
}
- var t = translate * Matrix.CreateScale(sx, sy);
-
- if (_transform != t)
- {
- _transform = t;
- _renderedGeometry = null;
- }
-
- return new Size(shapeSize.Width * sx, shapeSize.Height * sy);
+ var transform = translate * Matrix.CreateScale(sx, sy);
+ var size = new Size(shapeSize.Width * sx, shapeSize.Height * sy);
+ return (size, transform);
}
private static void AffectsGeometryInvalidate(AvaloniaPropertyChangedEventArgs e)
diff --git a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
index 2fbcba40c8..e2c866fe3d 100644
--- a/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
+++ b/src/Avalonia.DotNetFrameworkRuntime/Avalonia.DotNetFrameworkRuntime.csproj
@@ -44,6 +44,9 @@
+
+ Properties\SharedAssemblyInfo.cs
+
diff --git a/src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs b/src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs
index f55d3056f6..3a91d50a24 100644
--- a/src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.DotNetFrameworkRuntime/Properties/AssemblyInfo.cs
@@ -1,18 +1,10 @@
using System.Reflection;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.DotNetFrameworkRuntime")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Avalonia.DotNetFrameworkRuntime")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
@@ -21,16 +13,3 @@ using System.Runtime.InteropServices;
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4a1abb09-9047-4bd5-a4ad-a055e52c5ee0")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj b/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
index 508ede7f7d..9ac40cba07 100644
--- a/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
+++ b/src/Avalonia.Logging.Serilog/Avalonia.Logging.Serilog.csproj
@@ -23,6 +23,9 @@
bin\Release\Avalonia.Logging.Serilog.XML
true
+
+
+
diff --git a/src/Avalonia.Logging.Serilog/Properties/AssemblyInfo.cs b/src/Avalonia.Logging.Serilog/Properties/AssemblyInfo.cs
index 64c0eaffae..35b2e48f70 100644
--- a/src/Avalonia.Logging.Serilog/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.Logging.Serilog/Properties/AssemblyInfo.cs
@@ -1,30 +1,3 @@
-using System.Resources;
using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.Serilog")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Avalonia.Serilog")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-[assembly: NeutralResourcesLanguage("en")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
index 2d66b62eab..22d815d786 100644
--- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
+++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj
@@ -25,6 +25,7 @@
+
diff --git a/src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs b/src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs
index 358c6224fb..c8a5c5cc41 100644
--- a/src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs
+++ b/src/Avalonia.ReactiveUI/Properties/AssemblyInfo.cs
@@ -1,33 +1,9 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
-using System.Resources;
using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.ReactiveUI")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Avalonia.ReactiveUI")]
-[assembly: AssemblyCopyright("Copyright \u00A9 2015")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-[assembly: NeutralResourcesLanguage("en")]
-
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Avalonia.Visuals/Matrix.cs b/src/Avalonia.Visuals/Matrix.cs
index 8812000bdc..10549b967d 100644
--- a/src/Avalonia.Visuals/Matrix.cs
+++ b/src/Avalonia.Visuals/Matrix.cs
@@ -3,6 +3,7 @@
using System;
using System.Globalization;
+using System.Linq;
namespace Avalonia
{
@@ -295,5 +296,33 @@ namespace Avalonia
((_m21 * _m32) - (_m22 * _m31)) / d,
((_m12 * _m31) - (_m11 * _m32)) / d);
}
+
+ ///
+ /// Parses a string.
+ ///
+ /// The string.
+ /// The current culture.
+ /// The .
+ public static Matrix Parse(string s, CultureInfo culture)
+ {
+ var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(x => x.Trim())
+ .ToArray();
+
+ if (parts.Length == 6)
+ {
+ return new Matrix(
+ double.Parse(parts[0], culture),
+ double.Parse(parts[1], culture),
+ double.Parse(parts[2], culture),
+ double.Parse(parts[3], culture),
+ double.Parse(parts[4], culture),
+ double.Parse(parts[5], culture));
+ }
+ else
+ {
+ throw new FormatException("Invalid Matrix.");
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/Drawing.cs b/src/Avalonia.Visuals/Media/Drawing.cs
new file mode 100644
index 0000000000..a60c591edc
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/Drawing.cs
@@ -0,0 +1,9 @@
+namespace Avalonia.Media
+{
+ public abstract class Drawing : AvaloniaObject
+ {
+ public abstract void Draw(DrawingContext context);
+
+ public abstract Rect GetBounds();
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/DrawingGroup.cs b/src/Avalonia.Visuals/Media/DrawingGroup.cs
new file mode 100644
index 0000000000..744ff2af03
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/DrawingGroup.cs
@@ -0,0 +1,58 @@
+using Avalonia.Collections;
+using Avalonia.Metadata;
+
+namespace Avalonia.Media
+{
+ public class DrawingGroup : Drawing
+ {
+ public static readonly StyledProperty OpacityProperty =
+ AvaloniaProperty.Register(nameof(Opacity), 1);
+
+ public static readonly StyledProperty TransformProperty =
+ AvaloniaProperty.Register(nameof(Transform));
+
+ public double Opacity
+ {
+ get => GetValue(OpacityProperty);
+ set => SetValue(OpacityProperty, value);
+ }
+
+ public Transform Transform
+ {
+ get => GetValue(TransformProperty);
+ set => SetValue(TransformProperty, value);
+ }
+
+ [Content]
+ public AvaloniaList Children { get; } = new AvaloniaList();
+
+ public override void Draw(DrawingContext context)
+ {
+ using (context.PushPreTransform(Transform?.Value ?? Matrix.Identity))
+ using (context.PushOpacity(Opacity))
+ {
+ foreach (var drawing in Children)
+ {
+ drawing.Draw(context);
+ }
+ }
+ }
+
+ public override Rect GetBounds()
+ {
+ var rect = new Rect();
+
+ foreach (var drawing in Children)
+ {
+ rect = rect.Union(drawing.GetBounds());
+ }
+
+ if (Transform != null)
+ {
+ rect = rect.TransformToAABB(Transform.Value);
+ }
+
+ return rect;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/EllipseGeometry.cs b/src/Avalonia.Visuals/Media/EllipseGeometry.cs
index 414fd9ab59..591b55cf58 100644
--- a/src/Avalonia.Visuals/Media/EllipseGeometry.cs
+++ b/src/Avalonia.Visuals/Media/EllipseGeometry.cs
@@ -11,16 +11,51 @@ namespace Avalonia.Media
///
public class EllipseGeometry : Geometry
{
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty RectProperty =
+ AvaloniaProperty.Register(nameof(Rect));
+
+ public Rect Rect
+ {
+ get => GetValue(RectProperty);
+ set => SetValue(RectProperty, value);
+ }
+
+ static EllipseGeometry()
+ {
+ RectProperty.Changed.AddClassHandler(x => x.RectChanged);
+ }
+
///
/// Initializes a new instance of the class.
///
- /// The rectangle that the ellipse should fill.
- public EllipseGeometry(Rect rect)
+ public EllipseGeometry()
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
- IStreamGeometryImpl impl = factory.CreateStreamGeometry();
+ PlatformImpl = factory.CreateStreamGeometry();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The rectangle that the ellipse should fill.
+ public EllipseGeometry(Rect rect) : this()
+ {
+ Rect = rect;
+ }
+
+ ///
+ public override Geometry Clone()
+ {
+ return new EllipseGeometry(Rect);
+ }
- using (IStreamGeometryContextImpl ctx = impl.Open())
+ private void RectChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ var rect = (Rect)e.NewValue;
+ using (var ctx = ((IStreamGeometryImpl)PlatformImpl).Open())
{
double controlPointRatio = (Math.Sqrt(2) - 1) * 4 / 3;
var center = rect.Center;
@@ -45,14 +80,6 @@ namespace Avalonia.Media
ctx.CubicBezierTo(new Point(x0, y1), new Point(x1, y0), new Point(x2, y0));
ctx.EndFigure(true);
}
-
- PlatformImpl = impl;
- }
-
- ///
- public override Geometry Clone()
- {
- return new EllipseGeometry(Bounds);
}
}
}
diff --git a/src/Avalonia.Visuals/Media/GeometryDrawing.cs b/src/Avalonia.Visuals/Media/GeometryDrawing.cs
new file mode 100644
index 0000000000..e67e853a84
--- /dev/null
+++ b/src/Avalonia.Visuals/Media/GeometryDrawing.cs
@@ -0,0 +1,43 @@
+namespace Avalonia.Media
+{
+ public class GeometryDrawing : Drawing
+ {
+ public static readonly StyledProperty GeometryProperty =
+ AvaloniaProperty.Register(nameof(Geometry));
+
+ public Geometry Geometry
+ {
+ get => GetValue(GeometryProperty);
+ set => SetValue(GeometryProperty, value);
+ }
+
+ public static readonly StyledProperty BrushProperty =
+ AvaloniaProperty.Register(nameof(Brush), Brushes.Transparent);
+
+ public IBrush Brush
+ {
+ get => GetValue(BrushProperty);
+ set => SetValue(BrushProperty, value);
+ }
+
+ public static readonly StyledProperty PenProperty =
+ AvaloniaProperty.Register(nameof(Pen));
+
+ public Pen Pen
+ {
+ get => GetValue(PenProperty);
+ set => SetValue(PenProperty, value);
+ }
+
+ public override void Draw(DrawingContext context)
+ {
+ context.DrawGeometry(Brush, Pen, Geometry);
+ }
+
+ public override Rect GetBounds()
+ {
+ // adding the Pen's stroke thickness here could yield wrong results due to transforms
+ return Geometry?.GetRenderBounds(0) ?? new Rect();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Avalonia.Visuals/Media/LineGeometry.cs b/src/Avalonia.Visuals/Media/LineGeometry.cs
index 2783d7fb26..323bfa5a7e 100644
--- a/src/Avalonia.Visuals/Media/LineGeometry.cs
+++ b/src/Avalonia.Visuals/Media/LineGeometry.cs
@@ -10,35 +10,89 @@ namespace Avalonia.Media
///
public class LineGeometry : Geometry
{
- private Point _startPoint;
- private Point _endPoint;
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty StartPointProperty =
+ AvaloniaProperty.Register(nameof(StartPoint));
+
+ public Point StartPoint
+ {
+ get => GetValue(StartPointProperty);
+ set => SetValue(StartPointProperty, value);
+ }
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty EndPointProperty =
+ AvaloniaProperty.Register(nameof(EndPoint));
+ private bool _isDirty;
+
+ public Point EndPoint
+ {
+ get => GetValue(EndPointProperty);
+ set => SetValue(EndPointProperty, value);
+ }
+
+ static LineGeometry()
+ {
+ StartPointProperty.Changed.AddClassHandler(x => x.PointsChanged);
+ EndPointProperty.Changed.AddClassHandler(x => x.PointsChanged);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LineGeometry()
+ {
+ IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
+ PlatformImpl = factory.CreateStreamGeometry();
+ }
///
/// Initializes a new instance of the class.
///
/// The start point.
/// The end point.
- public LineGeometry(Point startPoint, Point endPoint)
+ public LineGeometry(Point startPoint, Point endPoint) : this()
{
- _startPoint = startPoint;
- _endPoint = endPoint;
- IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
- IStreamGeometryImpl impl = factory.CreateStreamGeometry();
+ StartPoint = startPoint;
+ EndPoint = endPoint;
+ }
- using (IStreamGeometryContextImpl context = impl.Open())
+ public override IGeometryImpl PlatformImpl
+ {
+ get
{
- context.BeginFigure(_startPoint, false);
- context.LineTo(_endPoint);
- context.EndFigure(false);
+ PrepareIfNeeded();
+ return base.PlatformImpl;
}
+ protected set => base.PlatformImpl = value;
+ }
- PlatformImpl = impl;
+ public void PrepareIfNeeded()
+ {
+ if (_isDirty)
+ {
+ _isDirty = false;
+
+ using (var context = ((IStreamGeometryImpl)PlatformImpl).Open())
+ {
+ context.BeginFigure(StartPoint, false);
+ context.LineTo(EndPoint);
+ context.EndFigure(false);
+ }
+ }
}
///
public override Geometry Clone()
{
- return new LineGeometry(_startPoint, _endPoint);
+ PrepareIfNeeded();
+ return new LineGeometry(StartPoint, EndPoint);
}
+
+ private void PointsChanged(AvaloniaPropertyChangedEventArgs e) => _isDirty = true;
}
}
diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs
index 9c5ffe7151..fad55ed531 100644
--- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs
+++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs
@@ -141,6 +141,7 @@ namespace Avalonia.Media
bool isLargeArc = ReadBool(reader);
ReadSeparator(reader);
SweepDirection sweepDirection = ReadBool(reader) ? SweepDirection.Clockwise : SweepDirection.CounterClockwise;
+ ReadSeparator(reader);
point = ReadPoint(reader, point, relative);
_context.ArcTo(point, size, rotationAngle, isLargeArc, sweepDirection);
diff --git a/src/Avalonia.Visuals/Media/PolylineGeometry.cs b/src/Avalonia.Visuals/Media/PolylineGeometry.cs
index 709ad7a9f5..7c47e7d04d 100644
--- a/src/Avalonia.Visuals/Media/PolylineGeometry.cs
+++ b/src/Avalonia.Visuals/Media/PolylineGeometry.cs
@@ -3,10 +3,9 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using Avalonia.Platform;
+using Avalonia.Metadata;
+using Avalonia.Collections;
namespace Avalonia.Media
{
@@ -15,36 +14,118 @@ namespace Avalonia.Media
///
public class PolylineGeometry : Geometry
{
- private IList _points;
- private bool _isFilled;
+ ///
+ /// Defines the property.
+ ///
+ public static readonly DirectProperty PointsProperty =
+ AvaloniaProperty.RegisterDirect(nameof(Points), g => g.Points, (g, f) => g.Points = f);
- public PolylineGeometry(IList points, bool isFilled)
+ ///
+ /// Defines the property.
+ ///
+ public static readonly AvaloniaProperty IsFilledProperty =
+ AvaloniaProperty.Register(nameof(IsFilled));
+
+ private Points _points;
+ private bool _isDirty;
+ private IDisposable _pointsObserver;
+
+ static PolylineGeometry()
+ {
+ PointsProperty.Changed.AddClassHandler((s, e) =>
+ s.OnPointsChanged(e.OldValue as Points, e.NewValue as Points));
+ IsFilledProperty.Changed.AddClassHandler((s, _) => s.NotifyChanged());
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PolylineGeometry()
{
- _points = points;
- _isFilled = isFilled;
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
- IStreamGeometryImpl impl = factory.CreateStreamGeometry();
+ PlatformImpl = factory.CreateStreamGeometry();
+
+ Points = new Points();
+ }
- using (IStreamGeometryContextImpl context = impl.Open())
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PolylineGeometry(IEnumerable points, bool isFilled) : this()
+ {
+ Points.AddRange(points);
+ IsFilled = isFilled;
+ }
+
+ public void PrepareIfNeeded()
+ {
+ if (_isDirty)
{
- if (points.Count > 0)
+ _isDirty = false;
+
+ using (var context = ((IStreamGeometryImpl)PlatformImpl).Open())
{
- context.BeginFigure(points[0], isFilled);
- for (int i = 1; i < points.Count; i++)
+ var points = Points;
+ var isFilled = IsFilled;
+ if (points.Count > 0)
{
- context.LineTo(points[i]);
+ context.BeginFigure(points[0], isFilled);
+ for (int i = 1; i < points.Count; i++)
+ {
+ context.LineTo(points[i]);
+ }
+ context.EndFigure(isFilled);
}
- context.EndFigure(isFilled);
}
}
+ }
- PlatformImpl = impl;
+ ///
+ /// Gets or sets the figures.
+ ///
+ ///
+ /// The points.
+ ///
+ [Content]
+ public Points Points
+ {
+ get => _points;
+ set => SetAndRaise(PointsProperty, ref _points, value);
+ }
+
+ public bool IsFilled
+ {
+ get => GetValue(IsFilledProperty);
+ set => SetValue(IsFilledProperty, value);
+ }
+
+ public override IGeometryImpl PlatformImpl
+ {
+ get
+ {
+ PrepareIfNeeded();
+ return base.PlatformImpl;
+ }
+ protected set => base.PlatformImpl = value;
}
///
public override Geometry Clone()
{
- return new PolylineGeometry(new List(_points), _isFilled);
+ PrepareIfNeeded();
+ return new PolylineGeometry(Points, IsFilled);
+ }
+
+ private void OnPointsChanged(Points oldValue, Points newValue)
+ {
+ _pointsObserver?.Dispose();
+
+ _pointsObserver = newValue?.ForEachItem(f => NotifyChanged(), f => NotifyChanged(), () => NotifyChanged());
+ }
+
+ internal void NotifyChanged()
+ {
+ _isDirty = true;
}
}
}
diff --git a/src/Avalonia.Visuals/Media/RectangleGeometry.cs b/src/Avalonia.Visuals/Media/RectangleGeometry.cs
index ef7deaa6f6..1aa449d9e1 100644
--- a/src/Avalonia.Visuals/Media/RectangleGeometry.cs
+++ b/src/Avalonia.Visuals/Media/RectangleGeometry.cs
@@ -10,16 +10,51 @@ namespace Avalonia.Media
///
public class RectangleGeometry : Geometry
{
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty RectProperty =
+ AvaloniaProperty.Register(nameof(Rect));
+
+ public Rect Rect
+ {
+ get => GetValue(RectProperty);
+ set => SetValue(RectProperty, value);
+ }
+
+ static RectangleGeometry()
+ {
+ RectProperty.Changed.AddClassHandler(x => x.RectChanged);
+ }
+
///
/// Initializes a new instance of the class.
///
- /// The rectangle bounds.
- public RectangleGeometry(Rect rect)
+ public RectangleGeometry()
{
IPlatformRenderInterface factory = AvaloniaLocator.Current.GetService();
- IStreamGeometryImpl impl = factory.CreateStreamGeometry();
+ PlatformImpl = factory.CreateStreamGeometry();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The rectangle bounds.
+ public RectangleGeometry(Rect rect) : this()
+ {
+ Rect = rect;
+ }
+
+ ///
+ public override Geometry Clone()
+ {
+ return new RectangleGeometry(Rect);
+ }
- using (IStreamGeometryContextImpl context = impl.Open())
+ private void RectChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ var rect = (Rect)e.NewValue;
+ using (var context = ((IStreamGeometryImpl)PlatformImpl).Open())
{
context.BeginFigure(rect.TopLeft, true);
context.LineTo(rect.TopRight);
@@ -27,14 +62,6 @@ namespace Avalonia.Media
context.LineTo(rect.BottomLeft);
context.EndFigure(true);
}
-
- PlatformImpl = impl;
- }
-
- ///
- public override Geometry Clone()
- {
- return new RectangleGeometry(Bounds);
}
}
}
diff --git a/src/Avalonia.Visuals/Point.cs b/src/Avalonia.Visuals/Point.cs
index 7c7a3336fc..5fbd082967 100644
--- a/src/Avalonia.Visuals/Point.cs
+++ b/src/Avalonia.Visuals/Point.cs
@@ -183,7 +183,7 @@ namespace Avalonia
}
else
{
- throw new FormatException("Invalid Thickness.");
+ throw new FormatException("Invalid Point.");
}
}
diff --git a/src/Avalonia.Visuals/Points.cs b/src/Avalonia.Visuals/Points.cs
new file mode 100644
index 0000000000..867d3d4d24
--- /dev/null
+++ b/src/Avalonia.Visuals/Points.cs
@@ -0,0 +1,9 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using Avalonia.Collections;
+
+namespace Avalonia
+{
+ public sealed class Points : AvaloniaList { }
+}
diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs
index 0132c5e8a3..d562429fc7 100644
--- a/src/Avalonia.Visuals/Rect.cs
+++ b/src/Avalonia.Visuals/Rect.cs
@@ -481,5 +481,31 @@ namespace Avalonia
_width,
_height);
}
+
+ ///
+ /// Parses a string.
+ ///
+ /// The string.
+ /// The current culture.
+ /// The parsed .
+ public static Rect Parse(string s, CultureInfo culture)
+ {
+ var parts = s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
+ .Select(x => x.Trim())
+ .ToList();
+
+ if (parts.Count == 4)
+ {
+ return new Rect(
+ double.Parse(parts[0], culture),
+ double.Parse(parts[1], culture),
+ double.Parse(parts[2], culture),
+ double.Parse(parts[3], culture));
+ }
+ else
+ {
+ throw new FormatException("Invalid Rect.");
+ }
+ }
}
}
diff --git a/src/Avalonia.Visuals/RelativeRect.cs b/src/Avalonia.Visuals/RelativeRect.cs
index 3ce3797c49..a11f080e94 100644
--- a/src/Avalonia.Visuals/RelativeRect.cs
+++ b/src/Avalonia.Visuals/RelativeRect.cs
@@ -203,7 +203,7 @@ namespace Avalonia
}
else
{
- throw new FormatException("Invalid Rect.");
+ throw new FormatException("Invalid RelativeRect.");
}
}
}
diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
index d3d6776efa..c30fb3bdc3 100644
--- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
+++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
@@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.IO;
using Avalonia.Media.Immutable;
using System.Threading;
+using System.Linq;
namespace Avalonia.Rendering
{
@@ -58,7 +59,6 @@ namespace Avalonia.Rendering
_dispatcher = dispatcher ?? Dispatcher.UIThread;
_root = root;
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
- _scene = new Scene(root);
_layerFactory = layerFactory ?? new DefaultRenderLayerFactory();
_layers = new RenderLayers(_layerFactory);
_renderLoop = renderLoop;
@@ -86,7 +86,6 @@ namespace Avalonia.Rendering
_root = root;
_renderTarget = renderTarget;
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
- _scene = new Scene(root);
_layerFactory = layerFactory ?? new DefaultRenderLayerFactory();
_layers = new RenderLayers(_layerFactory);
}
@@ -122,7 +121,7 @@ namespace Avalonia.Rendering
UpdateScene();
}
- return _scene.HitTest(p, filter);
+ return _scene?.HitTest(p, filter) ?? Enumerable.Empty();
}
///
@@ -186,7 +185,7 @@ namespace Avalonia.Rendering
_dirtyRectsDisplay.Tick();
}
- if (scene.Size != Size.Empty)
+ if (scene != null && scene.Size != Size.Empty)
{
if (scene.Generation != _lastSceneId)
{
@@ -366,25 +365,32 @@ namespace Avalonia.Rendering
try
{
- var scene = _scene.Clone();
-
- if (_dirty == null)
- {
- _dirty = new DirtyVisuals();
- _sceneBuilder.UpdateAll(scene);
- }
- else if (_dirty.Count > 0)
+ if (_root.IsVisible)
{
- foreach (var visual in _dirty)
+ var scene = _scene?.Clone() ?? new Scene(_root);
+
+ if (_dirty == null)
{
- _sceneBuilder.Update(scene, visual);
+ _dirty = new DirtyVisuals();
+ _sceneBuilder.UpdateAll(scene);
+ }
+ else if (_dirty.Count > 0)
+ {
+ foreach (var visual in _dirty)
+ {
+ _sceneBuilder.Update(scene, visual);
+ }
}
- }
- Interlocked.Exchange(ref _scene, scene);
+ Interlocked.Exchange(ref _scene, scene);
- _dirty.Clear();
- (_root as IRenderRoot)?.Invalidate(new Rect(scene.Size));
+ _dirty.Clear();
+ (_root as IRenderRoot)?.Invalidate(new Rect(scene.Size));
+ }
+ else
+ {
+ Interlocked.Exchange(ref _scene, null);
+ }
}
finally
{
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
index afdf488b31..10455eb147 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
@@ -36,8 +36,14 @@ namespace Avalonia.Rendering.SceneGraph
{
Contract.Requires(scene != null);
Contract.Requires(visual != null);
+
Dispatcher.UIThread.VerifyAccess();
+ if (!scene.Root.Visual.IsVisible)
+ {
+ throw new AvaloniaInternalException("Cannot update the scene for an invisible root visual.");
+ }
+
var node = (VisualNode)scene.FindNode(visual);
if (visual == scene.Root.Visual)
diff --git a/src/Gtk/Avalonia.Cairo/Avalonia.Cairo.csproj b/src/Gtk/Avalonia.Cairo/Avalonia.Cairo.csproj
index d09fd2ddc6..460db98895 100644
--- a/src/Gtk/Avalonia.Cairo/Avalonia.Cairo.csproj
+++ b/src/Gtk/Avalonia.Cairo/Avalonia.Cairo.csproj
@@ -47,6 +47,9 @@
+
+ Properties\SharedAssemblyInfo.cs
+
diff --git a/src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs b/src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
index dc7d815ddc..709f1eb22a 100644
--- a/src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
+++ b/src/Gtk/Avalonia.Cairo/Properties/AssemblyInfo.cs
@@ -11,13 +11,6 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.Cairo")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Avalonia.Cairo")]
-[assembly: AssemblyCopyright("Copyright \u00A9 2014")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
@@ -27,19 +20,6 @@ using System.Runtime.InteropServices;
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("f999ba8b-64e7-40cc-98a4-003f1852d2a3")]
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
-
[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 3, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.Linux, 2, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
[assembly: ExportRenderingSubsystem(OperatingSystemType.OSX, 3, "Cairo", typeof(CairoPlatform), nameof(CairoPlatform.Initialize), RequiresWindowingSubsystem = "GTK")]
diff --git a/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj b/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj
index b51377f29c..6049869424 100644
--- a/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj
+++ b/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj
@@ -39,6 +39,9 @@
+
+ Properties\SharedAssemblyInfo.cs
+
diff --git a/src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs b/src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
index 6d1eb24836..67e5f6dc17 100644
--- a/src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
+++ b/src/Gtk/Avalonia.Gtk/Properties/AssemblyInfo.cs
@@ -4,23 +4,10 @@
using Avalonia.Gtk;
using Avalonia.Platform;
using System.Reflection;
-using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle("Avalonia.Gtk")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("steven")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
-// The form "{Major}.{Minor}.*" will automatically update the build and revision,
-// and "{Major}.{Minor}.{Build}.*" will update just the revision.
-[assembly: AssemblyVersion("1.0.*")]
[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 3, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 2, "GTK", typeof(GtkPlatform), nameof(GtkPlatform.Initialize))]
diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
index d292e99b30..0f4b8c2e1b 100644
--- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
+++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
@@ -24,6 +24,7 @@
true
+
KeyTransform.cs
diff --git a/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs b/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs
index 6b040240c4..034b73f699 100644
--- a/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs
+++ b/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs
@@ -1,7 +1,4 @@
-using System.Resources;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
+using System.Reflection;
using Avalonia.Gtk3;
using Avalonia.Platform;
@@ -9,27 +6,7 @@ using Avalonia.Platform;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.Gtk3")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Avalonia.Gtk3")]
-[assembly: AssemblyCopyright("Copyright © 2016")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-[assembly: NeutralResourcesLanguage("en")]
-// Version information for an assembly consists of the following four values:
-//
-// Major Version
-// Minor Version
-// Build Number
-// Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportWindowingSubsystem(OperatingSystemType.WinNT, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.Linux, 1, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
[assembly: ExportWindowingSubsystem(OperatingSystemType.OSX, 2, "GTK3", typeof(Gtk3Platform), nameof(Gtk3Platform.Initialize))]
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
index dbf985fd79..08ea6b6877 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
+++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
@@ -31,6 +31,8 @@
+
+
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs
new file mode 100644
index 0000000000..c477ff5637
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/Converters/MatrixTypeConverter.cs
@@ -0,0 +1,23 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Globalization;
+
+namespace Avalonia.Markup.Xaml.Converters
+{
+ using System.ComponentModel;
+
+ public class MatrixTypeConverter : TypeConverter
+ {
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ return sourceType == typeof(string);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ return Matrix.Parse((string)value, culture);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs b/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs
new file mode 100644
index 0000000000..c9c6462f89
--- /dev/null
+++ b/src/Markup/Avalonia.Markup.Xaml/Converters/RectTypeConverter.cs
@@ -0,0 +1,23 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Globalization;
+
+namespace Avalonia.Markup.Xaml.Converters
+{
+ using System.ComponentModel;
+
+ public class RectTypeConverter : TypeConverter
+ {
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ return sourceType == typeof(string);
+ }
+
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ return Rect.Parse((string)value, culture);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs
index 66e9a697e4..1cf5b6a58e 100644
--- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaDefaultTypeConverters.cs
@@ -32,12 +32,14 @@ namespace Avalonia.Markup.Xaml.PortableXaml
{ typeof(AvaloniaList), typeof(AvaloniaListTypeConverter) },
{ typeof(IMemberSelector), typeof(MemberSelectorTypeConverter) },
{ typeof(Point), typeof(PointTypeConverter) },
+ { typeof(Matrix), typeof(MatrixTypeConverter) },
{ typeof(IList), typeof(PointsListTypeConverter) },
{ typeof(AvaloniaProperty), typeof(AvaloniaPropertyTypeConverter) },
{ typeof(RelativePoint), typeof(RelativePointTypeConverter) },
{ typeof(RelativeRect), typeof(RelativeRectTypeConverter) },
{ typeof(RowDefinitions), typeof(RowDefinitionsTypeConverter) },
{ typeof(Size), typeof(SizeTypeConverter) },
+ { typeof(Rect), typeof(RectTypeConverter) },
{ typeof(Selector), typeof(SelectorTypeConverter)},
{ typeof(SolidColorBrush), typeof(BrushTypeConverter) },
{ typeof(Thickness), typeof(ThicknessTypeConverter) },
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
index 0b46ba1c47..69b582b009 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
@@ -119,7 +119,10 @@ namespace Avalonia.Direct2D1.Media
using (var d2dOpacityMask = CreateBrush(opacityMask, opacityMaskRect.Size))
using (var geometry = new SharpDX.Direct2D1.RectangleGeometry(_renderTarget.Factory, destRect.ToDirect2D()))
{
- d2dOpacityMask.PlatformBrush.Transform = Matrix.CreateTranslation(opacityMaskRect.Position).ToDirect2D();
+ if (d2dOpacityMask.PlatformBrush != null)
+ {
+ d2dOpacityMask.PlatformBrush.Transform = Matrix.CreateTranslation(opacityMaskRect.Position).ToDirect2D();
+ }
_renderTarget.FillGeometry(
geometry,
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs
new file mode 100644
index 0000000000..4c1e361952
--- /dev/null
+++ b/tests/Avalonia.Visuals.UnitTests/Media/MatrixTests.cs
@@ -0,0 +1,16 @@
+using System.Globalization;
+using Xunit;
+
+namespace Avalonia.Visuals.UnitTests.Media
+{
+ public class MatrixTests
+ {
+ [Fact]
+ public void Parse_Parses()
+ {
+ var matrix = Matrix.Parse("1,2,3,-4,5 6", CultureInfo.CurrentCulture);
+ var expected = new Matrix(1, 2, 3, -4, 5, 6);
+ Assert.Equal(expected, matrix);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs
index f0d41680c0..3b903b4436 100644
--- a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs
@@ -56,6 +56,7 @@ namespace Avalonia.Visuals.UnitTests.Media
}
[Theory]
+ [InlineData("F1 M24,14 A2,2,0,1,1,20,14 A2,2,0,1,1,24,14 z")] // issue #1107
[InlineData("M0 0L10 10z")]
[InlineData("M50 50 L100 100 L150 50")]
[InlineData("M50 50L100 100L150 50")]
diff --git a/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs
new file mode 100644
index 0000000000..12070bfed3
--- /dev/null
+++ b/tests/Avalonia.Visuals.UnitTests/Media/RectTests.cs
@@ -0,0 +1,16 @@
+using System.Globalization;
+using Xunit;
+
+namespace Avalonia.Visuals.UnitTests.Media
+{
+ public class RectTests
+ {
+ [Fact]
+ public void Parse_Parses()
+ {
+ var rect = Rect.Parse("1,2 3,-4", CultureInfo.CurrentCulture);
+ var expected = new Rect(1, 2, 3, -4);
+ Assert.Equal(expected, rect);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs
index 13f28018db..e65487ac44 100644
--- a/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs
+++ b/tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests_Layers.cs
@@ -273,32 +273,5 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
((MockStreamGeometryImpl)borderLayer.GeometryClip).Transform);
}
}
-
- [Fact]
- public void Hiding_Root_Should_Not_Remove_Root_Layer()
- {
- using (TestApplication())
- {
- Border border;
- var tree = new TestRoot
- {
- Child = border = new Border()
- };
-
- var layout = AvaloniaLocator.Current.GetService();
- layout.ExecuteInitialLayoutPass(tree);
-
- var scene = new Scene(tree);
- var sceneBuilder = new SceneBuilder();
- sceneBuilder.UpdateAll(scene);
-
- tree.IsVisible = false;
-
- scene = scene.Clone();
- sceneBuilder.Update(scene, tree);
-
- Assert.Equal(1, scene.Layers.Count);
- }
- }
}
}