浏览代码

Requested changes

Benedikt Schroeder 7 年之前
父节点
当前提交
50dd6cb66d

+ 5 - 156
src/Avalonia.Controls/Border.cs

@@ -1,8 +1,8 @@
 // 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 Avalonia;
+using Avalonia.Controls.Utils;
 using Avalonia.Media;
 
 namespace Avalonia.Controls
@@ -10,7 +10,7 @@ namespace Avalonia.Controls
     /// <summary>
     /// A control which decorates a child with a border and background.
     /// </summary>
-    public class Border : Decorator
+    public partial class Border : Decorator
     {
         /// <summary>
         /// Defines the <see cref="Background"/> property.
@@ -36,7 +36,7 @@ namespace Avalonia.Controls
         public static readonly StyledProperty<CornerRadius> CornerRadiusProperty =
             AvaloniaProperty.Register<Border, CornerRadius>(nameof(CornerRadius));
 
-        private readonly BorderRenderer _borderRenderer = new BorderRenderer();
+        private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
 
         /// <summary>
         /// Initializes static members of the <see cref="Border"/> class.
@@ -88,7 +88,7 @@ namespace Avalonia.Controls
         /// <param name="context">The drawing context.</param>
         public override void Render(DrawingContext context)
         {
-            _borderRenderer.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush);
+            _borderRenderHelper.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush);
         }
 
         /// <summary>
@@ -114,7 +114,7 @@ namespace Avalonia.Controls
                 Child.Arrange(new Rect(finalSize).Deflate(padding));
             }
 
-            _borderRenderer.Update(finalSize, BorderThickness, CornerRadius);
+            _borderRenderHelper.Update(finalSize, BorderThickness, CornerRadius);           
 
             return finalSize;
         }
@@ -135,156 +135,5 @@ namespace Avalonia.Controls
 
             return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top);
         }
-
-        internal class BorderRenderer
-        {
-            private bool _useComplexRendering;
-            private StreamGeometry _backgroundGeometryCache;
-            private StreamGeometry _borderGeometryCache;
-
-            public void Update(Size finalSize, Thickness borderThickness, CornerRadius cornerRadius)
-            {
-                if (borderThickness.IsUniform && cornerRadius.IsUniform)
-                {
-                    _backgroundGeometryCache = null;
-                    _borderGeometryCache = null;
-                    _useComplexRendering = false;
-                }
-                else
-                {
-                    _useComplexRendering = true;
-
-                    var boundRect = new Rect(finalSize);
-                    var innerRect = new Rect(borderThickness.Left, borderThickness.Top,
-                        Math.Max(0, boundRect.Width - borderThickness.Right), 
-                        Math.Max(0, boundRect.Height - borderThickness.Bottom));
-
-                    StreamGeometry backgroundGeometry = null;
-
-                    if (!innerRect.Width.Equals(0) && !innerRect.Height.Equals(0))
-                    {
-                        backgroundGeometry = new StreamGeometry();
-
-                        using (var ctx = backgroundGeometry.Open())
-                        {
-                            CreateGeometry(ctx, innerRect, cornerRadius);
-                        }
-
-                        _backgroundGeometryCache = backgroundGeometry;
-                    }
-                    else
-                    {
-                        _backgroundGeometryCache = null;
-                    }                  
-
-                    if (!boundRect.Width.Equals(0) && !innerRect.Height.Equals(0))
-                    {
-                        var borderGeometry = new StreamGeometry();
-
-                        using (var ctx = borderGeometry.Open())
-                        {
-                            CreateGeometry(ctx, boundRect, cornerRadius);
-
-                            if (backgroundGeometry != null)
-                            {
-                                CreateGeometry(ctx, innerRect, cornerRadius);
-                            }
-                        }
-
-                        _borderGeometryCache = borderGeometry;
-                    }
-                    else
-                    {
-                        _borderGeometryCache = null;
-                    }
-                }
-            }
-
-            private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, CornerRadius cornerRadius)
-            {
-                var topLeft = new Point(boundRect.X + cornerRadius.TopLeft, boundRect.Y);
-                var topRight = new Point(boundRect.Width - cornerRadius.TopRight, boundRect.Y);
-                var rightTop = new Point(boundRect.Width, boundRect.Y + cornerRadius.TopRight);
-                var rightBottom = new Point(boundRect.Width, boundRect.Height - cornerRadius.BottomRight);
-                var bottomRight = new Point(boundRect.Width - cornerRadius.BottomRight, boundRect.Height);
-                var bottomLeft = new Point(boundRect.X + cornerRadius.BottomLeft, boundRect.Height);
-                var leftBottom = new Point(boundRect.X, boundRect.Height - cornerRadius.BottomLeft);
-                var leftTop = new Point(boundRect.X, boundRect.Y + cornerRadius.TopLeft);
-
-                context.BeginFigure(topLeft, true);
-
-                //Top
-                context.LineTo(topRight);
-
-                //TopRight corner
-                if (topRight != rightTop)
-                {
-                    context.ArcTo(rightTop, new Size(cornerRadius.TopRight, cornerRadius.TopRight), 0, false, SweepDirection.Clockwise);
-                }
-
-                //Right
-                context.LineTo(rightBottom);
-
-                //BottomRight corner
-                if (rightBottom != bottomRight)
-                {
-                    context.ArcTo(bottomRight, new Size(cornerRadius.BottomRight, cornerRadius.BottomRight), 0, false, SweepDirection.Clockwise);
-                }
-
-                //Bottom
-                context.LineTo(bottomLeft);
-
-                //BottomLeft corner
-                if (bottomLeft != leftBottom)
-                {
-                    context.ArcTo(leftBottom, new Size(cornerRadius.BottomLeft, cornerRadius.BottomLeft), 0, false, SweepDirection.Clockwise);
-                }
-
-                //Left
-                context.LineTo(leftTop);
-
-                //TopLeft corner
-                if (leftTop != topLeft)
-                {
-                    context.ArcTo(topLeft, new Size(cornerRadius.TopLeft, cornerRadius.TopLeft), 0, false, SweepDirection.Clockwise);
-                }
-
-                context.EndFigure(true);
-            }
-
-            public void Render(DrawingContext context, Size size, Thickness borders, CornerRadius radii, IBrush background, IBrush borderBrush)
-            {
-                if (_useComplexRendering)
-                {
-                    var backgroundGeometry = _backgroundGeometryCache;
-                    if (backgroundGeometry != null)
-                    {
-                        context.DrawGeometry(background, null, backgroundGeometry);
-                    }
-
-                    var borderGeometry = _borderGeometryCache;
-                    if (borderGeometry != null)
-                    {
-                        context.DrawGeometry(borderBrush, null, borderGeometry);
-                    }
-                }
-                else
-                {
-                    var borderThickness = borders.Left;
-                    var cornerRadius = (float)radii.TopLeft;
-                    var rect = new Rect(size);
-
-                    if (background != null)
-                    {
-                        context.FillRectangle(background, rect.Deflate(borders), cornerRadius);
-                    }
-
-                    if (borderBrush != null && borderThickness > 0)
-                    {
-                        context.DrawRectangle(new Pen(borderBrush, borderThickness), rect.Deflate(borderThickness), cornerRadius);
-                    }
-                }
-            }
-        }
     }
 }

+ 4 - 3
src/Avalonia.Controls/Presenters/ContentPresenter.cs

@@ -4,6 +4,7 @@
 using System;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Templates;
+using Avalonia.Controls.Utils;
 using Avalonia.Layout;
 using Avalonia.LogicalTree;
 using Avalonia.Media;
@@ -76,12 +77,12 @@ namespace Avalonia.Controls.Presenters
         /// Defines the <see cref="Padding"/> property.
         /// </summary>
         public static readonly StyledProperty<Thickness> PaddingProperty =
-            Border.PaddingProperty.AddOwner<ContentPresenter>();
+            Decorator.PaddingProperty.AddOwner<ContentPresenter>();
 
         private IControl _child;
         private bool _createdChild;
         private IDataTemplate _dataTemplate;
-        private readonly Border.BorderRenderer _borderRenderer = new Border.BorderRenderer();
+        private readonly BorderRenderHelper _borderRenderer = new BorderRenderHelper();
 
         /// <summary>
         /// Initializes static members of the <see cref="ContentPresenter"/> class.
@@ -438,7 +439,7 @@ namespace Avalonia.Controls.Presenters
         {
             var result = (VisualRoot as ILayoutRoot)?.LayoutScaling ?? 1.0;
 
-            if (result.Equals(0) || double.IsNaN(result) || double.IsInfinity(result))
+            if (result == 0 || double.IsNaN(result) || double.IsInfinity(result))
             {
                 throw new Exception($"Invalid LayoutScaling returned from {VisualRoot.GetType()}");
             }

+ 279 - 0
src/Avalonia.Controls/Utils/BorderRenderHelper.cs

@@ -0,0 +1,279 @@
+// 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 Avalonia.Media;
+
+namespace Avalonia.Controls.Utils
+{
+    internal class BorderRenderHelper
+    {
+        private bool _useComplexRendering;
+        private StreamGeometry _backgroundGeometryCache;
+        private StreamGeometry _borderGeometryCache;
+
+        public void Update(Size finalSize, Thickness borderThickness, CornerRadius cornerRadius)
+        {
+            if (borderThickness.IsUniform && cornerRadius.IsUniform)
+            {
+                _backgroundGeometryCache = null;
+                _borderGeometryCache = null;
+                _useComplexRendering = false;
+            }
+            else
+            {
+                _useComplexRendering = true;
+
+                var boundRect = new Rect(finalSize);
+                var innerRect = boundRect.Deflate(borderThickness);
+                var innerCoordinates = new BorderCoordinates(cornerRadius, borderThickness, false);
+
+                StreamGeometry backgroundGeometry = null;
+
+                if (innerRect.Width != 0 && innerRect.Height != 0)
+                {
+                    backgroundGeometry = new StreamGeometry();
+
+                    using (var ctx = backgroundGeometry.Open())
+                    {
+                        CreateGeometry(ctx, innerRect, innerCoordinates);
+                    }
+
+                    _backgroundGeometryCache = backgroundGeometry;
+                }
+                else
+                {
+                    _backgroundGeometryCache = null;
+                }
+
+                if (boundRect.Width != 0 && innerRect.Height != 0)
+                {
+                    var outerCoordinates = new BorderCoordinates(cornerRadius, borderThickness, true);
+                    var borderGeometry = new StreamGeometry();
+
+                    using (var ctx = borderGeometry.Open())
+                    {
+                        CreateGeometry(ctx, boundRect, outerCoordinates);
+
+                        if (backgroundGeometry != null)
+                        {
+                            CreateGeometry(ctx, innerRect, innerCoordinates);
+                        }
+                    }
+
+                    _borderGeometryCache = borderGeometry;
+                }
+                else
+                {
+                    _borderGeometryCache = null;
+                }
+            }
+        }
+
+        public void Render(DrawingContext context, Size size, Thickness borders, CornerRadius radii, IBrush background, IBrush borderBrush)
+        {
+            if (_useComplexRendering)
+            {
+                var backgroundGeometry = _backgroundGeometryCache;
+                if (backgroundGeometry != null)
+                {
+                    context.DrawGeometry(background, null, backgroundGeometry);
+                }
+
+                var borderGeometry = _borderGeometryCache;
+                if (borderGeometry != null)
+                {
+                    context.DrawGeometry(borderBrush, null, borderGeometry);
+                }
+            }
+            else
+            {
+                var borderThickness = borders.Left;
+                var cornerRadius = (float)radii.TopLeft;
+                var rect = new Rect(size);
+
+                if (background != null)
+                {
+                    context.FillRectangle(background, rect.Deflate(borders), cornerRadius);
+                }
+
+                if (borderBrush != null && borderThickness > 0)
+                {
+                    context.DrawRectangle(new Pen(borderBrush, borderThickness), rect.Deflate(borderThickness), cornerRadius);
+                }
+            }
+        }
+
+        private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderCoordinates borderCoordinates)
+        {
+            var topLeft = new Point(borderCoordinates.LeftTop, 0);
+            var topRight = new Point(boundRect.Width - borderCoordinates.RightTop, 0);
+            var rightTop = new Point(boundRect.Width, borderCoordinates.TopRight);
+            var rightBottom = new Point(boundRect.Width, boundRect.Height - borderCoordinates.BottomRight);
+            var bottomRight = new Point(boundRect.Width - borderCoordinates.RightBottom, boundRect.Height);
+            var bottomLeft = new Point(borderCoordinates.LeftBottom, boundRect.Height);
+            var leftBottom = new Point(0, boundRect.Height - borderCoordinates.BottomLeft);
+            var leftTop = new Point(0, borderCoordinates.TopLeft);
+
+
+            if (topLeft.X > topRight.X)
+            {
+                var scaledX = borderCoordinates.LeftTop / (borderCoordinates.LeftTop + borderCoordinates.RightTop) * boundRect.Width;
+                topLeft = new Point(scaledX, topLeft.Y);
+                topRight = new Point(scaledX, topRight.Y);
+            }
+
+            if (rightTop.Y > rightBottom.Y)
+            {
+                var scaledY = borderCoordinates.TopRight / (borderCoordinates.TopRight + borderCoordinates.BottomRight) * boundRect.Height;
+                rightTop = new Point(rightTop.X, scaledY);
+                rightBottom = new Point(rightBottom.X, scaledY);
+            }
+
+            if (bottomRight.X < bottomLeft.X)
+            {
+                var scaledX = borderCoordinates.LeftBottom / (borderCoordinates.LeftBottom + borderCoordinates.RightBottom) * boundRect.Width;
+                bottomRight = new Point(scaledX, bottomRight.Y);
+                bottomLeft = new Point(scaledX, bottomLeft.Y);
+            }
+
+            if (leftBottom.Y < leftTop.Y)
+            {
+                var scaledY = borderCoordinates.TopLeft / (borderCoordinates.TopLeft + borderCoordinates.BottomLeft) * boundRect.Height;
+                leftBottom = new Point(leftBottom.X, scaledY);
+                leftTop = new Point(leftTop.X, scaledY);
+            }
+
+            var offset = new Vector(boundRect.TopLeft.X, boundRect.TopLeft.Y);
+            topLeft += offset;
+            topRight += offset;
+            rightTop += offset;
+            rightBottom += offset;
+            bottomRight += offset;
+            bottomLeft += offset;
+            leftBottom += offset;
+            leftTop += offset;
+
+            context.BeginFigure(topLeft, true);
+
+            //Top
+            context.LineTo(topRight);
+
+            //TopRight corner
+            var radiusX = boundRect.TopRight.X - topRight.X;
+            var radiusY = rightTop.Y - boundRect.TopRight.Y;
+            if (radiusX != 0 || radiusY != 0)
+            {
+                context.ArcTo(rightTop, new Size(radiusY, radiusY), 0, false, SweepDirection.Clockwise);
+            }
+
+            //Right
+            context.LineTo(rightBottom);
+
+            //BottomRight corner
+            radiusX = boundRect.BottomRight.X - bottomRight.X;
+            radiusY = boundRect.BottomRight.Y - rightBottom.Y;
+            if (radiusX != 0 || radiusY != 0)
+            {
+                context.ArcTo(bottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
+            }
+
+            //Bottom
+            context.LineTo(bottomLeft);
+
+            //BottomLeft corner
+            radiusX = bottomLeft.X - boundRect.BottomLeft.X;
+            radiusY = boundRect.BottomLeft.Y - leftBottom.Y;
+            if (radiusX != 0 || radiusY != 0)
+            {
+                context.ArcTo(leftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
+            }
+
+            //Left
+            context.LineTo(leftTop);
+
+            //TopLeft corner
+            radiusX = topLeft.X - boundRect.TopLeft.X;
+            radiusY = leftTop.Y - boundRect.TopLeft.Y;
+
+            if (radiusX != 0 || radiusY != 0)
+            {
+                context.ArcTo(topLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise);
+            }
+
+            context.EndFigure(true);
+        }
+
+        private struct BorderCoordinates
+        {
+            internal BorderCoordinates(CornerRadius cornerRadius, Thickness borderThickness, bool isOuter)
+            {
+                var left = 0.5 * borderThickness.Left;
+                var top = 0.5 * borderThickness.Top;
+                var right = 0.5 * borderThickness.Right;
+                var bottom = 0.5 * borderThickness.Bottom;
+
+                if (isOuter)
+                {
+                    if (cornerRadius.TopLeft == 0)
+                    {
+                        LeftTop = TopLeft = 0.0;
+                    }
+                    else
+                    {
+                        LeftTop = cornerRadius.TopLeft + left;
+                        TopLeft = cornerRadius.TopLeft + top;
+                    }
+                    if (cornerRadius.TopRight == 0)
+                    {
+                        TopRight = RightTop = 0;
+                    }
+                    else
+                    {
+                        TopRight = cornerRadius.TopRight + top;
+                        RightTop = cornerRadius.TopRight + right;
+                    }
+                    if (cornerRadius.BottomRight == 0)
+                    {
+                        RightBottom = BottomRight = 0;
+                    }
+                    else
+                    {
+                        RightBottom = cornerRadius.BottomRight + right;
+                        BottomRight = cornerRadius.BottomRight + bottom;
+                    }
+                    if (cornerRadius.BottomLeft == 0)
+                    {
+                        BottomLeft = LeftBottom = 0;
+                    }
+                    else
+                    {
+                        BottomLeft = cornerRadius.BottomLeft + bottom;
+                        LeftBottom = cornerRadius.BottomLeft + left;
+                    }
+                }
+                else
+                {
+                    LeftTop = Math.Max(0, cornerRadius.TopLeft - left);
+                    TopLeft = Math.Max(0, cornerRadius.TopLeft - top);
+                    TopRight = Math.Max(0, cornerRadius.TopRight - top);
+                    RightTop = Math.Max(0, cornerRadius.TopRight - right);
+                    RightBottom = Math.Max(0, cornerRadius.BottomRight - right);
+                    BottomRight = Math.Max(0, cornerRadius.BottomRight - bottom);
+                    BottomLeft = Math.Max(0, cornerRadius.BottomLeft - bottom);
+                    LeftBottom = Math.Max(0, cornerRadius.BottomLeft - left);
+                }
+            }
+
+            internal readonly double LeftTop;
+            internal readonly double TopLeft;
+            internal readonly double TopRight;
+            internal readonly double RightTop;
+            internal readonly double RightBottom;
+            internal readonly double BottomRight;
+            internal readonly double BottomLeft;
+            internal readonly double LeftBottom;
+        }
+
+    }
+}

+ 2 - 3
src/Avalonia.Themes.Default/Accents/BaseLight.xaml

@@ -1,7 +1,6 @@
 <Style xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-       xmlns:sys="clr-namespace:System;assembly=mscorlib"
-       xmlns:avalonia="clr-namespace:Avalonia;assembly=Avalonia.Visuals">
+       xmlns:sys="clr-namespace:System;assembly=mscorlib">
   <Style.Resources>
     <SolidColorBrush x:Key="ThemeBackgroundBrush">#FFFFFFFF</SolidColorBrush>
     <SolidColorBrush x:Key="ThemeBorderLightBrush">#FFAAAAAA</SolidColorBrush>
@@ -21,7 +20,7 @@
     <SolidColorBrush x:Key="ErrorBrush">Red</SolidColorBrush>
     <SolidColorBrush x:Key="ErrorBrushLight">#10ff0000</SolidColorBrush>
 
-    <avalonia:Thickness x:Key="ThemeBorderThickness">2</avalonia:Thickness>
+    <Thickness x:Key="ThemeBorderThickness">2</Thickness>
     <sys:Double x:Key="ThemeDisabledOpacity">0.5</sys:Double>
 
     <sys:Double x:Key="FontSizeSmall">10</sys:Double>

+ 5 - 5
src/Avalonia.Visuals/CornerRadius.cs

@@ -83,15 +83,15 @@ namespace Avalonia
 
         public static bool operator ==(CornerRadius cr1, CornerRadius cr2)
         {
-            return ((cr1.TopLeft.Equals(cr2.TopLeft) || double.IsNaN(cr1.TopLeft) && double.IsNaN(cr2.TopLeft)))
-                   && (cr1.TopRight.Equals(cr2.TopRight) || (double.IsNaN(cr1.TopRight) && double.IsNaN(cr2.TopRight)))
-                   && (cr1.BottomRight.Equals(cr2.BottomRight) || double.IsNaN(cr1.BottomRight) && double.IsNaN(cr2.BottomRight))
-                   && (cr1.BottomLeft.Equals(cr2.BottomLeft) || double.IsNaN(cr1.BottomLeft) && double.IsNaN(cr2.BottomLeft));
+            return cr1.TopLeft.Equals(cr2.TopLeft)
+                   && cr1.TopRight.Equals(cr2.TopRight)
+                   && cr1.BottomRight.Equals(cr2.BottomRight) 
+                   && cr1.BottomLeft.Equals(cr2.BottomLeft);
         }
 
         public static bool operator !=(CornerRadius cr1, CornerRadius cr2)
         {
-            return (!(cr1 == cr2));
+            return !(cr1 == cr2);
         }
     }
 }

+ 1 - 0
src/Avalonia.Visuals/Properties/AssemblyInfo.cs

@@ -9,6 +9,7 @@ using Avalonia.Metadata;
 [assembly: InternalsVisibleTo("Avalonia.Visuals.UnitTests")]
 [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Animation")]
 [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Media")]
+[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia")]
 
 [assembly: InternalsVisibleTo("Avalonia.Direct2D1.RenderTests")]
 [assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests")]

+ 1 - 11
src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs

@@ -1,7 +1,6 @@
 // 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 Avalonia.Platform;
 using SharpDX.Direct2D1;
 
@@ -20,21 +19,12 @@ namespace Avalonia.Direct2D1.Media
         /// <inheritdoc/>
         public Rect Bounds => Geometry.GetWidenedBounds(0).ToAvalonia();
 
-        /// <inheritdoc/>
         public Geometry Geometry { get; }
 
         /// <inheritdoc/>
         public Rect GetRenderBounds(Avalonia.Media.Pen pen)
         {
-            //var factory = AvaloniaLocator.Current.GetService<Factory>();
-            if (pen == null)
-            {
-                return Geometry.GetWidenedBounds(0).ToAvalonia();
-            }
-            else
-            {
-                return Geometry.GetWidenedBounds((float)pen.Thickness).ToAvalonia();
-            }
+            return Geometry.GetWidenedBounds((float) (pen?.Thickness ?? 0)).ToAvalonia();
         }
 
         /// <inheritdoc/>