201 lines
5.4 KiB
C#
201 lines
5.4 KiB
C#
// Licensed to the .NET Foundation under one or more agreements.
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
|
|
using SkiaSharp;
|
|
|
|
namespace Microsoft.Maui.Platform;
|
|
|
|
/// <summary>
|
|
/// Skia-rendered border/frame container control.
|
|
/// </summary>
|
|
public class SkiaBorder : SkiaLayoutView
|
|
{
|
|
private float _strokeThickness = 1;
|
|
private float _cornerRadius = 0;
|
|
private SKColor _stroke = SKColors.Black;
|
|
private float _paddingLeft = 0;
|
|
private float _paddingTop = 0;
|
|
private float _paddingRight = 0;
|
|
private float _paddingBottom = 0;
|
|
private bool _hasShadow;
|
|
|
|
public float StrokeThickness
|
|
{
|
|
get => _strokeThickness;
|
|
set { _strokeThickness = value; Invalidate(); }
|
|
}
|
|
|
|
public float CornerRadius
|
|
{
|
|
get => _cornerRadius;
|
|
set { _cornerRadius = value; Invalidate(); }
|
|
}
|
|
|
|
public SKColor Stroke
|
|
{
|
|
get => _stroke;
|
|
set { _stroke = value; Invalidate(); }
|
|
}
|
|
|
|
public float PaddingLeft
|
|
{
|
|
get => _paddingLeft;
|
|
set { _paddingLeft = value; InvalidateMeasure(); }
|
|
}
|
|
|
|
public float PaddingTop
|
|
{
|
|
get => _paddingTop;
|
|
set { _paddingTop = value; InvalidateMeasure(); }
|
|
}
|
|
|
|
public float PaddingRight
|
|
{
|
|
get => _paddingRight;
|
|
set { _paddingRight = value; InvalidateMeasure(); }
|
|
}
|
|
|
|
public float PaddingBottom
|
|
{
|
|
get => _paddingBottom;
|
|
set { _paddingBottom = value; InvalidateMeasure(); }
|
|
}
|
|
|
|
public bool HasShadow
|
|
{
|
|
get => _hasShadow;
|
|
set { _hasShadow = value; Invalidate(); }
|
|
}
|
|
|
|
public void SetPadding(float all)
|
|
{
|
|
_paddingLeft = _paddingTop = _paddingRight = _paddingBottom = all;
|
|
InvalidateMeasure();
|
|
}
|
|
|
|
public void SetPadding(float horizontal, float vertical)
|
|
{
|
|
_paddingLeft = _paddingRight = horizontal;
|
|
_paddingTop = _paddingBottom = vertical;
|
|
InvalidateMeasure();
|
|
}
|
|
|
|
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
|
|
{
|
|
var borderRect = new SKRect(
|
|
bounds.Left + _strokeThickness / 2,
|
|
bounds.Top + _strokeThickness / 2,
|
|
bounds.Right - _strokeThickness / 2,
|
|
bounds.Bottom - _strokeThickness / 2);
|
|
|
|
// Draw shadow if enabled
|
|
if (_hasShadow)
|
|
{
|
|
using var shadowPaint = new SKPaint
|
|
{
|
|
Color = new SKColor(0, 0, 0, 40),
|
|
MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, 4),
|
|
Style = SKPaintStyle.Fill
|
|
};
|
|
var shadowRect = new SKRect(borderRect.Left + 2, borderRect.Top + 2, borderRect.Right + 2, borderRect.Bottom + 2);
|
|
canvas.DrawRoundRect(new SKRoundRect(shadowRect, _cornerRadius), shadowPaint);
|
|
}
|
|
|
|
// Draw background
|
|
using var bgPaint = new SKPaint
|
|
{
|
|
Color = BackgroundColor,
|
|
Style = SKPaintStyle.Fill,
|
|
IsAntialias = true
|
|
};
|
|
canvas.DrawRoundRect(new SKRoundRect(borderRect, _cornerRadius), bgPaint);
|
|
|
|
// Draw border
|
|
if (_strokeThickness > 0)
|
|
{
|
|
using var borderPaint = new SKPaint
|
|
{
|
|
Color = _stroke,
|
|
Style = SKPaintStyle.Stroke,
|
|
StrokeWidth = _strokeThickness,
|
|
IsAntialias = true
|
|
};
|
|
canvas.DrawRoundRect(new SKRoundRect(borderRect, _cornerRadius), borderPaint);
|
|
}
|
|
|
|
// Draw children (call base which draws children)
|
|
foreach (var child in Children)
|
|
{
|
|
if (child.IsVisible)
|
|
{
|
|
child.Draw(canvas);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override SKRect GetContentBounds()
|
|
{
|
|
return GetContentBounds(Bounds);
|
|
}
|
|
|
|
protected new SKRect GetContentBounds(SKRect bounds)
|
|
{
|
|
return new SKRect(
|
|
bounds.Left + _paddingLeft + _strokeThickness,
|
|
bounds.Top + _paddingTop + _strokeThickness,
|
|
bounds.Right - _paddingRight - _strokeThickness,
|
|
bounds.Bottom - _paddingBottom - _strokeThickness);
|
|
}
|
|
|
|
protected override SKSize MeasureOverride(SKSize availableSize)
|
|
{
|
|
var paddingWidth = _paddingLeft + _paddingRight + _strokeThickness * 2;
|
|
var paddingHeight = _paddingTop + _paddingBottom + _strokeThickness * 2;
|
|
|
|
var childAvailable = new SKSize(
|
|
availableSize.Width - paddingWidth,
|
|
availableSize.Height - paddingHeight);
|
|
|
|
var maxChildSize = SKSize.Empty;
|
|
|
|
foreach (var child in Children)
|
|
{
|
|
var childSize = child.Measure(childAvailable);
|
|
maxChildSize = new SKSize(
|
|
Math.Max(maxChildSize.Width, childSize.Width),
|
|
Math.Max(maxChildSize.Height, childSize.Height));
|
|
}
|
|
|
|
return new SKSize(
|
|
maxChildSize.Width + paddingWidth,
|
|
maxChildSize.Height + paddingHeight);
|
|
}
|
|
|
|
protected override SKRect ArrangeOverride(SKRect bounds)
|
|
{
|
|
|
|
var contentBounds = GetContentBounds(bounds);
|
|
|
|
foreach (var child in Children)
|
|
{
|
|
child.Arrange(contentBounds);
|
|
}
|
|
|
|
return bounds;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Frame control (alias for Border with shadow enabled).
|
|
/// </summary>
|
|
public class SkiaFrame : SkiaBorder
|
|
{
|
|
public SkiaFrame()
|
|
{
|
|
HasShadow = true;
|
|
CornerRadius = 4;
|
|
SetPadding(10);
|
|
BackgroundColor = SKColors.White;
|
|
}
|
|
}
|