Skip to content

Commit

Permalink
chore: fix ellipse & rectangle shadow shape
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoy312 authored and RafaelMendesRosa committed Aug 30, 2023
1 parent a5aa053 commit db080c0
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,19 +133,21 @@ private void OnSurfacePainted(object? sender, SKPaintSurfaceEventArgs e)
};
}

private IShadowShapeContext GetShadowShapeContext(object? content)
private IShadowShapeContext GetShadowShapeContext(object content)
{
return content switch
{
FrameworkElement fe => new RectangularShadowShapeContext(fe.ActualWidth, fe.ActualHeight, GetCornerRadiusFor(Content) ?? default),
// any dp used here, beside width/height that is covered by SizeChanged, needs to register for dp changed in: BindToPaintingProperties\BindToContent()
Ellipse ellipse => new RadiusXYRectShadowShapeContext(ellipse.ActualWidth, ellipse.ActualHeight, ellipse.ActualWidth / 2, ellipse.ActualHeight / 2),
Rectangle rect => new RadiusXYRectShadowShapeContext(rect.ActualWidth, rect.ActualHeight, rect.RadiusX, rect.RadiusY),
FrameworkElement fe => new CornerRadiusRectShadowShapeContext(fe.ActualWidth, fe.ActualHeight, GetCornerRadiusFor(Content) ?? default),

null => RectangularShadowShapeContext.Empty,
_ => throw new NotSupportedException($"Unsupported content type: {content.GetType().Name}"),
};
}

/// <summary>
/// Serves both as a record of states relevant to shadow shape, and the implementations for painting the shadows
/// Serves both as a record of states relevant to shadow shape (not <see cref="Shape"/>, but in the broad sense), and the implementations for painting the shadows.
/// </summary>
private interface IShadowShapeContext
{
Expand All @@ -158,17 +160,9 @@ private interface IShadowShapeContext
void DrawInnerShadow(ShadowPaintState state, SKCanvas canvas, SKPaint paint, ShadowInfo shadow);
}

private record RectangularShadowShapeContext(double ContentWidth, double ContentHeight, CornerRadius CornerRadius) : IShadowShapeContext
private abstract record RoundRectShadowShapeContext(double Width, double Height) : IShadowShapeContext
{
public static readonly RectangularShadowShapeContext Empty = new(0, 0, default);

private SKRoundRect GetContentShape(ShadowPaintState state)
{
var rect = new SKRect(0, 0, (float)ContentWidth * state.PixelRatio, (float)ContentHeight * state.PixelRatio);
var shape = new SKRoundRect(rect, (float)CornerRadius.BottomRight * state.PixelRatio);

return shape;
}
protected abstract SKRoundRect GetContentShape(ShadowPaintState state);

public void ClipToContent(ShadowPaintState state, SKCanvas canvas)
{
Expand Down Expand Up @@ -231,7 +225,6 @@ public void DrawDropShadow(ShadowPaintState state, SKCanvas canvas, SKPaint pain

public void DrawInnerShadow(ShadowPaintState state, SKCanvas canvas, SKPaint paint, ShadowInfo shadow)
{
var cornerRadius = (float)CornerRadius.BottomRight * state.PixelRatio;
var spread = (float)shadow.Spread * state.PixelRatio;
var offsetX = (float)shadow.OffsetX * state.PixelRatio;
var offsetY = (float)shadow.OffsetY * state.PixelRatio;
Expand All @@ -253,12 +246,39 @@ public void DrawInnerShadow(ShadowPaintState state, SKCanvas canvas, SKPaint pai

if (_logger.IsEnabled(LogLevel.Trace))
{
_logger.Trace($"[ShadowContainer] DrawInnerShadow => strokeWidth: {paint.StrokeWidth}, cornerRadius: {cornerRadius}, x: {offsetX}, y: {offsetY}, width: {shape.Rect.Width}, height: {shape.Rect.Height}");
_logger.Trace($"[ShadowContainer] DrawInnerShadow => strokeWidth: {paint.StrokeWidth}, x: {offsetX}, y: {offsetY}, width: {shape.Rect.Width}, height: {shape.Rect.Height}");
}
canvas.DrawRoundRect(shape, paint);
}
}

private record CornerRadiusRectShadowShapeContext(double Width, double Height, CornerRadius CornerRadius) : RoundRectShadowShapeContext(Width, Height)
{
protected override SKRoundRect GetContentShape(ShadowPaintState state)
{
var rect = new SKRect(0, 0, (float)Width * state.PixelRatio, (float)Height * state.PixelRatio);
var radii = new[] { CornerRadius.TopLeft, CornerRadius.TopRight, CornerRadius.BottomRight, CornerRadius.BottomLeft }
.Select(x => (float)x * state.PixelRatio)
.Select(x => new SKPoint(x, x))
.ToArray();
var shape = new SKRoundRect();
shape.SetRectRadii(rect, radii);

return shape;
}
}

private record RadiusXYRectShadowShapeContext(double Width, double Height, double RadiusX, double RadiusY) : RoundRectShadowShapeContext(Width, Height)
{
protected override SKRoundRect GetContentShape(ShadowPaintState state)
{
var rect = new SKRect(0, 0, (float)Width * state.PixelRatio, (float)Height * state.PixelRatio);
var shape = new SKRoundRect(rect, (float)RadiusX * state.PixelRatio, (float)RadiusY * state.PixelRatio);

return shape;
}
}

/// <summary>
/// Record of <see cref="Shadow"/> properties at one point in time.
/// </summary>
Expand Down
43 changes: 24 additions & 19 deletions src/Uno.Toolkit.Skia.WinUI/Controls/Shadows/ShadowContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,6 @@ void OnShadowPropertyChanged(object? sender, PropertyChangedEventArgs e)
}
void OnInnerPropertyChanged(DependencyObject sender, DependencyProperty dp)
{
if (sender == this && dp == CornerRadiusProperty)
{
InvalidateCanvasLayout();
}
InvalidateShadows();
}
void OnContentPropertyChanged(DependencyObject sender, DependencyProperty dp)
Expand Down Expand Up @@ -207,13 +203,29 @@ void BindToContent(object? content)
{
if (content is FrameworkElement contentAsFE)
{
contentAsFE.SizeChanged += OnContentSizeChanged;
contentNestedDisposable.Disposable = new CompositeDisposable
{
Disposable.Create(() => contentAsFE.SizeChanged -= OnContentSizeChanged),
GetCornerRadiusPropertyFor(content) is { } dp ? contentAsFE.RegisterDisposablePropertyChangedCallback(dp, OnContentPropertyChanged) : Disposable.Empty,
contentAsFE.RegisterDisposablePropertyChangedCallback(FrameworkElement.MarginProperty, OnContentPropertyChanged),
RegisterSizeChangedHandler(contentAsFE, OnContentSizeChanged),
RegisterNestedPropertyChangedSafe<FrameworkElement>(GetCornerRadiusPropertyFor(content)),
RegisterNestedPropertyChangedSafe<Rectangle>(Rectangle.RadiusXProperty),
RegisterNestedPropertyChangedSafe<Rectangle>(Rectangle.RadiusYProperty),
RegisterNestedPropertyChangedSafe<FrameworkElement>(FrameworkElement.MarginProperty),
};

static IDisposable RegisterSizeChangedHandler(FrameworkElement fe, SizeChangedEventHandler handler)
{
fe.SizeChanged += handler;
return Disposable.Create(() => fe.SizeChanged -= handler);
}
IDisposable RegisterNestedPropertyChangedSafe<T>(DependencyProperty? dp, DependencyPropertyChangedCallback? callback = null)
{
if (contentAsFE is T && dp is { })
{
return contentAsFE.RegisterDisposablePropertyChangedCallback(dp, callback ?? OnContentPropertyChanged);
}

return Disposable.Empty;
}
}
else
{
Expand Down Expand Up @@ -377,7 +389,7 @@ private void InvalidateShadows(bool force = false)
Grid => Grid.CornerRadiusProperty,
StackPanel => StackPanel.CornerRadiusProperty,

Shape => null,
Shape => null, // note: shapes have special handling, see: GetShadowShapeContext
DependencyObject @do => @do.FindDependencyPropertyUsingReflection<CornerRadius>("CornerRadiusProperty"),
_ => null,
};
Expand All @@ -392,18 +404,11 @@ private void InvalidateShadows(bool force = false)
Border border => border.CornerRadius,
Grid grid => grid.CornerRadius,
StackPanel stackpanel => stackpanel.CornerRadius,
Rectangle border => new CornerRadius(border.RadiusY == 0 ? 0 : (border.RadiusX / border.RadiusY) * (border.Width > border.Height ? border.Width : border.Height)),
Ellipse ellipse => new CornerRadius(ellipse.Height == 0 ? 0 : (ellipse.Width / ellipse.Height / 2) * (ellipse.Width > ellipse.Height ? ellipse.Width : ellipse.Height)),

Shape => null,
DependencyObject @do =>
@do.FindDependencyPropertyUsingReflection<CornerRadius>("CornerRadiusProperty") is { } dp
Shape => null, // note: shapes have special handling, see: GetShadowShapeContext
DependencyObject @do => @do.FindDependencyPropertyUsingReflection<CornerRadius>("CornerRadiusProperty") is { } dp
? (CornerRadius)@do.GetValue(dp)
:
//@do.FindDependencyPropertyUsingReflection<CornerRadius>("RadiusXProperty") is { } radiusX
//? (CornerRadius)new CornerRadius((double)@do.GetValue(radiusX))
//:
null,
: null,
_ => null,
};
}
Expand Down

0 comments on commit db080c0

Please sign in to comment.