Browse Source

Merge branch 'master' into feature/flowDirectionImpl

pull/7810/head
Jumar Macato 5 years ago
committed by GitHub
parent
commit
f86b2ad571
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      samples/ControlCatalog/Pages/CanvasPage.xaml
  2. 126
      src/Avalonia.Controls/Shapes/Rectangle.cs
  3. 4
      src/Avalonia.Visuals/Media/StreamGeometryContext.cs
  4. 4
      src/Avalonia.Visuals/Platform/IGeometryContext.cs
  5. 7
      src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs

5
samples/ControlCatalog/Pages/CanvasPage.xaml

@ -4,7 +4,7 @@
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">A panel which lays out its children by explicit coordinates</TextBlock>
<Canvas Background="Yellow" Width="300" Height="400">
<Rectangle Fill="Blue" Width="63" Height="41" Canvas.Left="40" Canvas.Top="31">
<Rectangle Fill="Blue" Width="63" Height="41" Canvas.Left="40" Canvas.Top="31" RadiusX="10" RadiusY="10">
<Rectangle.OpacityMask>
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<LinearGradientBrush.GradientStops>
@ -12,8 +12,9 @@
<GradientStop Offset="1" Color="Transparent"/>
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.OpacityMask>
</Rectangle.OpacityMask>
</Rectangle>
<Rectangle Fill="Green" Stroke="Black" StrokeThickness="2" Width="40" Height="20" Canvas.Left="150" Canvas.Top="10" RadiusX="10" RadiusY="5" />
<Ellipse Fill="Green" Width="58" Height="58" Canvas.Left="88" Canvas.Top="100"/>
<Path Fill="Orange" Data="M 0,0 c 0,0 50,0 50,-50 c 0,0 50,0 50,50 h -50 v 50 l -50,-50 Z" Canvas.Left="30" Canvas.Top="250"/>
<Path Fill="OrangeRed" Canvas.Left="180" Canvas.Top="250">

126
src/Avalonia.Controls/Shapes/Rectangle.cs

@ -2,19 +2,139 @@ using Avalonia.Media;
namespace Avalonia.Controls.Shapes
{
/// <summary>
/// Represents a rectangle with optional rounded corners.
/// </summary>
public class Rectangle : Shape
{
private const double PiOver2 = 1.57079633; // 90 deg to rad
/// <summary>
/// Defines the <see cref="RadiusX"/> property.
/// </summary>
public static readonly StyledProperty<double> RadiusXProperty =
AvaloniaProperty.Register<Rectangle, double>(nameof(RadiusX));
/// <summary>
/// Defines the <see cref="RadiusY"/> property.
/// </summary>
public static readonly StyledProperty<double> RadiusYProperty =
AvaloniaProperty.Register<Rectangle, double>(nameof(RadiusY));
static Rectangle()
{
AffectsGeometry<Rectangle>(BoundsProperty, StrokeThicknessProperty);
AffectsGeometry<Rectangle>(
BoundsProperty,
RadiusXProperty,
RadiusYProperty,
StrokeThicknessProperty);
}
/// <summary>
/// Gets or sets the radius on the X-axis used to round the corners of the rectangle.
/// Corner radii are represented by an ellipse so this is the X-axis width of the ellipse.
/// </summary>
public double RadiusX
{
get => GetValue(RadiusXProperty);
set => SetValue(RadiusXProperty, value);
}
/// <summary>
/// Gets or sets the radius on the Y-axis used to round the corners of the rectangle.
/// Corner radii are represented by an ellipse so this is the Y-axis height of the ellipse.
/// </summary>
public double RadiusY
{
get => GetValue(RadiusYProperty);
set => SetValue(RadiusYProperty, value);
}
/// <inheritdoc/>
protected override Geometry CreateDefiningGeometry()
{
var rect = new Rect(Bounds.Size).Deflate(StrokeThickness / 2);
return new RectangleGeometry(rect);
// TODO: If RectangleGeometry ever supports RadiusX/Y like in WPF,
// this code can be removed/combined with that implementation
double x = RadiusX;
double y = RadiusY;
if (x == 0 && y == 0)
{
// Optimization when there are no corner radii
var rect = new Rect(Bounds.Size).Deflate(StrokeThickness / 2);
return new RectangleGeometry(rect);
}
else
{
var rect = new Rect(Bounds.Size).Deflate(StrokeThickness / 2);
var geometry = new StreamGeometry();
var arcSize = new Size(x, y);
using (StreamGeometryContext context = geometry.Open())
{
// The rectangle is constructed as follows:
//
// (origin)
// Corner 4 Corner 1
// Top/Left Line 1 Top/Right
// \_ __________ _/
// | |
// Line 4 | | Line 2
// _ |__________| _
// / Line 3 \
// Corner 3 Corner 2
// Bottom/Left Bottom/Right
//
// - Lines 1,3 follow the deflated rectangle bounds minus RadiusX
// - Lines 2,4 follow the deflated rectangle bounds minus RadiusY
// - All corners are constructed using elliptical arcs
// Line 1 + Corner 1
context.BeginFigure(new Point(rect.Left + x, rect.Top), true);
context.LineTo(new Point(rect.Right - x, rect.Top));
context.ArcTo(
new Point(rect.Right, rect.Top + y),
arcSize,
rotationAngle: PiOver2,
isLargeArc: false,
SweepDirection.Clockwise);
// Line 2 + Corner 2
context.LineTo(new Point(rect.Right, rect.Bottom - y));
context.ArcTo(
new Point(rect.Right - x, rect.Bottom),
arcSize,
rotationAngle: PiOver2,
isLargeArc: false,
SweepDirection.Clockwise);
// Line 3 + Corner 3
context.LineTo(new Point(rect.Left + x, rect.Bottom));
context.ArcTo(
new Point(rect.Left, rect.Bottom - y),
arcSize,
rotationAngle: PiOver2,
isLargeArc: false,
SweepDirection.Clockwise);
// Line 4 + Corner 4
context.LineTo(new Point(rect.Left, rect.Top + y));
context.ArcTo(
new Point(rect.Left + x, rect.Top),
arcSize,
rotationAngle: PiOver2,
isLargeArc: false,
SweepDirection.Clockwise);
context.EndFigure(true);
}
return geometry;
}
}
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
return new Size(StrokeThickness, StrokeThickness);

4
src/Avalonia.Visuals/Media/StreamGeometryContext.cs

@ -41,7 +41,7 @@ namespace Avalonia.Media
/// </summary>
/// <param name="point">The destination point.</param>
/// <param name="size">The radii of an oval whose perimeter is used to draw the angle.</param>
/// <param name="rotationAngle">The rotation angle of the oval that specifies the curve.</param>
/// <param name="rotationAngle">The rotation angle (in radians) of the oval that specifies the curve.</param>
/// <param name="isLargeArc">true to draw the arc greater than 180 degrees; otherwise, false.</param>
/// <param name="sweepDirection">
/// A value that indicates whether the arc is drawn in the Clockwise or Counterclockwise direction.
@ -59,7 +59,7 @@ namespace Avalonia.Media
/// </summary>
/// <param name="point">The destination point.</param>
/// <param name="size">The radii of an oval whose perimeter is used to draw the angle.</param>
/// <param name="rotationAngle">The rotation angle of the oval that specifies the curve.</param>
/// <param name="rotationAngle">The rotation angle (in radians) of the oval that specifies the curve.</param>
/// <param name="isLargeArc">true to draw the arc greater than 180 degrees; otherwise, false.</param>
/// <param name="sweepDirection">
/// A value that indicates whether the arc is drawn in the Clockwise or Counterclockwise direction.

4
src/Avalonia.Visuals/Platform/IGeometryContext.cs

@ -13,7 +13,7 @@ namespace Avalonia.Platform
/// </summary>
/// <param name="point">The destination point.</param>
/// <param name="size">The radii of an oval whose perimeter is used to draw the angle.</param>
/// <param name="rotationAngle">The rotation angle of the oval that specifies the curve.</param>
/// <param name="rotationAngle">The rotation angle (in radians) of the oval that specifies the curve.</param>
/// <param name="isLargeArc">true to draw the arc greater than 180 degrees; otherwise, false.</param>
/// <param name="sweepDirection">
/// A value that indicates whether the arc is drawn in the Clockwise or Counterclockwise direction.
@ -60,4 +60,4 @@ namespace Avalonia.Platform
/// <param name="fillRule">The fill rule.</param>
void SetFillRule(FillRule fillRule);
}
}
}

7
src/Avalonia.Visuals/Platform/IPlatformRenderInterface.cs

@ -168,6 +168,13 @@ namespace Avalonia.Platform
/// <returns></returns>
IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun);
/// <summary>
/// Gets a value indicating whether the platform directly supports rectangles with rounded corners.
/// </summary>
/// <remarks>
/// Some platform renderers can't directly handle rounded corners on rectangles.
/// In this case, code that requires rounded corners must generate and retain a geometry instead.
/// </remarks>
bool SupportsIndividualRoundRects { get; }
/// <summary>

Loading…
Cancel
Save