fix(ui): render live component counts
Port retail text-surface clipping so the 15px component count field remains visible under the 16px DAT font. Cover SetStackSize decrements and final component removal while preserving desired restock values. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
a1175e6aac
commit
0527325b25
7 changed files with 217 additions and 36 deletions
|
|
@ -18,34 +18,10 @@ internal readonly record struct UiClipRect(float Left, float Top, float Right, f
|
|||
UiClipRect clip,
|
||||
ref float x, ref float y, ref float w, ref float h,
|
||||
ref float u0, ref float v0, ref float u1, ref float v1)
|
||||
{
|
||||
if (clip.IsEmpty || w <= 0f || h <= 0f)
|
||||
return false;
|
||||
|
||||
float left = MathF.Max(x, clip.Left);
|
||||
float top = MathF.Max(y, clip.Top);
|
||||
float right = MathF.Min(x + w, clip.Right);
|
||||
float bottom = MathF.Min(y + h, clip.Bottom);
|
||||
if (right <= left || bottom <= top)
|
||||
return false;
|
||||
|
||||
float oldX = x, oldY = y, oldW = w, oldH = h;
|
||||
float oldU0 = u0, oldV0 = v0, oldU1 = u1, oldV1 = v1;
|
||||
float tx0 = (left - oldX) / oldW;
|
||||
float tx1 = (right - oldX) / oldW;
|
||||
float ty0 = (top - oldY) / oldH;
|
||||
float ty1 = (bottom - oldY) / oldH;
|
||||
|
||||
x = left;
|
||||
y = top;
|
||||
w = right - left;
|
||||
h = bottom - top;
|
||||
u0 = oldU0 + (oldU1 - oldU0) * tx0;
|
||||
u1 = oldU0 + (oldU1 - oldU0) * tx1;
|
||||
v0 = oldV0 + (oldV1 - oldV0) * ty0;
|
||||
v1 = oldV0 + (oldV1 - oldV0) * ty1;
|
||||
return true;
|
||||
}
|
||||
=> QuadClipper.TryClip(
|
||||
clip.Left, clip.Top, clip.Right, clip.Bottom,
|
||||
ref x, ref y, ref w, ref h,
|
||||
ref u0, ref v0, ref u1, ref v1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -210,7 +186,16 @@ public sealed class UiRenderContext
|
|||
{
|
||||
var f = font ?? DefaultFont;
|
||||
if (f is null) return;
|
||||
TextRenderer.DrawString(f, text, _current.X + x, _current.Y + y, color);
|
||||
float screenX = _current.X + x;
|
||||
float screenY = _current.Y + y;
|
||||
if (_clip is { } clip)
|
||||
{
|
||||
TextRenderer.DrawStringClipped(
|
||||
f, text, screenX, screenY, color,
|
||||
clip.Left, clip.Top, clip.Right, clip.Bottom);
|
||||
return;
|
||||
}
|
||||
TextRenderer.DrawString(f, text, screenX, screenY, color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -336,6 +336,25 @@ public sealed class UiText : UiElement, IUiDatStateful
|
|||
}
|
||||
|
||||
private void DrawText(UiRenderContext ctx)
|
||||
{
|
||||
// Retail UIElement_Text::DrawSelf @ 0x00467AA0 receives the text element's
|
||||
// visible surface as arg3 and clips each glyph blit to that rectangle. This is
|
||||
// observable in LayoutDesc 0x21000033: the owned component count is a 15px-high
|
||||
// text element using a 16px DAT font, so rejecting a partially visible line makes
|
||||
// the value disappear entirely. The shared render context clips both DAT and
|
||||
// bitmap glyph quads and composes this bound with any list/window ancestor clip.
|
||||
ctx.PushClip(0f, 0f, Width, Height);
|
||||
try
|
||||
{
|
||||
DrawClippedText(ctx);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ctx.PopClip();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawClippedText(UiRenderContext ctx)
|
||||
{
|
||||
// Static centered single-line mode (vitals cur/max numbers etc.): draw the first
|
||||
// line centered H+V (or H+Top/Bottom per VerticalJustify) with the SAME formula
|
||||
|
|
@ -451,7 +470,7 @@ public sealed class UiText : UiElement, IUiDatStateful
|
|||
for (int i = 0; i < lines.Count; i++)
|
||||
{
|
||||
float y = baseY + i * lh;
|
||||
if (y < top || y + lh > bottom) continue; // whole-line vertical clip (no scissor yet)
|
||||
if (!LineIntersectsViewport(y, lh, top, bottom)) continue;
|
||||
|
||||
string text = lines[i].Text;
|
||||
float lineX = HorizontalOffset(text, datFont, bitmapFont);
|
||||
|
|
@ -489,6 +508,19 @@ public sealed class UiText : UiElement, IUiDatStateful
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True when any vertical portion of a line intersects a text viewport. Retail
|
||||
/// clips the glyphs at the viewport edge; it does not require the full line box to fit.
|
||||
/// </summary>
|
||||
internal static bool LineIntersectsViewport(
|
||||
float lineTop,
|
||||
float lineHeight,
|
||||
float viewportTop,
|
||||
float viewportBottom)
|
||||
=> lineHeight > 0f
|
||||
&& lineTop < viewportBottom
|
||||
&& lineTop + lineHeight > viewportTop;
|
||||
|
||||
private float HorizontalOffset(string text, UiDatFont? datFont, BitmapFont? bitmapFont)
|
||||
{
|
||||
float width = datFont is not null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue