perf(ui): memoize the input-field caret prefix width (#128)
DrawInputField re-measured the cursor prefix via Font::GetLineWidth on every frame of a focused field, even though only the blink (caretVisible) changes frame-to-frame — value, cursorPos and fontSize are stable across the vast majority of frames. Cache the measured prefix WIDTH on the InputField, keyed on (prefix bytes, fontSize). A cheap byte compare of the cached prefix guards the expensive per-glyph UTF-8 decode + advance accumulation. The cache is mutable so the const-ref draw fn can refresh it. The absolute caretX is deliberately NOT cached: it adds the layout- dependent textX (rect.x + paddingX) each frame, so caching it would give a relocated field a stale caret. Caching the width keeps that correct. Adds tests/InputFieldCaretCache pinning: the cached caret matches the uncached GetLineWidth oracle across states, a redraw (cache hit) is identical to the miss, the cache invalidates on value/cursorPos/fontSize changes, the blink never moves the caret, and a relocated field shifts the caret by exactly the rect delta. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2c07abee00
commit
a45e793da4
5 changed files with 309 additions and 1 deletions
|
|
@ -58,6 +58,20 @@ export namespace Crafter {
|
|||
InputFieldType type = InputFieldType::Text;
|
||||
bool focused = false;
|
||||
std::size_t cursorPos = 0; // byte offset into `value`
|
||||
|
||||
// Caret-x memo (issue #128). For a focused field DrawInputField measures
|
||||
// the cursor prefix via Font::GetLineWidth every frame, but only the
|
||||
// blink (caretVisible) changes frame-to-frame — value/cursorPos/fontSize
|
||||
// are stable across the vast majority of frames. Cache the measured
|
||||
// prefix WIDTH (the GetLineWidth result, in px) keyed on the inputs that
|
||||
// determine it: the prefix bytes and fontSize. The final caretX adds the
|
||||
// layout-dependent textX each frame, so it is intentionally NOT cached —
|
||||
// a field that moves on screen with an unchanged value would otherwise
|
||||
// get a stale caret. `mutable` so the `const InputField&` draw fn can
|
||||
// refresh it; these fields are internal and need no user initialisation.
|
||||
mutable std::string caretCachePrefix_;
|
||||
mutable float caretCacheFontSize_ = -1.0f; // <0 = unset
|
||||
mutable float caretCacheWidth_ = 0.0f;
|
||||
};
|
||||
|
||||
// Returns true if `s` is a valid candidate for the given type, including
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue