Browse Source

Internal API for rounded border clip for usage with Border.

pull/4081/head
Nikita Tsukanov 6 years ago
parent
commit
da580f0373
  1. 7
      samples/ControlCatalog/Pages/BorderPage.xaml
  2. 5
      src/Avalonia.Controls/Border.cs
  3. 6
      src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs
  4. 14
      src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs
  5. 17
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  6. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs
  7. 5
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  8. 8
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  9. 11
      src/Avalonia.Visuals/RoundedRect.cs
  10. 15
      src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs
  11. 6
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

7
samples/ControlCatalog/Pages/BorderPage.xaml

@ -29,6 +29,13 @@
Padding="16">
<TextBlock>Rounded Corners</TextBlock>
</Border>
<Border BorderBrush="{DynamicResource ThemeAccentBrush2}"
BorderThickness="4"
CornerRadius="8" ClipToBounds="True">
<Border Background="Red" Padding="16">
<TextBlock>Clipped Rounded Corners</TextBlock>
</Border>
</Border>
</StackPanel>
</StackPanel>
</UserControl>

5
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
{
/// <summary>
/// A control which decorates a child with a border and background.
/// </summary>
public partial class Border : Decorator
public partial class Border : Decorator, IVisualWithRoundRectClip
{
/// <summary>
/// Defines the <see cref="Background"/> property.
@ -129,5 +130,7 @@ namespace Avalonia.Controls
{
return LayoutHelper.ArrangeChild(Child, finalSize, Padding, BorderThickness);
}
public CornerRadius ClipToBoundsRadius => CornerRadius;
}
}

6
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())

14
src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs

@ -16,6 +16,16 @@ namespace Avalonia.Rendering.SceneGraph
{
Clip = clip;
}
/// <summary>
/// Initializes a new instance of the <see cref="ClipNode"/> class that represents a
/// clip push.
/// </summary>
/// <param name="clip">The clip to push.</param>
public ClipNode(RoundedRect clip)
{
Clip = clip;
}
/// <summary>
/// Initializes a new instance of the <see cref="ClipNode"/> class that represents a
@ -31,7 +41,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <summary>
/// Gets the clip to be pushed or null if the operation represents a pop.
/// </summary>
public Rect? Clip { get; }
public RoundedRect? Clip { get; }
/// <inheritdoc/>
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.
/// </remarks>
public bool Equals(Rect? clip) => Clip == clip;
public bool Equals(RoundedRect? clip) => Clip == clip;
/// <inheritdoc/>
public void Render(IDrawingContextImpl context)

17
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");
}
/// <inheritdoc/>
public void PopClip()
{
@ -282,6 +282,21 @@ namespace Avalonia.Rendering.SceneGraph
++_drawOperationindex;
}
}
/// <inheritdoc />
public void PushClip(RoundedRect clip)
{
var next = NextDrawAs<ClipNode>();
if (next == null || !next.Item.Equals(clip))
{
Add(new ClipNode(clip));
}
else
{
++_drawOperationindex;
}
}
/// <inheritdoc/>
public void PushGeometryClip(IGeometryImpl clip)

2
src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs

@ -29,7 +29,7 @@ namespace Avalonia.Rendering.SceneGraph
/// <summary>
/// Gets the corner radius of visual. Contents are clipped to this radius.
/// </summary>
CornerRadius CornerRadius { get; }
CornerRadius ClipToBoundsRadius { get; }
/// <summary>
/// Gets the bounds of the node's geometry in global coordinates.

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

8
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@ -47,7 +47,7 @@ namespace Avalonia.Rendering.SceneGraph
public IVisualNode Parent { get; }
/// <inheritdoc/>
public CornerRadius CornerRadius { get; }
public CornerRadius ClipToBoundsRadius { get; set; }
/// <inheritdoc/>
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;

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

15
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
{
/// <summary>
/// Gets a value indicating the corner radius of control's clip bounds
/// </summary>
[Obsolete("Internal API, will be removed in future versions, you've been warned")]
CornerRadius ClipToBoundsRadius { get; }
}
}

6
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();

Loading…
Cancel
Save