Browse Source

Requested changes

pull/1462/head
Benedikt Schroeder 9 years ago
parent
commit
50dd6cb66d
  1. 161
      src/Avalonia.Controls/Border.cs
  2. 7
      src/Avalonia.Controls/Presenters/ContentPresenter.cs
  3. 279
      src/Avalonia.Controls/Utils/BorderRenderHelper.cs
  4. 5
      src/Avalonia.Themes.Default/Accents/BaseLight.xaml
  5. 10
      src/Avalonia.Visuals/CornerRadius.cs
  6. 1
      src/Avalonia.Visuals/Properties/AssemblyInfo.cs
  7. 12
      src/Windows/Avalonia.Direct2D1/Media/GeometryImpl.cs

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

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

5
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>

10
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
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")]

12
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/>

Loading…
Cancel
Save