New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added Thickness support for BorderThickness and CornerRadius #1462
Changes from 2 commits
27a6664
5b9de83
1b28310
50dd6cb
8fdc748
253ff56
f63b85f
030b48b
dbd2406
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +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.Media; | ||
|
||
namespace Avalonia.Controls | ||
|
@@ -25,14 +27,16 @@ public class Border : Decorator | |
/// <summary> | ||
/// Defines the <see cref="BorderThickness"/> property. | ||
/// </summary> | ||
public static readonly StyledProperty<double> BorderThicknessProperty = | ||
AvaloniaProperty.Register<Border, double>(nameof(BorderThickness)); | ||
public static readonly StyledProperty<Thickness> BorderThicknessProperty = | ||
AvaloniaProperty.Register<Border, Thickness>(nameof(BorderThickness)); | ||
|
||
/// <summary> | ||
/// Defines the <see cref="CornerRadius"/> property. | ||
/// </summary> | ||
public static readonly StyledProperty<float> CornerRadiusProperty = | ||
AvaloniaProperty.Register<Border, float>(nameof(CornerRadius)); | ||
public static readonly StyledProperty<CornerRadius> CornerRadiusProperty = | ||
AvaloniaProperty.Register<Border, CornerRadius>(nameof(CornerRadius)); | ||
|
||
private readonly BorderRenderer _borderRenderer = new BorderRenderer(); | ||
|
||
/// <summary> | ||
/// Initializes static members of the <see cref="Border"/> class. | ||
|
@@ -63,7 +67,7 @@ public IBrush BorderBrush | |
/// <summary> | ||
/// Gets or sets the thickness of the border. | ||
/// </summary> | ||
public double BorderThickness | ||
public Thickness BorderThickness | ||
{ | ||
get { return GetValue(BorderThicknessProperty); } | ||
set { SetValue(BorderThicknessProperty, value); } | ||
|
@@ -72,7 +76,7 @@ public double BorderThickness | |
/// <summary> | ||
/// Gets or sets the radius of the border rounded corners. | ||
/// </summary> | ||
public float CornerRadius | ||
public CornerRadius CornerRadius | ||
{ | ||
get { return GetValue(CornerRadiusProperty); } | ||
set { SetValue(CornerRadiusProperty, value); } | ||
|
@@ -84,21 +88,7 @@ public float CornerRadius | |
/// <param name="context">The drawing context.</param> | ||
public override void Render(DrawingContext context) | ||
{ | ||
var background = Background; | ||
var borderBrush = BorderBrush; | ||
var borderThickness = BorderThickness; | ||
var cornerRadius = CornerRadius; | ||
var rect = new Rect(Bounds.Size).Deflate(BorderThickness); | ||
|
||
if (background != null) | ||
{ | ||
context.FillRectangle(background, rect, cornerRadius); | ||
} | ||
|
||
if (borderBrush != null && borderThickness > 0) | ||
{ | ||
context.DrawRectangle(new Pen(borderBrush, borderThickness), rect, cornerRadius); | ||
} | ||
_borderRenderer.Render(context, Bounds.Size, BorderThickness, CornerRadius, Background, BorderBrush); | ||
} | ||
|
||
/// <summary> | ||
|
@@ -120,29 +110,205 @@ protected override Size ArrangeOverride(Size finalSize) | |
{ | ||
if (Child != null) | ||
{ | ||
var padding = Padding + new Thickness(BorderThickness); | ||
var padding = Padding + BorderThickness; | ||
Child.Arrange(new Rect(finalSize).Deflate(padding)); | ||
} | ||
|
||
_borderRenderer.Update(finalSize, BorderThickness, CornerRadius); | ||
|
||
return finalSize; | ||
} | ||
|
||
internal static Size MeasureOverrideImpl( | ||
Size availableSize, | ||
IControl child, | ||
Thickness padding, | ||
double borderThickness) | ||
Thickness borderThickness) | ||
{ | ||
padding += new Thickness(borderThickness); | ||
padding += borderThickness; | ||
|
||
if (child != null) | ||
{ | ||
child.Measure(availableSize.Deflate(padding)); | ||
return child.DesiredSize.Inflate(padding); | ||
} | ||
else | ||
|
||
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) | ||
{ | ||
return new Size(padding.Left + padding.Right, padding.Bottom + padding.Top); | ||
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, | ||
boundRect.Width - borderThickness.Right, boundRect.Height - borderThickness.Bottom); | ||
|
||
StreamGeometry backgroundGeometry = null; | ||
|
||
if (!boundRect.Width.Equals(0) && !boundRect.Height.Equals(0)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't it supposed to be |
||
{ | ||
backgroundGeometry = new StreamGeometry(); | ||
|
||
if (cornerRadius.IsEmpty) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why there are two identical if-else branches? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. Those checks are not needed anymore. |
||
{ | ||
using (var ctx = backgroundGeometry.Open()) | ||
{ | ||
CreateGeometry(ctx, innerRect, cornerRadius); | ||
} | ||
} | ||
else | ||
{ | ||
using (var ctx = backgroundGeometry.Open()) | ||
{ | ||
CreateGeometry(ctx, innerRect, cornerRadius); | ||
} | ||
} | ||
|
||
_backgroundGeometryCache = backgroundGeometry; | ||
} | ||
else | ||
{ | ||
_backgroundGeometryCache = null; | ||
} | ||
|
||
if (!boundRect.Width.Equals(0) && !boundRect.Height.Equals(0)) | ||
{ | ||
var borderGeometry = new StreamGeometry(); | ||
|
||
if (cornerRadius.IsEmpty) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And two more identical if-else braches |
||
{ | ||
using (var ctx = borderGeometry.Open()) | ||
{ | ||
CreateGeometry(ctx, boundRect, cornerRadius); | ||
|
||
if (backgroundGeometry != null) | ||
{ | ||
CreateGeometry(ctx, innerRect, cornerRadius); | ||
} | ||
} | ||
} | ||
else | ||
{ | ||
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); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'd rather have this class moved out of
Border
. Maybe put it as an internal class underUtils
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will have a look at present render tests and create new ones to represent my additions. That way we can make sure everything looks as it should be.