diff --git a/src/AcDream.App/UI/UiElement.cs b/src/AcDream.App/UI/UiElement.cs
index 7e1df4ad..b22da24e 100644
--- a/src/AcDream.App/UI/UiElement.cs
+++ b/src/AcDream.App/UI/UiElement.cs
@@ -167,6 +167,15 @@ public abstract class UiElement
///
protected virtual void OnDraw(UiRenderContext ctx) { }
+ ///
+ /// Draw AFTER this element's own children, but still within this element's
+ /// transform/alpha (NOT a global pass like ). Use for a
+ /// window FRAME border, which must be the outermost layer drawn OVER its content's
+ /// edges (so content can't poke through the frame), while the frame's center fill
+ /// stays a background in . Default: nothing.
+ ///
+ protected virtual void OnDrawAfterChildren(UiRenderContext ctx) { }
+
///
/// Draw content that must sit ON TOP of the ENTIRE UI, regardless of this
/// element's position in the tree — open menus, dropdowns, tooltips. Called in
@@ -228,6 +237,10 @@ public abstract class UiElement
for (int i = 0; i < ordered.Length; i++)
ordered[i].DrawSelfAndChildren(ctx);
}
+
+ // Foreground pass for this element (e.g. a window frame's border drawn
+ // OVER its content's edges). Default no-op for ordinary elements.
+ OnDrawAfterChildren(ctx);
}
finally
{
diff --git a/src/AcDream.App/UI/UiNineSlicePanel.cs b/src/AcDream.App/UI/UiNineSlicePanel.cs
index 9c18f095..f407f07b 100644
--- a/src/AcDream.App/UI/UiNineSlicePanel.cs
+++ b/src/AcDream.App/UI/UiNineSlicePanel.cs
@@ -61,9 +61,18 @@ public sealed class UiNineSlicePanel : UiPanel
protected override void OnDraw(UiRenderContext ctx)
{
+ // Center fill is the window BACKGROUND — it must sit UNDER the content, so it
+ // draws here (before children). The bevel border + grip is the OUTERMOST layer
+ // and draws in OnDrawAfterChildren (over the content's edges) so content can
+ // never poke through the frame (e.g. the toolbar's 2px bottom-right cap overhang).
var r = ComputeFrameRects(Width, Height, RetailChromeSprites.Border);
- // center + edges tile (UV repeat); corners stretch 1:1.
DrawTiled(ctx, RetailChromeSprites.CenterFill, r.Center);
+ }
+
+ protected override void OnDrawAfterChildren(UiRenderContext ctx)
+ {
+ var r = ComputeFrameRects(Width, Height, RetailChromeSprites.Border);
+ // 8-piece bevel: edges tile (UV repeat); corners stretch 1:1.
DrawTiled(ctx, RetailChromeSprites.TopEdge, r.Top);
DrawTiled(ctx, RetailChromeSprites.BottomEdge, r.Bottom);
DrawTiled(ctx, RetailChromeSprites.LeftEdge, r.Left);
@@ -73,10 +82,8 @@ public sealed class UiNineSlicePanel : UiPanel
DrawStretched(ctx, RetailChromeSprites.CornerBL, r.BL);
DrawStretched(ctx, RetailChromeSprites.CornerBR, r.BR);
- // Resize-grip overlay (gold ridged edges + square corner studs) drawn on
- // top of the bevel — the second border layer the vitals LayoutDesc carries
- // (0x1000063B–0x10000642). Edges tile; the corner stud is the same sprite
- // at all four corners.
+ // Resize-grip overlay (gold ridged edges + square corner studs) on top of the
+ // bevel — the second border layer the vitals LayoutDesc carries (0x1000063B–0x10000642).
DrawTiled(ctx, RetailChromeSprites.GripTop, r.Top);
DrawTiled(ctx, RetailChromeSprites.GripBottom, r.Bottom);
DrawTiled(ctx, RetailChromeSprites.GripLeft, r.Left);