// 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; /// /// Skia-rendered border/frame container control. /// 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; } } /// /// Frame control (alias for Border with shadow enabled). /// public class SkiaFrame : SkiaBorder { public SkiaFrame() { HasShadow = true; CornerRadius = 4; SetPadding(10); BackgroundColor = SKColors.White; } }