LibWeb: Improve caret positioning behavior inside fragments

When clicking on a glyph or starting a selection on it, we would use the
glyph's offset/index as the position which represents the left side of
the glyph, or the position between the glyph and the glyph before it.

Instead of looking at which glyph is under the mouse pointer, look at
which glyph boundary is closer. Now, if you click to the right of a
glyph (but still on that glyph), it correctly selects the next glyph's
offset as the position.
This commit is contained in:
Jelle Raaijmakers 2025-06-12 13:04:58 +02:00 committed by Jelle Raaijmakers
commit dbbdf2cebc
Notes: github-actions[bot] 2025-06-13 13:09:58 +00:00
4 changed files with 18 additions and 22 deletions

View file

@ -41,34 +41,30 @@ size_t PaintableFragment::text_index_at(CSSPixelPoint position) const
if (!is<TextPaintable>(paintable()))
return 0;
CSSPixels relative_inline_offset = [&]() {
auto relative_inline_offset = [&] {
switch (orientation()) {
case Gfx::Orientation::Horizontal:
return position.x() - absolute_rect().x();
case Gfx::Orientation::Vertical:
return position.y() - absolute_rect().y();
default:
VERIFY_NOT_REACHED();
case Orientation::Horizontal:
return (position.x() - absolute_rect().x()).to_float();
case Orientation::Vertical:
return (position.y() - absolute_rect().y()).to_float();
}
VERIFY_NOT_REACHED();
}();
if (relative_inline_offset < 0)
return 0;
auto const& glyphs = m_glyph_run->glyphs();
auto smallest_distance = AK::NumericLimits<float>::max();
for (size_t i = 0; i < glyphs.size(); ++i) {
auto glyph_position = CSSPixels::nearest_value_for(glyphs[i].position.x());
if (i + 1 < glyphs.size()) {
auto next_glyph_position = CSSPixels::nearest_value_for(glyphs[i + 1].position.x());
if (relative_inline_offset >= glyph_position && relative_inline_offset < next_glyph_position)
return m_start + i;
} else {
if (relative_inline_offset >= glyph_position)
return m_start + i;
}
auto distance_to_position = AK::abs(glyphs[i].position.x() - relative_inline_offset);
// The last distance was smaller than this new distance, so we've found the closest glyph.
if (distance_to_position > smallest_distance)
return m_start + i - 1;
smallest_distance = distance_to_position;
}
return m_start + m_length;
return m_start + m_length - 1;
}
CSSPixelRect PaintableFragment::range_rect(size_t start_offset, size_t end_offset) const