feat(D.2b): UiChatView dat-font transcript + 1-line wheel quantum
- Add `DatFont` property (UiDatFont?): when set, OnDraw uses ctx.DrawStringDat + datFont.MeasureWidth for all transcript lines; BitmapFont path unchanged as fallback when DatFont is null. - Cache `_lastDatFont` alongside `_lastFont` so HitChar hit-tests the same advance source that drew the last frame. - HitChar prefers `_lastDatFont` (via UiDatFont.GlyphAdvance) over `_lastFont` (via bf.Advance) for column resolution, keeping drag-select and Ctrl+C accurate with the dat font. - Scroll event handler uses DatFont?.LineHeight first, so the wheel quantum stays correct when the dat font has a different line height. - WheelLines 3f → 1f: retail UIElement_Text::HandleMouseWheel (@0x471450) advances one line per notch; our 3-line quantum was wrong. - Add UiChatViewDatFontTests: pins GlyphAdvance formula (Before+Width+After = 10) and CharIndexAt dat-advance integration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
50883e445b
commit
7552dcba39
2 changed files with 68 additions and 15 deletions
|
|
@ -34,6 +34,11 @@ public sealed class UiChatView : UiElement
|
|||
/// <summary>Font for the transcript; falls back to the context default.</summary>
|
||||
public BitmapFont? Font { get; set; }
|
||||
|
||||
/// <summary>Retail dat font (0x40000000) for the transcript. When set, glyphs
|
||||
/// render via the two-pass dat-font blit and measure/hit-test use the dat glyph
|
||||
/// advance; when null, the debug BitmapFont path is used. Set by the controller.</summary>
|
||||
public UiDatFont? DatFont { get; set; }
|
||||
|
||||
/// <summary>Keyboard device for clipboard (Ctrl+C) + modifier state. Wired by
|
||||
/// the host from <see cref="UiHost.Keyboard"/>.</summary>
|
||||
public Silk.NET.Input.IKeyboard? Keyboard { get; set; }
|
||||
|
|
@ -49,11 +54,12 @@ public sealed class UiChatView : UiElement
|
|||
|
||||
// Pixels the transcript is scrolled UP from the newest line (0 = pinned to bottom).
|
||||
private float _scroll;
|
||||
private const float WheelLines = 3f; // lines advanced per wheel notch
|
||||
private const float WheelLines = 1f; // lines advanced per wheel notch (retail = 1 line per notch)
|
||||
|
||||
// ── Cached layout from the last OnDraw, so OnEvent hit-tests the SAME geometry ──
|
||||
private IReadOnlyList<Line> _lastLines = Array.Empty<Line>();
|
||||
private BitmapFont? _lastFont;
|
||||
private UiDatFont? _lastDatFont;
|
||||
private float _lastLineHeight = 16f;
|
||||
private float _lastBaseY; // top Y of line 0 in local space
|
||||
private float _lastPadding = 4f;
|
||||
|
|
@ -85,21 +91,24 @@ public sealed class UiChatView : UiElement
|
|||
{
|
||||
ctx.DrawRect(0, 0, Width, Height, BackgroundColor);
|
||||
|
||||
var font = Font ?? ctx.DefaultFont;
|
||||
if (font is null) return;
|
||||
// Prefer the retail dat font when set; fall back to BitmapFont.
|
||||
var datFont = DatFont;
|
||||
var bitmapFont = datFont is null ? (Font ?? ctx.DefaultFont) : null;
|
||||
if (datFont is null && bitmapFont is null) return;
|
||||
|
||||
var lines = LinesProvider();
|
||||
|
||||
// Cache the geometry OnEvent will hit-test against. Even when there are no
|
||||
// lines we record the font/padding so a stray hit-test is harmless.
|
||||
_lastLines = lines;
|
||||
_lastFont = font;
|
||||
_lastLineHeight = font.LineHeight;
|
||||
_lastDatFont = datFont;
|
||||
_lastFont = bitmapFont;
|
||||
_lastLineHeight = datFont is not null ? datFont.LineHeight : bitmapFont!.LineHeight;
|
||||
_lastPadding = Padding;
|
||||
|
||||
if (lines.Count == 0) return;
|
||||
|
||||
float lh = font.LineHeight;
|
||||
float lh = _lastLineHeight;
|
||||
float top = Padding, bottom = Height - Padding;
|
||||
float innerH = bottom - top;
|
||||
float contentH = lines.Count * lh;
|
||||
|
|
@ -129,13 +138,25 @@ public sealed class UiChatView : UiElement
|
|||
c1 = Math.Clamp(c1, 0, text.Length);
|
||||
if (c1 > c0)
|
||||
{
|
||||
float hx = Padding + font.MeasureWidth(text.Substring(0, c0));
|
||||
float hw = font.MeasureWidth(text.Substring(c0, c1 - c0));
|
||||
float hx, hw;
|
||||
if (datFont is not null)
|
||||
{
|
||||
hx = Padding + datFont.MeasureWidth(text.Substring(0, c0));
|
||||
hw = datFont.MeasureWidth(text.Substring(c0, c1 - c0));
|
||||
}
|
||||
else
|
||||
{
|
||||
hx = Padding + bitmapFont!.MeasureWidth(text.Substring(0, c0));
|
||||
hw = bitmapFont.MeasureWidth(text.Substring(c0, c1 - c0));
|
||||
}
|
||||
ctx.DrawRect(hx, y, hw, lh, SelectionColor);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.DrawString(text, Padding, y, lines[i].Color, font);
|
||||
if (datFont is not null)
|
||||
ctx.DrawStringDat(datFont, text, Padding, y, lines[i].Color);
|
||||
else
|
||||
ctx.DrawString(text, Padding, y, lines[i].Color, bitmapFont);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +166,7 @@ public sealed class UiChatView : UiElement
|
|||
{
|
||||
case UiEventType.Scroll:
|
||||
{
|
||||
float lh = (Font ?? _lastFont)?.LineHeight ?? 16f;
|
||||
float lh = DatFont?.LineHeight ?? (Font ?? _lastFont)?.LineHeight ?? 16f;
|
||||
// Silk wheel +Y = scroll up = reveal older = shift content down = larger _scroll.
|
||||
_scroll += e.Data0 * WheelLines * lh; // re-clamped next OnDraw against live content
|
||||
return true;
|
||||
|
|
@ -316,11 +337,13 @@ public sealed class UiChatView : UiElement
|
|||
line = Math.Clamp(line, 0, lines.Count - 1);
|
||||
|
||||
string text = lines[line].Text;
|
||||
var font = _lastFont;
|
||||
int col = font is null
|
||||
? 0
|
||||
: CharIndexAt(text, ch => font.TryGetGlyph(ch, out var g) ? g.Advance : 0f,
|
||||
localX - _lastPadding);
|
||||
int col = _lastDatFont is { } df
|
||||
? CharIndexAt(text, ch => df.TryGetGlyph(ch, out var g) ? UiDatFont.GlyphAdvance(g) : 0f,
|
||||
localX - _lastPadding)
|
||||
: (_lastFont is { } bf
|
||||
? CharIndexAt(text, ch => bf.TryGetGlyph(ch, out var bg) ? bg.Advance : 0f,
|
||||
localX - _lastPadding)
|
||||
: 0);
|
||||
return new Pos(line, col);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue