Files
ShrlAlgoToolkit/Melskin/Controls/SplashWindow/Border.cs
2026-02-17 22:17:13 +08:00

226 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
namespace Melskin.Controls;
/// <summary>
///
/// </summary>
public class Border : System.Windows.Controls.Border
{
/// <summary>
/// 获取布局剪辑区域的几何形状。
/// </summary>
/// <param name="layoutSlotSize">布局槽的大小。</param>
/// <returns>返回一个表示布局剪辑区域的几何形状如果不需要剪辑则返回null。</returns>
protected override Geometry? GetLayoutClip(Size layoutSlotSize)
{
if (!ClipToBounds)
{
return base.GetLayoutClip(layoutSlotSize);
}
var borderThickness = BorderThickness;
var cornerRadius = CornerRadius;
var renderSize = RenderSize;
if (renderSize.Width <= 0 || renderSize.Height <= 0)
{
return null;
}
var rect = new Rect(0, 0, renderSize.Width, renderSize.Height);
var radii = new Radii(cornerRadius, borderThickness, true);
var layoutGeometry = new StreamGeometry();
using var ctx = layoutGeometry.Open();
GenerateGeometry(ctx, rect, radii);
layoutGeometry.Freeze();
return layoutGeometry;
}
internal static void GenerateGeometry(StreamGeometryContext ctx, Rect rect, Radii radii)
{
//
// compute the coordinates of the key points
//
Point topLeft = new(radii.LeftTop, 0);
Point topRight = new(rect.Width - radii.RightTop, 0);
Point rightTop = new(rect.Width, radii.TopRight);
Point rightBottom = new(rect.Width, rect.Height - radii.BottomRight);
Point bottomRight = new(rect.Width - radii.RightBottom, rect.Height);
Point bottomLeft = new(radii.LeftBottom, rect.Height);
Point leftBottom = new(0, rect.Height - radii.BottomLeft);
Point leftTop = new(0, radii.TopLeft);
//
// check key points for overlap and resolve by partitioning radii according to
// the percentage of each one.
//
// top edge is handled here
if (topLeft.X > topRight.X)
{
var v = radii.LeftTop / (radii.LeftTop + radii.RightTop) * rect.Width;
topLeft.X = v;
topRight.X = v;
}
// right edge
if (rightTop.Y > rightBottom.Y)
{
var v = radii.TopRight / (radii.TopRight + radii.BottomRight) * rect.Height;
rightTop.Y = v;
rightBottom.Y = v;
}
// bottom edge
if (bottomRight.X < bottomLeft.X)
{
var v = radii.LeftBottom / (radii.LeftBottom + radii.RightBottom) * rect.Width;
bottomRight.X = v;
bottomLeft.X = v;
}
// left edge
if (leftBottom.Y < leftTop.Y)
{
var v = radii.TopLeft / (radii.TopLeft + radii.BottomLeft) * rect.Height;
leftBottom.Y = v;
leftTop.Y = v;
}
//
// add on offsets
//
Vector offset = new(rect.TopLeft.X, rect.TopLeft.Y);
topLeft += offset;
topRight += offset;
rightTop += offset;
rightBottom += offset;
bottomRight += offset;
bottomLeft += offset;
leftBottom += offset;
leftTop += offset;
//
// create the border geometry
//
ctx.BeginFigure(topLeft, true /* is filled */, true /* is closed */);
// Top line
ctx.LineTo(topRight, true /* is stroked */, false /* is smooth join */);
// Upper-right corner
var radiusX = rect.TopRight.X - topRight.X;
var radiusY = rightTop.Y - rect.TopRight.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(rightTop, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
// Right line
ctx.LineTo(rightBottom, true /* is stroked */, false /* is smooth join */);
// Lower-right corner
radiusX = rect.BottomRight.X - bottomRight.X;
radiusY = rect.BottomRight.Y - rightBottom.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(bottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
// Bottom line
ctx.LineTo(bottomLeft, true /* is stroked */, false /* is smooth join */);
// Lower-left corner
radiusX = bottomLeft.X - rect.BottomLeft.X;
radiusY = rect.BottomLeft.Y - leftBottom.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(leftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
// Left line
ctx.LineTo(leftTop, true /* is stroked */, false /* is smooth join */);
// Upper-left corner
radiusX = topLeft.X - rect.TopLeft.X;
radiusY = leftTop.Y - rect.TopLeft.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(topLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
}
}
internal struct Radii
{
internal Radii(CornerRadius radii, Thickness borders, bool outer)
{
var left = 0.5 * borders.Left;
var top = 0.5 * borders.Top;
var right = 0.5 * borders.Right;
var bottom = 0.5 * borders.Bottom;
if (outer)
{
if (MathHelper.IsZero(radii.TopLeft))
{
LeftTop = TopLeft = 0.0;
}
else
{
LeftTop = radii.TopLeft + left;
TopLeft = radii.TopLeft + top;
}
if (MathHelper.IsZero(radii.TopRight))
{
TopRight = RightTop = 0.0;
}
else
{
TopRight = radii.TopRight + top;
RightTop = radii.TopRight + right;
}
if (MathHelper.IsZero(radii.BottomRight))
{
RightBottom = BottomRight = 0.0;
}
else
{
RightBottom = radii.BottomRight + right;
BottomRight = radii.BottomRight + bottom;
}
if (MathHelper.IsZero(radii.BottomLeft))
{
BottomLeft = LeftBottom = 0.0;
}
else
{
BottomLeft = radii.BottomLeft + bottom;
LeftBottom = radii.BottomLeft + left;
}
}
else
{
LeftTop = Math.Max(0.0, radii.TopLeft - left);
TopLeft = Math.Max(0.0, radii.TopLeft - top);
TopRight = Math.Max(0.0, radii.TopRight - top);
RightTop = Math.Max(0.0, radii.TopRight - right);
RightBottom = Math.Max(0.0, radii.BottomRight - right);
BottomRight = Math.Max(0.0, radii.BottomRight - bottom);
BottomLeft = Math.Max(0.0, radii.BottomLeft - bottom);
LeftBottom = Math.Max(0.0, radii.BottomLeft - left);
}
}
internal double LeftTop;
internal double TopLeft;
internal double TopRight;
internal double RightTop;
internal double RightBottom;
internal double BottomRight;
internal double BottomLeft;
internal double LeftBottom;
}
}