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); - } - } } }