using System.Numerics; namespace AcDream.App.UI; /// /// A whose background is the retail 8-piece window bevel /// (): 4 corners + 4 edges around a tiled /// center fill. Retires the flat translucent rect (divergence row TS-30). /// Sprites resolve to (GL handle, width, height) via an injected delegate so /// the widget is testable without GL. In production: /// id => { var t = cache.GetOrUploadRenderSurface(id, out var w, out var h); return (t, w, h); }. /// public sealed class UiNineSlicePanel : UiPanel { /// A placed chrome piece: destination rect in local pixel space. public readonly record struct Rect(float X, float Y, float W, float H); /// The nine destination rects for an 8-piece border + center. public readonly record struct FrameRects( Rect Center, Rect Top, Rect Bottom, Rect Left, Rect Right, Rect TL, Rect TR, Rect BL, Rect BR); private readonly System.Func _resolve; public UiNineSlicePanel(System.Func resolve) { _resolve = resolve; BackgroundColor = Vector4.Zero; // suppress the base flat-rect fill BorderColor = Vector4.Zero; } /// /// Destination rects (local px) for a frame of (, /// ) with border thickness : /// b×b corners, top/bottom edges spanning the interior width at height b, /// left/right edges spanning the interior height at width b, center fills /// the interior. /// public static FrameRects ComputeFrameRects(float w, float h, int b) { float innerW = w - 2 * b; float innerH = h - 2 * b; return new FrameRects( Center: new Rect(b, b, innerW, innerH), Top: new Rect(b, 0, innerW, b), Bottom: new Rect(b, h - b, innerW, b), Left: new Rect(0, b, b, innerH), Right: new Rect(w - b, b, b, innerH), TL: new Rect(0, 0, b, b), TR: new Rect(w - b, 0, b, b), BL: new Rect(0, h - b, b, b), BR: new Rect(w - b, h - b, b, b)); } protected override void OnDraw(UiRenderContext ctx) { var r = ComputeFrameRects(Width, Height, RetailChromeSprites.Border); // center + edges tile (UV repeat); corners stretch 1:1. DrawTiled(ctx, RetailChromeSprites.CenterFill, r.Center); DrawTiled(ctx, RetailChromeSprites.TopEdge, r.Top); DrawTiled(ctx, RetailChromeSprites.BottomEdge, r.Bottom); DrawTiled(ctx, RetailChromeSprites.LeftEdge, r.Left); DrawTiled(ctx, RetailChromeSprites.RightEdge, r.Right); DrawStretched(ctx, RetailChromeSprites.CornerTL, r.TL); DrawStretched(ctx, RetailChromeSprites.CornerTR, r.TR); DrawStretched(ctx, RetailChromeSprites.CornerBL, r.BL); DrawStretched(ctx, RetailChromeSprites.CornerBR, r.BR); } private void DrawTiled(UiRenderContext ctx, uint id, Rect d) { if (d.W <= 0 || d.H <= 0) return; var (tex, tw, th) = _resolve(id); if (tex == 0 || tw == 0 || th == 0) return; ctx.DrawSprite(tex, d.X, d.Y, d.W, d.H, 0, 0, d.W / tw, d.H / th, Vector4.One); } private void DrawStretched(UiRenderContext ctx, uint id, Rect d) { if (d.W <= 0 || d.H <= 0) return; var (tex, _, _) = _resolve(id); if (tex == 0) return; ctx.DrawSprite(tex, d.X, d.Y, d.W, d.H, 0, 0, 1, 1, Vector4.One); } }