diff --git a/api/Avalonia.nupkg.xml b/api/Avalonia.nupkg.xml index af46d2fb2ea..992944cec56 100644 --- a/api/Avalonia.nupkg.xml +++ b/api/Avalonia.nupkg.xml @@ -1,4 +1,4 @@ - + @@ -43,12 +43,174 @@ baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.Media.PolylineGeometry.#ctor(System.Collections.Generic.IEnumerable{Avalonia.Point},System.Boolean) + baseline/Avalonia/lib/net10.0/Avalonia.Base.dll + current/Avalonia/lib/net10.0/Avalonia.Base.dll + CP0002 M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) baseline/Avalonia/lib/net10.0/Avalonia.Base.dll current/Avalonia/lib/net10.0/Avalonia.Base.dll + + CP0002 + F:Avalonia.Controls.Documents.TextElement.LetterSpacingProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Presenters.ContentPresenter.LetterSpacingProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Primitives.TemplatedControl.LetterSpacingProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Shapes.Shape.StrokeMiterLimitProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.TextBlock.LetterSpacingProperty + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.GetDataContext(Avalonia.Controls.Templates.IDataTemplate) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Controls.Templates.IDataTemplate) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Styling.IStyle) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetDataContext(Avalonia.Controls.Templates.IDataTemplate,System.Object) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.ResourceDictionary,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.Control) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.Control) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Documents.TextElement.get_LetterSpacing + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Documents.TextElement.GetLetterSpacing(Avalonia.Controls.Control) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Documents.TextElement.set_LetterSpacing(System.Double) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Documents.TextElement.SetLetterSpacing(Avalonia.Controls.Control,System.Double) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Presenters.ContentPresenter.get_LetterSpacing + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Presenters.ContentPresenter.set_LetterSpacing(System.Double) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.TemplatedControl.get_LetterSpacing + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.TemplatedControl.set_LetterSpacing(System.Double) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Shapes.Shape.get_StrokeMiterLimit + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Shapes.Shape.set_StrokeMiterLimit(System.Double) + baseline/Avalonia/lib/net10.0/Avalonia.Controls.dll + current/Avalonia/lib/net10.0/Avalonia.Controls.dll + CP0002 F:Avalonia.Media.Fonts.FontCollectionBase._glyphTypefaceCache @@ -97,12 +259,174 @@ baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0002 + M:Avalonia.Media.PolylineGeometry.#ctor(System.Collections.Generic.IEnumerable{Avalonia.Point},System.Boolean) + baseline/Avalonia/lib/net8.0/Avalonia.Base.dll + current/Avalonia/lib/net8.0/Avalonia.Base.dll + CP0002 M:Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32,Avalonia.Media.FontStyle,Avalonia.Media.FontWeight,Avalonia.Media.FontStretch,System.Globalization.CultureInfo,Avalonia.Media.Typeface@) baseline/Avalonia/lib/net8.0/Avalonia.Base.dll current/Avalonia/lib/net8.0/Avalonia.Base.dll + + CP0002 + F:Avalonia.Controls.Documents.TextElement.LetterSpacingProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Presenters.ContentPresenter.LetterSpacingProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Primitives.TemplatedControl.LetterSpacingProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.Shapes.Shape.StrokeMiterLimitProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + F:Avalonia.Controls.TextBlock.LetterSpacingProperty + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.CreatePreviewWithControl(System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.GetDataContext(Avalonia.Controls.Templates.IDataTemplate) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Controls.Templates.IDataTemplate) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.GetPreviewWith(Avalonia.Styling.IStyle) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetDataContext(Avalonia.Controls.Templates.IDataTemplate,System.Object) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.AvaloniaObject,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.ResourceDictionary,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.Control) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Controls.Templates.IDataTemplate,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.Control) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Design.SetPreviewWith(Avalonia.Styling.IStyle,Avalonia.Controls.ITemplate{Avalonia.Controls.Control}) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Documents.TextElement.get_LetterSpacing + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Documents.TextElement.GetLetterSpacing(Avalonia.Controls.Control) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Documents.TextElement.set_LetterSpacing(System.Double) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Documents.TextElement.SetLetterSpacing(Avalonia.Controls.Control,System.Double) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Presenters.ContentPresenter.get_LetterSpacing + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Presenters.ContentPresenter.set_LetterSpacing(System.Double) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.TemplatedControl.get_LetterSpacing + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Primitives.TemplatedControl.set_LetterSpacing(System.Double) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Shapes.Shape.get_StrokeMiterLimit + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + + + CP0002 + M:Avalonia.Controls.Shapes.Shape.set_StrokeMiterLimit(System.Double) + baseline/Avalonia/lib/net8.0/Avalonia.Controls.dll + current/Avalonia/lib/net8.0/Avalonia.Controls.dll + CP0002 M:Avalonia.Dialogs.Internal.ManagedFileChooserFilterViewModel.#ctor(Avalonia.Platform.Storage.FilePickerFileType) @@ -445,4 +769,4 @@ baseline/Avalonia/lib/netstandard2.0/Avalonia.Base.dll current/Avalonia/lib/netstandard2.0/Avalonia.Base.dll - + \ No newline at end of file diff --git a/src/Avalonia.Base/Media/PolylineGeometry.cs b/src/Avalonia.Base/Media/PolylineGeometry.cs index 13b11d84445..f9b9c24be96 100644 --- a/src/Avalonia.Base/Media/PolylineGeometry.cs +++ b/src/Avalonia.Base/Media/PolylineGeometry.cs @@ -27,6 +27,7 @@ public class PolylineGeometry : Geometry private IList _points; private IDisposable? _pointsObserver; + private readonly FillRule _fillRule; static PolylineGeometry() { @@ -40,8 +41,10 @@ static PolylineGeometry() public PolylineGeometry() { _points = new Points(); + _fillRule = FillRule.EvenOdd; } + /// /// /// Initializes a new instance of the class. /// @@ -49,6 +52,17 @@ public PolylineGeometry(IEnumerable points, bool isFilled) { _points = new Points(points); IsFilled = isFilled; + _fillRule = FillRule.EvenOdd; + } + + /// + /// Initializes a new instance of the class. + /// + public PolylineGeometry(IEnumerable points, bool isFilled, FillRule fillRule) + { + _points = new Points(points); + IsFilled = isFilled; + _fillRule = fillRule; } /// @@ -70,10 +84,18 @@ public bool IsFilled set => SetValue(IsFilledProperty, value); } + /// + /// Gets how the intersecting areas of the polyline are combined. + /// + public FillRule FillRule => _fillRule; + /// public override Geometry Clone() { - return new PolylineGeometry(Points, IsFilled); + return new PolylineGeometry(Points, IsFilled, _fillRule) + { + Transform = Transform + }; } private protected sealed override IGeometryImpl? CreateDefiningGeometry() @@ -83,6 +105,7 @@ public override Geometry Clone() using (var context = geometry.Open()) { + context.SetFillRule(_fillRule); var points = Points; var isFilled = IsFilled; if (points.Count > 0) diff --git a/src/Avalonia.Controls/Shapes/Polygon.cs b/src/Avalonia.Controls/Shapes/Polygon.cs index 48530432f02..d6fdcafa565 100644 --- a/src/Avalonia.Controls/Shapes/Polygon.cs +++ b/src/Avalonia.Controls/Shapes/Polygon.cs @@ -9,9 +9,12 @@ public class Polygon : Shape public static readonly StyledProperty> PointsProperty = AvaloniaProperty.Register>("Points"); + public static readonly StyledProperty FillRuleProperty = + AvaloniaProperty.Register(nameof(FillRule)); + static Polygon() { - AffectsGeometry(PointsProperty); + AffectsGeometry(PointsProperty, FillRuleProperty); } public Polygon() @@ -25,9 +28,18 @@ public IList Points set => SetValue(PointsProperty, value); } + /// + /// Gets or sets how the interior of the polygon is determined when a is applied. + /// + public FillRule FillRule + { + get => GetValue(FillRuleProperty); + set => SetValue(FillRuleProperty, value); + } + protected override Geometry CreateDefiningGeometry() { - return new PolylineGeometry { Points = Points, IsFilled = true }; + return new PolylineGeometry(Points, isFilled: true, fillRule: FillRule); } } } diff --git a/src/Avalonia.Controls/Shapes/Polyline.cs b/src/Avalonia.Controls/Shapes/Polyline.cs index 628fc70e1d9..a0675202733 100644 --- a/src/Avalonia.Controls/Shapes/Polyline.cs +++ b/src/Avalonia.Controls/Shapes/Polyline.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Avalonia; using Avalonia.Media; using Avalonia.Data; @@ -10,10 +11,13 @@ public class Polyline : Shape public static readonly StyledProperty> PointsProperty = AvaloniaProperty.Register>("Points"); + public static readonly StyledProperty FillRuleProperty = + AvaloniaProperty.Register(nameof(FillRule)); + static Polyline() { StrokeThicknessProperty.OverrideDefaultValue(1); - AffectsGeometry(PointsProperty); + AffectsGeometry(PointsProperty, FillRuleProperty); } public Polyline() @@ -27,9 +31,19 @@ public IList Points set => SetValue(PointsProperty, value); } + /// + /// Gets or sets how the interior of the polyline is determined when a is applied. + /// + public FillRule FillRule + { + get => GetValue(FillRuleProperty); + set => SetValue(FillRuleProperty, value); + } + protected override Geometry CreateDefiningGeometry() { - return new PolylineGeometry { Points = Points, IsFilled = false }; + var isFilled = Fill != null; + return new PolylineGeometry(Points, isFilled, FillRule); } } } diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/PolygonTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/PolygonTests.cs index 1775a01cd3d..ccb04fa4986 100644 --- a/tests/Avalonia.Controls.UnitTests/Shapes/PolygonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Shapes/PolygonTests.cs @@ -1,5 +1,6 @@ using System.Collections.ObjectModel; using Avalonia.Controls.Shapes; +using Avalonia.Media; using Avalonia.UnitTests; using Xunit; @@ -25,4 +26,51 @@ public void Polygon_Will_Update_Geometry_On_Shapes_Collection_Content_Change() root.Child = null; } + + [Fact] + public void FillRule_On_Polygon_Is_Applied_To_DefiningGeometry() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Polygon + { + Points = new Points { new Point(0, 0), new Point(10, 10), new Point(20, 0) }, + FillRule = FillRule.NonZero + }; + + target.Measure(Size.Infinity); + + var geometry = Assert.IsType(target.DefiningGeometry); + Assert.Equal(FillRule.NonZero, geometry.FillRule); + } + + [Fact] + public void Polygon_Equals_Closed_Polyline_Bounds() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var polyline = new Polyline + { + Points = new Points + { + new Point(0, 0), + new Point(10, 0), + new Point(10, 10), + new Point(0, 10), + new Point(0, 0) + }, + FillRule = FillRule.NonZero + }; + + var polygon = new Polygon + { + Points = new Points { new Point(0, 0), new Point(10, 0), new Point(10, 10), new Point(0, 10) }, + FillRule = FillRule.NonZero + }; + + polyline.Measure(Size.Infinity); + polygon.Measure(Size.Infinity); + + Assert.Equal(polygon.DefiningGeometry!.Bounds, polyline.DefiningGeometry!.Bounds); + } } diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/PolylineTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/PolylineTests.cs index 971cd81f4a6..2b73ec21a51 100644 --- a/tests/Avalonia.Controls.UnitTests/Shapes/PolylineTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Shapes/PolylineTests.cs @@ -1,5 +1,6 @@ using System.Collections.ObjectModel; using Avalonia.Controls.Shapes; +using Avalonia.Media; using Avalonia.UnitTests; using Xunit; @@ -25,4 +26,66 @@ public void Polyline_Will_Update_Geometry_On_Shapes_Collection_Content_Change() root.Child = null; } + + [Fact] + public void FillRule_On_Polyline_Is_Applied_To_DefiningGeometry() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Polyline + { + Points = new Points { new Point(0, 0), new Point(10, 10), new Point(20, 0) }, + Fill = Brushes.Red, + FillRule = FillRule.NonZero + }; + + target.Measure(Size.Infinity); + + var geometry = Assert.IsType(target.DefiningGeometry); + Assert.Equal(FillRule.NonZero, geometry.FillRule); + Assert.True(geometry.IsFilled); + } + + [Fact] + public void FillRule_Differs_Between_EvenOdd_And_NonZero() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var evenOdd = new Polyline + { + Points = new Points { new Point(0, 0), new Point(10, 10), new Point(20, 0) }, + Fill = Brushes.Red, + FillRule = FillRule.EvenOdd + }; + + var nonZero = new Polyline + { + Points = new Points { new Point(0, 0), new Point(10, 10), new Point(20, 0) }, + Fill = Brushes.Red, + FillRule = FillRule.NonZero + }; + + evenOdd.Measure(Size.Infinity); + nonZero.Measure(Size.Infinity); + + Assert.Equal(FillRule.EvenOdd, Assert.IsType(evenOdd.DefiningGeometry).FillRule); + Assert.Equal(FillRule.NonZero, Assert.IsType(nonZero.DefiningGeometry).FillRule); + } + + [Fact] + public void When_Fill_Is_Null_Polyline_Geometry_Is_Not_Filled() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Polyline + { + Points = new Points { new Point(0, 0), new Point(10, 10), new Point(20, 0) }, + FillRule = FillRule.NonZero, + Fill = null + }; + + target.Measure(Size.Infinity); + var geometry = Assert.IsType(target.DefiningGeometry); + Assert.False(geometry.IsFilled); + } } diff --git a/tests/Avalonia.RenderTests/Shapes/PolygonTests.cs b/tests/Avalonia.RenderTests/Shapes/PolygonTests.cs index 301ee46cf7d..f2b9ec31a94 100644 --- a/tests/Avalonia.RenderTests/Shapes/PolygonTests.cs +++ b/tests/Avalonia.RenderTests/Shapes/PolygonTests.cs @@ -13,6 +13,39 @@ public PolygonTests() { } + [Theory] + [InlineData(FillRule.EvenOdd)] + [InlineData(FillRule.NonZero)] + public async Task Polygon_FillRule(FillRule fillRule) + { + var target = new Decorator + { + Padding = new Thickness(8), + Width = 220, + Height = 220, + Child = new Polygon + { + Stroke = Brushes.Black, + StrokeThickness = 2, + Fill = Brushes.Gold, + Points = new Points + { + new Point(50, 0), + new Point(21, 90), + new Point(98, 35), + new Point(2, 35), + new Point(79, 90) + }, + Stretch = Stretch.Uniform, + FillRule = fillRule + } + }; + + var testName = $"{nameof(Polygon_FillRule)}_{fillRule}"; + await RenderToFile(target, testName); + CompareImages(testName); + } + [Fact] public async Task Polygon_1px_Stroke() { diff --git a/tests/Avalonia.RenderTests/Shapes/PolylineTests.cs b/tests/Avalonia.RenderTests/Shapes/PolylineTests.cs index e110b4e30e8..dd4f6828dd1 100644 --- a/tests/Avalonia.RenderTests/Shapes/PolylineTests.cs +++ b/tests/Avalonia.RenderTests/Shapes/PolylineTests.cs @@ -13,6 +13,75 @@ public PolylineTests() { } + [Theory] + [InlineData(FillRule.EvenOdd)] + [InlineData(FillRule.NonZero)] + public async Task Polyline_FillRule(FillRule fillRule) + { + var target = new Decorator + { + Padding = new Thickness(8), + Width = 260, + Height = 180, + Child = new Polyline + { + Stroke = Brushes.Black, + StrokeThickness = 2, + Fill = Brushes.OrangeRed, + Points = new Points + { + new Point(10, 170), + new Point(60, 20), + new Point(110, 170), + new Point(20, 70), + new Point(240, 70), + new Point(130, 170), + new Point(190, 20), + new Point(10, 170), + }, + Stretch = Stretch.Uniform, + FillRule = fillRule + } + }; + + var testName = $"{nameof(Polyline_FillRule)}_{fillRule}"; + await RenderToFile(target, testName); + CompareImages(testName); + } + + [Fact] + public async Task Polyline_FillRule_NoFill() + { + var target = new Decorator + { + Padding = new Thickness(8), + Width = 260, + Height = 180, + Child = new Polyline + { + Stroke = Brushes.Black, + StrokeThickness = 2, + Fill = null, + Points = new Points + { + new Point(10, 170), + new Point(60, 20), + new Point(110, 170), + new Point(20, 70), + new Point(240, 70), + new Point(130, 170), + new Point(190, 20), + new Point(10, 170), + }, + Stretch = Stretch.Uniform, + FillRule = FillRule.EvenOdd + } + }; + + await RenderToFile(target); + CompareImages(); + } + [Fact] public async Task Polyline_1px_Stroke() { diff --git a/tests/TestFiles/Skia/Shapes/Polygon/Polygon_FillRule_EvenOdd.expected.png b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_FillRule_EvenOdd.expected.png new file mode 100644 index 00000000000..db832be43b3 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_FillRule_EvenOdd.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polygon/Polygon_FillRule_NonZero.expected.png b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_FillRule_NonZero.expected.png new file mode 100644 index 00000000000..152f959fd94 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polygon/Polygon_FillRule_NonZero.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polyline/Polyline_FillRule_EvenOdd.expected.png b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_FillRule_EvenOdd.expected.png new file mode 100644 index 00000000000..4b1348f5b63 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_FillRule_EvenOdd.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polyline/Polyline_FillRule_NoFill.expected.png b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_FillRule_NoFill.expected.png new file mode 100644 index 00000000000..5837133ef4b Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_FillRule_NoFill.expected.png differ diff --git a/tests/TestFiles/Skia/Shapes/Polyline/Polyline_FillRule_NonZero.expected.png b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_FillRule_NonZero.expected.png new file mode 100644 index 00000000000..b697dc80355 Binary files /dev/null and b/tests/TestFiles/Skia/Shapes/Polyline/Polyline_FillRule_NonZero.expected.png differ