diff --git a/samples/ControlCatalog/Pages/BorderPage.xaml b/samples/ControlCatalog/Pages/BorderPage.xaml
index c30056d5e5..429db7917e 100644
--- a/samples/ControlCatalog/Pages/BorderPage.xaml
+++ b/samples/ControlCatalog/Pages/BorderPage.xaml
@@ -29,6 +29,13 @@
Padding="16">
Rounded Corners
+
+
+ Clipped Rounded Corners
+
+
diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs
index b355310244..c8337df99c 100644
--- a/src/Avalonia.Controls/Border.cs
+++ b/src/Avalonia.Controls/Border.cs
@@ -1,13 +1,14 @@
using Avalonia.Controls.Utils;
using Avalonia.Layout;
using Avalonia.Media;
+using Avalonia.VisualTree;
namespace Avalonia.Controls
{
///
/// A control which decorates a child with a border and background.
///
- public partial class Border : Decorator
+ public partial class Border : Decorator, IVisualWithRoundRectClip
{
///
/// Defines the property.
@@ -129,5 +130,7 @@ namespace Avalonia.Controls
{
return LayoutHelper.ArrangeChild(Child, finalSize, Padding, BorderThickness);
}
+
+ public CornerRadius ClipToBoundsRadius => CornerRadius;
}
}
diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
index c1c4b6bc99..b1f51b941a 100644
--- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
+++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
@@ -289,7 +289,11 @@ namespace Avalonia.Rendering
using (context.PushPostTransform(m))
using (context.PushOpacity(opacity))
- using (clipToBounds ? context.PushClip(bounds) : default(DrawingContext.PushedState))
+ using (clipToBounds
+ ? visual is IVisualWithRoundRectClip roundClipVisual
+ ? context.PushClip(new RoundedRect(bounds, roundClipVisual.ClipToBoundsRadius))
+ : context.PushClip(bounds)
+ : default(DrawingContext.PushedState))
using (visual.Clip != null ? context.PushGeometryClip(visual.Clip) : default(DrawingContext.PushedState))
using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default(DrawingContext.PushedState))
using (context.PushTransformContainer())
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs
index 34f042e334..ada04bfefd 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs
@@ -16,6 +16,16 @@ namespace Avalonia.Rendering.SceneGraph
{
Clip = clip;
}
+
+ ///
+ /// Initializes a new instance of the class that represents a
+ /// clip push.
+ ///
+ /// The clip to push.
+ public ClipNode(RoundedRect clip)
+ {
+ Clip = clip;
+ }
///
/// Initializes a new instance of the class that represents a
@@ -31,7 +41,7 @@ namespace Avalonia.Rendering.SceneGraph
///
/// Gets the clip to be pushed or null if the operation represents a pop.
///
- public Rect? Clip { get; }
+ public RoundedRect? Clip { get; }
///
public bool HitTest(Point p) => false;
@@ -45,7 +55,7 @@ namespace Avalonia.Rendering.SceneGraph
/// The properties of the other draw operation are passed in as arguments to prevent
/// allocation of a not-yet-constructed draw operation object.
///
- public bool Equals(Rect? clip) => Clip == clip;
+ public bool Equals(RoundedRect? clip) => Clip == clip;
///
public void Render(IDrawingContextImpl context)
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
index 6ad71ac111..936f5d30a7 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
@@ -207,7 +207,7 @@ namespace Avalonia.Rendering.SceneGraph
{
throw new NotSupportedException("Creating layers on a deferred drawing context not supported");
}
-
+
///
public void PopClip()
{
@@ -282,6 +282,21 @@ namespace Avalonia.Rendering.SceneGraph
++_drawOperationindex;
}
}
+
+ ///
+ public void PushClip(RoundedRect clip)
+ {
+ var next = NextDrawAs();
+
+ if (next == null || !next.Item.Equals(clip))
+ {
+ Add(new ClipNode(clip));
+ }
+ else
+ {
+ ++_drawOperationindex;
+ }
+ }
///
public void PushGeometryClip(IGeometryImpl clip)
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs
index 679cf3daa0..6d12b5bca4 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs
@@ -29,7 +29,7 @@ namespace Avalonia.Rendering.SceneGraph
///
/// Gets the corner radius of visual. Contents are clipped to this radius.
///
- CornerRadius CornerRadius { get; }
+ CornerRadius ClipToBoundsRadius { get; }
///
/// Gets the bounds of the node's geometry in global coordinates.
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
index 263a491933..5da44c5943 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
@@ -163,6 +163,10 @@ namespace Avalonia.Rendering.SceneGraph
var visual = node.Visual;
var opacity = visual.Opacity;
var clipToBounds = visual.ClipToBounds;
+ var clipToBoundsRadius = visual is IVisualWithRoundRectClip roundRectClip ?
+ roundRectClip.ClipToBoundsRadius :
+ default;
+
var bounds = new Rect(visual.Bounds.Size);
var contextImpl = (DeferredDrawingContextImpl)context.PlatformImpl;
@@ -201,6 +205,7 @@ namespace Avalonia.Rendering.SceneGraph
node.ClipBounds = clipBounds;
node.ClipToBounds = clipToBounds;
node.LayoutBounds = globalBounds;
+ node.ClipToBoundsRadius = clipToBoundsRadius;
node.GeometryClip = visual.Clip?.PlatformImpl;
node.Opacity = opacity;
diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
index 6276d5c7c3..8fb6b2542a 100644
--- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
+++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
@@ -47,7 +47,7 @@ namespace Avalonia.Rendering.SceneGraph
public IVisualNode Parent { get; }
///
- public CornerRadius CornerRadius { get; }
+ public CornerRadius ClipToBoundsRadius { get; set; }
///
public Matrix Transform { get; set; }
@@ -265,6 +265,7 @@ namespace Avalonia.Rendering.SceneGraph
{
Transform = Transform,
ClipBounds = ClipBounds,
+ ClipToBoundsRadius = ClipToBoundsRadius,
ClipToBounds = ClipToBounds,
LayoutBounds = LayoutBounds,
GeometryClip = GeometryClip,
@@ -304,7 +305,10 @@ namespace Avalonia.Rendering.SceneGraph
if (ClipToBounds)
{
context.Transform = Matrix.Identity;
- context.PushClip(ClipBounds);
+ if (ClipToBoundsRadius.IsEmpty)
+ context.PushClip(ClipBounds);
+ else
+ context.PushClip(new RoundedRect(ClipBounds, ClipToBoundsRadius));
}
context.Transform = Transform;
diff --git a/src/Avalonia.Visuals/RoundedRect.cs b/src/Avalonia.Visuals/RoundedRect.cs
index ad860240f2..3452bc1ff8 100644
--- a/src/Avalonia.Visuals/RoundedRect.cs
+++ b/src/Avalonia.Visuals/RoundedRect.cs
@@ -27,6 +27,10 @@ namespace Avalonia
}
}
+ public static bool operator ==(RoundedRect left, RoundedRect right) => left.Equals(right);
+
+ public static bool operator !=(RoundedRect left, RoundedRect right) => !left.Equals(right);
+
public Rect Rect { get; }
public Vector RadiiTopLeft { get; }
public Vector RadiiTopRight { get; }
@@ -74,6 +78,13 @@ namespace Avalonia
}
+ public RoundedRect(in Rect bounds, in CornerRadius radius) : this(bounds,
+ radius.TopLeft, radius.TopRight,
+ radius.BottomRight, radius.BottomLeft)
+ {
+
+ }
+
public static implicit operator RoundedRect(Rect r) => new RoundedRect(r);
public bool IsRounded => RadiiTopLeft != default || RadiiTopRight != default || RadiiBottomRight != default ||
diff --git a/src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs b/src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs
new file mode 100644
index 0000000000..9ace215d03
--- /dev/null
+++ b/src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Avalonia.VisualTree
+{
+ [Obsolete("Internal API, will be removed in future versions, you've been warned")]
+ public interface IVisualWithRoundRectClip
+ {
+ ///
+ /// Gets a value indicating the corner radius of control's clip bounds
+ ///
+ [Obsolete("Internal API, will be removed in future versions, you've been warned")]
+ CornerRadius ClipToBoundsRadius { get; }
+
+ }
+}
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
index bbb45cf64c..696dc5978e 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
+++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
@@ -359,6 +359,12 @@ namespace Avalonia.Direct2D1.Media
_deviceContext.PushAxisAlignedClip(clip.ToSharpDX(), AntialiasMode.PerPrimitive);
}
+ public void PushClip(RoundedRect clip)
+ {
+ //TODO: radius
+ _deviceContext.PushAxisAlignedClip(clip.Rect.ToDirect2D(), AntialiasMode.PerPrimitive);
+ }
+
public void PopClip()
{
_deviceContext.PopAxisAlignedClip();