feat(D.2b): UiNineSlicePanel — 8-piece retail window frame + geometry test
Implements the retail floating-window bevel as a UiPanel subclass using RetailChromeSprites: 4 tiled edges + 4 stretched corners + tiled center fill, matching the 8-piece border layout confirmed by the D.2b Step-0 prove-out. Resolver delegate keeps GL out of unit tests. Geometry verified by ComputeFrameRects_PlacesCornersEdgesAndCenter (1/1 pass). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8e91805206
commit
0bf790c8bf
2 changed files with 112 additions and 0 deletions
85
src/AcDream.App/UI/UiNineSlicePanel.cs
Normal file
85
src/AcDream.App/UI/UiNineSlicePanel.cs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
using System.Numerics;
|
||||
|
||||
namespace AcDream.App.UI;
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="UiPanel"/> whose background is the retail 8-piece window bevel
|
||||
/// (<see cref="RetailChromeSprites"/>): 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:
|
||||
/// <c>id => { var t = cache.GetOrUploadRenderSurface(id, out var w, out var h); return (t, w, h); }</c>.
|
||||
/// </summary>
|
||||
public sealed class UiNineSlicePanel : UiPanel
|
||||
{
|
||||
/// <summary>A placed chrome piece: destination rect in local pixel space.</summary>
|
||||
public readonly record struct Rect(float X, float Y, float W, float H);
|
||||
|
||||
/// <summary>The nine destination rects for an 8-piece border + center.</summary>
|
||||
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<uint, (uint tex, int w, int h)> _resolve;
|
||||
|
||||
public UiNineSlicePanel(System.Func<uint, (uint, int, int)> resolve)
|
||||
{
|
||||
_resolve = resolve;
|
||||
BackgroundColor = Vector4.Zero; // suppress the base flat-rect fill
|
||||
BorderColor = Vector4.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destination rects (local px) for a frame of (<paramref name="w"/>,
|
||||
/// <paramref name="h"/>) with border thickness <paramref name="b"/>:
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue