Merge pull request 'perf(input-field): O(n) cursor hit test via Font::NearestCursorByte (#56)' (#90) from claude/issue-56 into master
This commit is contained in:
commit
79db987f83
6 changed files with 280 additions and 16 deletions
|
|
@ -72,6 +72,30 @@ std::uint32_t Font::GetLineWidth(const std::string_view text, float size) {
|
|||
return lineWidth;
|
||||
}
|
||||
|
||||
std::size_t Font::NearestCursorByte(std::string_view text, float size, float target) {
|
||||
if (target <= 0.0f) return 0;
|
||||
float scale = stbtt_ScaleForPixelHeight(&font, size);
|
||||
std::size_t best = 0; // byte offset 0 → caret before the first glyph
|
||||
float bestDist = target; // |target - 0|, and target > 0 here
|
||||
float cumWidth = 0.0f;
|
||||
std::size_t i = 0;
|
||||
while (i < text.size()) {
|
||||
std::uint32_t cp = DecodeUtf8(text, i); // i advances to the next boundary
|
||||
if (cp == 0) break;
|
||||
int advance, lsb;
|
||||
stbtt_GetCodepointHMetrics(&font, static_cast<int>(cp), &advance, &lsb);
|
||||
cumWidth += (int)(advance * scale); // match GetLineWidth's per-glyph rounding
|
||||
float d = std::abs(target - cumWidth);
|
||||
if (d < bestDist) {
|
||||
bestDist = d;
|
||||
best = i; // byte offset just past this codepoint
|
||||
} else {
|
||||
break; // monotonic advance ⇒ already past the closest boundary
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
float Font::LineHeight(float size) {
|
||||
float scale = stbtt_ScaleForPixelHeight(&font, size);
|
||||
return (ascent - descent + lineGap) * scale;
|
||||
|
|
|
|||
|
|
@ -117,23 +117,12 @@ std::size_t Crafter::InputField_HitTestCursor(const InputField& f,
|
|||
Font& font, float fontSize,
|
||||
const InputFieldColors& colors)
|
||||
{
|
||||
// Single O(n) cumulative-advance pass over the value, keyed at codepoint
|
||||
// byte boundaries — matching cursorPos, which is a byte offset. The old
|
||||
// loop re-walked the prefix for every boundary (O(n^2) glyph metric
|
||||
// lookups) and could even land mid-codepoint on multibyte input.
|
||||
float target = clickX - (rect.x + colors.paddingX);
|
||||
if (target <= 0.0f) return 0;
|
||||
|
||||
std::size_t best = 0;
|
||||
float bestDist = std::abs(target);
|
||||
for (std::size_t i = 1; i <= f.value.size(); ++i) {
|
||||
std::string_view sub(f.value.data(), i);
|
||||
float w = static_cast<float>(font.GetLineWidth(sub, fontSize));
|
||||
float d = std::abs(target - w);
|
||||
if (d < bestDist) {
|
||||
bestDist = d;
|
||||
best = i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
return font.NearestCursorByte(f.value, fontSize, target);
|
||||
}
|
||||
|
||||
void Crafter::DrawInputField(UIBuffer& buf, const InputField& f, Rect rect,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue