Browse Source

Merge pull request #6836 from RomanSoloweow/master

Border StrokeDashArray
pull/7094/head
GMIKE 4 years ago
committed by GitHub
parent
commit
621bb5688d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 74
      src/Avalonia.Controls/Border.cs
  2. 52
      src/Avalonia.Controls/Utils/BorderRenderHelper.cs

74
src/Avalonia.Controls/Border.cs

@ -1,3 +1,5 @@
using Avalonia.Collections;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Utils; using Avalonia.Controls.Utils;
using Avalonia.Layout; using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
@ -41,7 +43,31 @@ namespace Avalonia.Controls
/// </summary> /// </summary>
public static readonly StyledProperty<BoxShadows> BoxShadowProperty = public static readonly StyledProperty<BoxShadows> BoxShadowProperty =
AvaloniaProperty.Register<Border, BoxShadows>(nameof(BoxShadow)); AvaloniaProperty.Register<Border, BoxShadows>(nameof(BoxShadow));
/// <summary>
/// Defines the <see cref="BorderDashOffset"/> property.
/// </summary>
public static readonly StyledProperty<double> BorderDashOffsetProperty =
AvaloniaProperty.Register<Border, double>(nameof(BorderDashOffset));
/// <summary>
/// Defines the <see cref="BorderDashArray"/> property.
/// </summary>
public static readonly StyledProperty<AvaloniaList<double>?> BorderDashArrayProperty =
AvaloniaProperty.Register<Border, AvaloniaList<double>?>(nameof(BorderDashArray));
/// <summary>
/// Defines the <see cref="BorderLineCap"/> property.
/// </summary>
public static readonly StyledProperty<PenLineCap> BorderLineCapProperty =
AvaloniaProperty.Register<Border, PenLineCap>(nameof(BorderLineCap), PenLineCap.Flat);
/// <summary>
/// Defines the <see cref="BorderLineJoin"/> property.
/// </summary>
public static readonly StyledProperty<PenLineJoin> BorderLineJoinProperty =
AvaloniaProperty.Register<Border, PenLineJoin>(nameof(BorderLineJoin), PenLineJoin.Miter);
private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper(); private readonly BorderRenderHelper _borderRenderHelper = new BorderRenderHelper();
/// <summary> /// <summary>
@ -54,6 +80,10 @@ namespace Avalonia.Controls
BorderBrushProperty, BorderBrushProperty,
BorderThicknessProperty, BorderThicknessProperty,
CornerRadiusProperty, CornerRadiusProperty,
BorderDashArrayProperty,
BorderLineCapProperty,
BorderLineJoinProperty,
BorderDashOffsetProperty,
BoxShadowProperty); BoxShadowProperty);
AffectsMeasure<Border>(BorderThicknessProperty); AffectsMeasure<Border>(BorderThicknessProperty);
} }
@ -76,6 +106,15 @@ namespace Avalonia.Controls
set { SetValue(BorderBrushProperty, value); } set { SetValue(BorderBrushProperty, value); }
} }
/// <summary>
/// Gets or sets a collection of <see cref="double"/> values that indicate the pattern of dashes and gaps that is used to outline shapes.
/// </summary>
public AvaloniaList<double>? BorderDashArray
{
get { return GetValue(BorderDashArrayProperty); }
set { SetValue(BorderDashArrayProperty, value); }
}
/// <summary> /// <summary>
/// Gets or sets the thickness of the border. /// Gets or sets the thickness of the border.
/// </summary> /// </summary>
@ -85,6 +124,33 @@ namespace Avalonia.Controls
set { SetValue(BorderThicknessProperty, value); } set { SetValue(BorderThicknessProperty, value); }
} }
/// <summary>
/// Gets or sets a value that specifies the distance within the dash pattern where a dash begins.
/// </summary>
public double BorderDashOffset
{
get { return GetValue(BorderDashOffsetProperty); }
set { SetValue(BorderDashOffsetProperty, value); }
}
/// <summary>
/// Gets or sets a <see cref="PenLineCap"/> enumeration value that describes the shape at the ends of a line.
/// </summary>
public PenLineCap BorderLineCap
{
get { return GetValue(BorderLineCapProperty); }
set { SetValue(BorderLineCapProperty, value); }
}
/// <summary>
/// Gets or sets a <see cref="PenLineJoin"/> enumeration value that specifies the type of join that is used at the vertices of a Shape.
/// </summary>
public PenLineJoin BorderLineJoin
{
get { return GetValue(BorderLineJoinProperty); }
set { SetValue(BorderLineJoinProperty, value); }
}
/// <summary> /// <summary>
/// Gets or sets the radius of the border rounded corners. /// Gets or sets the radius of the border rounded corners.
/// </summary> /// </summary>
@ -93,7 +159,7 @@ namespace Avalonia.Controls
get { return GetValue(CornerRadiusProperty); } get { return GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); } set { SetValue(CornerRadiusProperty, value); }
} }
/// <summary> /// <summary>
/// Gets or sets the box shadow effect parameters /// Gets or sets the box shadow effect parameters
/// </summary> /// </summary>
@ -102,7 +168,7 @@ namespace Avalonia.Controls
get => GetValue(BoxShadowProperty); get => GetValue(BoxShadowProperty);
set => SetValue(BoxShadowProperty, value); set => SetValue(BoxShadowProperty, value);
} }
/// <summary> /// <summary>
/// Renders the control. /// Renders the control.
/// </summary> /// </summary>
@ -110,7 +176,7 @@ namespace Avalonia.Controls
public override void Render(DrawingContext context) public override void Render(DrawingContext context)
{ {
_borderRenderHelper.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush, _borderRenderHelper.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush,
BoxShadow); BoxShadow, BorderDashOffset, BorderLineCap, BorderLineJoin, BorderDashArray);
} }
/// <summary> /// <summary>

52
src/Avalonia.Controls/Utils/BorderRenderHelper.cs

@ -1,8 +1,10 @@
using System; using System;
using Avalonia.Collections;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Immutable; using Avalonia.Media.Immutable;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.Utilities; using Avalonia.Utilities;
using JetBrains.Annotations;
namespace Avalonia.Controls.Utils namespace Avalonia.Controls.Utils
{ {
@ -15,8 +17,13 @@ namespace Avalonia.Controls.Utils
private Size _size; private Size _size;
private Thickness _borderThickness; private Thickness _borderThickness;
private CornerRadius _cornerRadius; private CornerRadius _cornerRadius;
private AvaloniaList<double> _borderDashArray;
private double _borderDashOffset;
private PenLineCap _borderLineCap;
private PenLineJoin _borderJoin;
private bool _initialized; private bool _initialized;
void Update(Size finalSize, Thickness borderThickness, CornerRadius cornerRadius) void Update(Size finalSize, Thickness borderThickness, CornerRadius cornerRadius)
{ {
_backendSupportsIndividualCorners ??= AvaloniaLocator.Current.GetService<IPlatformRenderInterface>() _backendSupportsIndividualCorners ??= AvaloniaLocator.Current.GetService<IPlatformRenderInterface>()
@ -60,7 +67,8 @@ namespace Avalonia.Controls.Utils
if (boundRect.Width != 0 && innerRect.Height != 0) if (boundRect.Width != 0 && innerRect.Height != 0)
{ {
var borderGeometryKeypoints = new BorderGeometryKeypoints(boundRect, borderThickness, cornerRadius, false); var borderGeometryKeypoints =
new BorderGeometryKeypoints(boundRect, borderThickness, cornerRadius, false);
var borderGeometry = new StreamGeometry(); var borderGeometry = new StreamGeometry();
using (var ctx = borderGeometry.Open()) using (var ctx = borderGeometry.Open())
@ -84,17 +92,22 @@ namespace Avalonia.Controls.Utils
public void Render(DrawingContext context, public void Render(DrawingContext context,
Size finalSize, Thickness borderThickness, CornerRadius cornerRadius, Size finalSize, Thickness borderThickness, CornerRadius cornerRadius,
IBrush background, IBrush borderBrush, BoxShadows boxShadows) IBrush background, IBrush borderBrush, BoxShadows boxShadows, double borderDashOffset = 0,
PenLineCap borderLineCap = PenLineCap.Flat, PenLineJoin borderLineJoin = PenLineJoin.Miter,
AvaloniaList<double> borderDashArray = null)
{ {
if (_size != finalSize if (_size != finalSize
|| _borderThickness != borderThickness || _borderThickness != borderThickness
|| _cornerRadius != cornerRadius || _cornerRadius != cornerRadius
|| !_initialized) || !_initialized)
Update(finalSize, borderThickness, cornerRadius); Update(finalSize, borderThickness, cornerRadius);
RenderCore(context, background, borderBrush, boxShadows); RenderCore(context, background, borderBrush, boxShadows, borderDashOffset, borderLineCap, borderLineJoin,
borderDashArray);
} }
void RenderCore(DrawingContext context, IBrush background, IBrush borderBrush, BoxShadows boxShadows) void RenderCore(DrawingContext context, IBrush background, IBrush borderBrush, BoxShadows boxShadows,
double borderDashOffset, PenLineCap borderLineCap, PenLineJoin borderLineJoin,
AvaloniaList<double> borderDashArray)
{ {
if (_useComplexRendering) if (_useComplexRendering)
{ {
@ -115,11 +128,25 @@ namespace Avalonia.Controls.Utils
var borderThickness = _borderThickness.Top; var borderThickness = _borderThickness.Top;
IPen pen = null; IPen pen = null;
ImmutableDashStyle? dashStyle = null;
if (borderDashArray != null && borderDashArray.Count > 0)
{
dashStyle = new ImmutableDashStyle(borderDashArray, borderDashOffset);
}
if (borderBrush != null && borderThickness > 0) if (borderBrush != null && borderThickness > 0)
{ {
pen = new ImmutablePen(borderBrush.ToImmutable(), borderThickness); pen = new ImmutablePen(
borderBrush.ToImmutable(),
borderThickness,
dashStyle,
borderLineCap,
borderLineJoin);
} }
var rect = new Rect(_size); var rect = new Rect(_size);
if (!MathUtilities.IsZero(borderThickness)) if (!MathUtilities.IsZero(borderThickness))
rect = rect.Deflate(borderThickness * 0.5); rect = rect.Deflate(borderThickness * 0.5);
@ -130,7 +157,8 @@ namespace Avalonia.Controls.Utils
} }
} }
private static void CreateGeometry(StreamGeometryContext context, Rect boundRect, BorderGeometryKeypoints keypoints) private static void CreateGeometry(StreamGeometryContext context, Rect boundRect,
BorderGeometryKeypoints keypoints)
{ {
context.BeginFigure(keypoints.TopLeft, true); context.BeginFigure(keypoints.TopLeft, true);
@ -184,7 +212,8 @@ namespace Avalonia.Controls.Utils
private class BorderGeometryKeypoints private class BorderGeometryKeypoints
{ {
internal BorderGeometryKeypoints(Rect boundRect, Thickness borderThickness, CornerRadius cornerRadius, bool inner) internal BorderGeometryKeypoints(Rect boundRect, Thickness borderThickness, CornerRadius cornerRadius,
bool inner)
{ {
var left = 0.5 * borderThickness.Left; var left = 0.5 * borderThickness.Left;
var top = 0.5 * borderThickness.Top; var top = 0.5 * borderThickness.Top;
@ -206,10 +235,13 @@ namespace Avalonia.Controls.Utils
topLeftX = Math.Max(0, cornerRadius.TopLeft - left) + boundRect.TopLeft.X; topLeftX = Math.Max(0, cornerRadius.TopLeft - left) + boundRect.TopLeft.X;
topRightX = boundRect.Width - Math.Max(0, cornerRadius.TopRight - top) + boundRect.TopLeft.X; topRightX = boundRect.Width - Math.Max(0, cornerRadius.TopRight - top) + boundRect.TopLeft.X;
rightTopY = Math.Max(0, cornerRadius.TopRight - right) + boundRect.TopLeft.Y; rightTopY = Math.Max(0, cornerRadius.TopRight - right) + boundRect.TopLeft.Y;
rightBottomY = boundRect.Height - Math.Max(0, cornerRadius.BottomRight - bottom) + boundRect.TopLeft.Y; rightBottomY = boundRect.Height - Math.Max(0, cornerRadius.BottomRight - bottom) +
bottomRightX = boundRect.Width - Math.Max(0, cornerRadius.BottomRight - right) + boundRect.TopLeft.X; boundRect.TopLeft.Y;
bottomRightX = boundRect.Width - Math.Max(0, cornerRadius.BottomRight - right) +
boundRect.TopLeft.X;
bottomLeftX = Math.Max(0, cornerRadius.BottomLeft - left) + boundRect.TopLeft.X; bottomLeftX = Math.Max(0, cornerRadius.BottomLeft - left) + boundRect.TopLeft.X;
leftBottomY = boundRect.Height - Math.Max(0, cornerRadius.BottomLeft - bottom) + boundRect.TopLeft.Y; leftBottomY = boundRect.Height - Math.Max(0, cornerRadius.BottomLeft - bottom) +
boundRect.TopLeft.Y;
} }
else else
{ {

Loading…
Cancel
Save