diff --git a/src/Avalonia.Controls/Image.cs b/src/Avalonia.Controls/Image.cs index 802b700a07..40203bab02 100644 --- a/src/Avalonia.Controls/Image.cs +++ b/src/Avalonia.Controls/Image.cs @@ -26,6 +26,7 @@ namespace Avalonia.Controls static Image() { AffectsRender(SourceProperty, StretchProperty); + AffectsMeasure(SourceProperty, StretchProperty); } /// diff --git a/src/Avalonia.Controls/Presenters/ContentPresenter.cs b/src/Avalonia.Controls/Presenters/ContentPresenter.cs index 8d703cfc1c..83d8616e90 100644 --- a/src/Avalonia.Controls/Presenters/ContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ContentPresenter.cs @@ -197,13 +197,6 @@ namespace Avalonia.Controls.Presenters } } - /// - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - _dataTemplate = null; - } - /// /// Updates the control based on the control's . /// @@ -268,6 +261,7 @@ namespace Avalonia.Controls.Presenters protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) { base.OnAttachedToLogicalTree(e); + _dataTemplate = null; _createdChild = false; InvalidateMeasure(); } diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs index 0fd6593fbe..a91e6a3a48 100644 --- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs +++ b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs @@ -88,24 +88,82 @@ namespace Avalonia.Controls.Utils } else { - var borderThickness = borders.Left; - var cornerRadius = (float)radii.TopLeft; - var rect = new Rect(size); + var borderThickness = borders.Top; + var top = borderThickness * 0.5; + var cornerRadius = (float)Math.Max(0, radii.TopLeft - borderThickness - top); if (background != null) { - context.FillRectangle(background, rect.Deflate(borders), cornerRadius); + var topLeft = new Point(borders.Left, borders.Top); + var bottomRight = new Point(size.Width - borders.Right, size.Height - borders.Bottom); + var innerRect = new Rect(topLeft, bottomRight); + context.FillRectangle(background, innerRect, cornerRadius); } if (borderBrush != null && borderThickness > 0) { - context.DrawRectangle(new Pen(borderBrush, borderThickness), rect.Deflate(borderThickness), cornerRadius); + var topLeft = new Point(top, top); + var bottomRight = new Point(size.Width - top, size.Height - top); + var outerRect = new Rect(topLeft, bottomRight); + context.DrawRectangle(new Pen(borderBrush, borderThickness), outerRect, (float)radii.TopLeft); } } + } + + private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderGeometryKeypoints keypoints) + { + context.BeginFigure(keypoints.TopLeft, true); + + // Top + context.LineTo(keypoints.TopRight); + + // TopRight corner + var radiusX = boundRect.TopRight.X - keypoints.TopRight.X; + var radiusY = keypoints.RightTop.Y - boundRect.TopRight.Y; + if (radiusX != 0 || radiusY != 0) + { + context.ArcTo(keypoints.RightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise); + } + + // Right + context.LineTo(keypoints.RightBottom); + + // BottomRight corner + radiusX = boundRect.BottomRight.X - keypoints.BottomRight.X; + radiusY = boundRect.BottomRight.Y - keypoints.RightBottom.Y; + if (radiusX != 0 || radiusY != 0) + { + context.ArcTo(keypoints.BottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); + } + + // Bottom + context.LineTo(keypoints.BottomLeft); + + // BottomLeft corner + radiusX = keypoints.BottomLeft.X - boundRect.BottomLeft.X; + radiusY = boundRect.BottomLeft.Y - keypoints.LeftBottom.Y; + if (radiusX != 0 || radiusY != 0) + { + context.ArcTo(keypoints.LeftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); + } + + // Left + context.LineTo(keypoints.LeftTop); + + // TopLeft corner + radiusX = keypoints.TopLeft.X - boundRect.TopLeft.X; + radiusY = keypoints.LeftTop.Y - boundRect.TopLeft.Y; + + if (radiusX != 0 || radiusY != 0) + { + context.ArcTo(keypoints.TopLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); + } + + context.EndFigure(true); } private class BorderGeometryKeypoints - { + { internal BorderGeometryKeypoints(Rect boundRect, Thickness borderThickness, CornerRadius cornerRadius, bool inner) { var left = 0.5 * borderThickness.Left; @@ -135,25 +193,24 @@ namespace Avalonia.Controls.Utils } else { - leftTopY = cornerRadius.TopLeft + top + boundRect.TopLeft.Y; - topLeftX = cornerRadius.TopLeft + left + boundRect.TopLeft.X; + topLeftX = cornerRadius.TopLeft + left + boundRect.TopLeft.X; topRightX = boundRect.Width - (cornerRadius.TopRight + right) + boundRect.TopLeft.X; - rightTopY = cornerRadius.TopRight + top + boundRect.TopLeft.Y; + rightTopY = cornerRadius.TopRight + top + boundRect.TopLeft.Y; rightBottomY = boundRect.Height - (cornerRadius.BottomRight + bottom) + boundRect.TopLeft.Y; - bottomRightX = boundRect.Width - (cornerRadius.BottomRight + right) + boundRect.TopLeft.X; - bottomLeftX = cornerRadius.BottomLeft + left + boundRect.TopLeft.X; + bottomRightX = boundRect.Width - (cornerRadius.BottomRight + right) + boundRect.TopLeft.X; + bottomLeftX = cornerRadius.BottomLeft + left + boundRect.TopLeft.X; leftBottomY = boundRect.Height - (cornerRadius.BottomLeft + bottom) + boundRect.TopLeft.Y; - } + } - var leftTopX = boundRect.TopLeft.X; - var topLeftY = boundRect.TopLeft.Y; + var leftTopX = boundRect.TopLeft.X; + var topLeftY = boundRect.TopLeft.Y; var topRightY = boundRect.TopLeft.Y; - var rightTopX = boundRect.Width + boundRect.TopLeft.X; - var rightBottomX = boundRect.Width + boundRect.TopLeft.X; - var bottomRightY = boundRect.Height + boundRect.TopLeft.Y; + var rightTopX = boundRect.Width + boundRect.TopLeft.X; + var rightBottomX = boundRect.Width + boundRect.TopLeft.X; + var bottomRightY = boundRect.Height + boundRect.TopLeft.Y; var bottomLeftY = boundRect.Height + boundRect.TopLeft.Y; - var leftBottomX = boundRect.TopLeft.X; + var leftBottomX = boundRect.TopLeft.X; LeftTop = new Point(leftTopX, leftTopY); TopLeft = new Point(topLeftX, topLeftY); @@ -164,7 +221,7 @@ namespace Avalonia.Controls.Utils BottomLeft = new Point(bottomLeftX, bottomLeftY); LeftBottom = new Point(leftBottomX, leftBottomY); - //Fix overlap + // Fix overlap if (TopLeft.X > TopRight.X) { var scaledX = topLeftX / (topLeftX + topRightX) * boundRect.Width; @@ -194,66 +251,21 @@ namespace Avalonia.Controls.Utils } } - internal Point LeftTop { get; private set; } - internal Point TopLeft { get; private set; } - internal Point TopRight { get; private set; } - internal Point RightTop { get; private set; } - internal Point RightBottom { get; private set; } - internal Point BottomRight { get; private set; } - internal Point BottomLeft { get; private set; } - internal Point LeftBottom { get; private set; } - } + internal Point LeftTop { get; } - private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderGeometryKeypoints keypoints) - { - context.BeginFigure(keypoints.TopLeft, true); + internal Point TopLeft { get; } - //Top - context.LineTo(keypoints.TopRight); + internal Point TopRight { get; } - //TopRight corner - var radiusX = boundRect.TopRight.X - keypoints.TopRight.X; - var radiusY = keypoints.RightTop.Y - boundRect.TopRight.Y; - if (radiusX != 0 || radiusY != 0) - { - context.ArcTo(keypoints.RightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise); - } + internal Point RightTop { get; } - //Right - context.LineTo(keypoints.RightBottom); + internal Point RightBottom { get; } - //BottomRight corner - radiusX = boundRect.BottomRight.X - keypoints.BottomRight.X; - radiusY = boundRect.BottomRight.Y - keypoints.RightBottom.Y; - if (radiusX != 0 || radiusY != 0) - { - context.ArcTo(keypoints.BottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); - } - - //Bottom - context.LineTo(keypoints.BottomLeft); - - //BottomLeft corner - radiusX = keypoints.BottomLeft.X - boundRect.BottomLeft.X; - radiusY = boundRect.BottomLeft.Y - keypoints.LeftBottom.Y; - if (radiusX != 0 || radiusY != 0) - { - context.ArcTo(keypoints.LeftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); - } + internal Point BottomRight { get; } - //Left - context.LineTo(keypoints.LeftTop); - - //TopLeft corner - radiusX = keypoints.TopLeft.X - boundRect.TopLeft.X; - radiusY = keypoints.LeftTop.Y - boundRect.TopLeft.Y; - - if (radiusX != 0 || radiusY != 0) - { - context.ArcTo(keypoints.TopLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise); - } + internal Point BottomLeft { get; } - context.EndFigure(true); + internal Point LeftBottom { get; } } } } diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index 656526890a..8852883dcc 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -107,7 +107,7 @@ namespace Avalonia.Media { if(!ReadCommand(ref span, out var command, out var relative)) { - return; + break; } bool initialCommand = true; @@ -565,4 +565,4 @@ namespace Avalonia.Media return true; } } -} \ No newline at end of file +} diff --git a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github index 8abbe09592..8e4700d4b2 160000 --- a/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github +++ b/src/Markup/Avalonia.Markup.Xaml/PortableXaml/portable.xaml.github @@ -1 +1 @@ -Subproject commit 8abbe09592668efb573ac4d5548ba2d7e464ba78 +Subproject commit 8e4700d4b24935ed5400e5e0d6fce96b5b4a317a diff --git a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs index 8dca52e6a7..5570378063 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Media/PathMarkupParserTests.cs @@ -9,6 +9,8 @@ namespace Avalonia.Visuals.UnitTests.Media { using System.Globalization; using System.IO; + using Avalonia.Platform; + using Moq; public class PathMarkupParserTests { @@ -18,7 +20,7 @@ namespace Avalonia.Visuals.UnitTests.Media var pathGeometry = new PathGeometry(); using (var context = new PathGeometryContext(pathGeometry)) using (var parser = new PathMarkupParser(context)) - { + { parser.Parse("M10 10"); var figure = pathGeometry.Figures[0]; @@ -202,6 +204,25 @@ namespace Avalonia.Visuals.UnitTests.Media } } + [Theory] + [InlineData("M0 0L10 10")] + [InlineData("M0 0L10 10z")] + [InlineData("M0 0L10 10 \n ")] + [InlineData("M0 0L10 10z \n ")] + [InlineData("M0 0L10 10 ")] + [InlineData("M0 0L10 10z ")] + public void Should_AlwaysEndFigure(string pathData) + { + var context = new Mock(); + + using (var parser = new PathMarkupParser(context.Object)) + { + parser.Parse(pathData); + } + + context.Verify(v => v.EndFigure(It.IsAny()), Times.AtLeastOnce()); + } + [Theory] [InlineData("0 0")] [InlineData("j")] @@ -215,4 +236,4 @@ namespace Avalonia.Visuals.UnitTests.Media } } } -} \ No newline at end of file +}