From 4b12def5d8cd1c201a6a44cb4ffbb76c256af520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Oppeb=C3=B8en?= Date: Wed, 30 Nov 2022 01:51:07 +0100 Subject: [PATCH] LibWeb: Use start-of-line in hit test of position directly to the left This fixes a bug where selecting from the left within a line would begin the selection from the right, as pointed out by @AtkinsSJ in https://github.com/SerenityOS/serenity/pull/16245#pullrequestreview-1197595820 --- .../Libraries/LibWeb/Painting/PaintableBox.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index 871d283a89..4e320a74f9 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -699,8 +699,23 @@ Optional PaintableWithLines::hit_test(CSSPixelPoint position, Hit return fragment.layout_node().paintable()->hit_test(position, type); return HitTestResult { *fragment.layout_node().paintable(), fragment.text_index_at(position.x().value()) }; } - if (fragment_absolute_rect.top() <= position.y()) + + // If we reached this point, the position is not within the fragment. However, the fragment start or end might be the place to place the cursor. + // This determines whether the fragment is a good candidate for the position. The last such good fragment is chosen. + // The best candidate is either the end of the line above, the beginning of the line below, or the beginning or end of the current line. + // We arbitrarily choose to consider the end of the line above and ignore the beginning of the line below. + // If we knew the direction of selection, we could make a better choice. + if (fragment_absolute_rect.bottom() <= position.y()) { // fully below the fragment last_good_candidate = HitTestResult { *fragment.layout_node().paintable(), fragment.start() + fragment.length() }; + } else if (fragment_absolute_rect.top() <= position.y()) { // vertically within the fragment + if (position.x() < fragment_absolute_rect.left()) { // left of the fragment + if (!last_good_candidate.has_value()) { // first fragment of the line + last_good_candidate = HitTestResult { *fragment.layout_node().paintable(), fragment.start() }; + } + } else { // right of the fragment + last_good_candidate = HitTestResult { *fragment.layout_node().paintable(), fragment.start() + fragment.length() }; + } + } } }