Repository navigation
Fix TextLayout.HitTestPoint IsInside for multi-line and aligned text - #22433
Conversation
Add a test that hit-tests a point over the first character of each line in a three-line TextLayout, once with hard line breaks and once with wrapping. HitTestPoint incorrectly returns IsInside = false for a point over a character on any line below the first. The CharacterHit is correct.
Check IsInside against the point's offset from the top of the hit line instead of from the top of the layout.
|
Isn't the issue that we are using the same point for all lines? So, by translating the point to the line's local coordinates, we should get the same desired results. This would not require changing the signature of the helper function. Ideally, we make it clear that the specified point should be in local coordinates. |
|
There was a problem hiding this comment.
🟢 Approval recommended
The implementation correctly addresses the coordinate-space bug and includes focused regression coverage.
0 open findings
What changed in this PR
Fixes multiline TextLayout.HitTestPoint results by evaluating vertical containment relative to the selected line.
Changes:
- Passes each line’s vertical offset into hit-test result calculation.
- Adds explicit and wrapped multiline regression tests.
| File | Description |
|---|---|
src/Avalonia.Base/Media/TextFormatting/TextLayout.cs |
Corrects per-line IsInside calculation. |
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs |
Covers hit testing across multiple lines. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Translate the hit point into the hit line's coordinates in HitTestPoint instead of passing the line's top to GetHitTestResult, so the helper works in a single coordinate space. The check for a point below the text becomes y > textLine.Height, which is equivalent because only the last line can have a point below it.
|
@cla-avalonia agree |
Add a test that hit-tests a point just inside the right edge of a centered or right-aligned line, in both flow directions. HitTestPoint incorrectly returns IsInside = false because it ignores the line's horizontal offset.
Also translate the hit point horizontally by the line's Start, so the point passed to GetHitTestResult is relative to the line's top-left corner as documented.
|
@Gillibald Thanks, that's a better approach. Done in 4220d5b: HitTestPoint now translates the point into the hit line's coordinates, and GetHitTestResult keeps its original signature, with the parameter documented as line-relative. Doing that showed the same problem horizontally. The point also needs to be relative to the line's Start, or IsInside is wrong for centered and right-aligned lines. I've added a test and fix for that in 6ab6474 and 02a0225. I added doc comments to the TextHitResult method, and renamed param point to linePoint, to make it clear it's local coords. |
- Points left of, right of, above and below an aligned line must not report IsInside, guarding against the line-relative translation over-correcting. - An aligned layout hit at Start + dx must give the same CharacterHit, TextPosition, IsInside and IsTrailing as a left-aligned layout hit at dx, which covers the newline trimming that now sees line-relative x.
NuGet packagesThe packages for this pull request have been published (workflow run):
|
…22433) * Test HitTestPoint IsInside on lines after the first Add a test that hit-tests a point over the first character of each line in a three-line TextLayout, once with hard line breaks and once with wrapping. HitTestPoint incorrectly returns IsInside = false for a point over a character on any line below the first. The CharacterHit is correct. * Fix HitTestPoint IsInside on lines after the first Check IsInside against the point's offset from the top of the hit line instead of from the top of the layout. * Pass a line-relative point to GetHitTestResult Translate the hit point into the hit line's coordinates in HitTestPoint instead of passing the line's top to GetHitTestResult, so the helper works in a single coordinate space. The check for a point below the text becomes y > textLine.Height, which is equivalent because only the last line can have a point below it. * Test HitTestPoint IsInside on aligned lines Add a test that hit-tests a point just inside the right edge of a centered or right-aligned line, in both flow directions. HitTestPoint incorrectly returns IsInside = false because it ignores the line's horizontal offset. * Fix HitTestPoint IsInside on aligned lines Also translate the hit point horizontally by the line's Start, so the point passed to GetHitTestResult is relative to the line's top-left corner as documented. * Test HitTestPoint outside aligned lines and aligned/left parity - Points left of, right of, above and below an aligned line must not report IsInside, guarding against the line-relative translation over-correcting. - An aligned layout hit at Start + dx must give the same CharacterHit, TextPosition, IsInside and IsTrailing as a left-aligned layout hit at dx, which covers the newline trimming that now sees line-relative x. --------- Co-authored-by: Benedikt Stebner <Gillibald@users.noreply.github.com> Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
What does the pull request do?
Fixes
TextLayout.HitTestPointso thatIsInsideis correct for points on lines below the first, and on centered or right-aligned lines.What is the current behavior?
HitTestPointreturnsIsInside = falsefor a point over a character:What is the updated/expected behavior with this PR?
IsInsideistruefor a point over a character on any line, whatever the text alignment.How was the solution implemented (if it's not obvious)?
GetHitTestResultcompared a point relative to the whole layout against the size of a single line.HitTestPointnow translates the point into the hit line's coordinates (relative to the line's top and itsTextLine.Start) before passing it toGetHitTestResult.The commits come in test/fix pairs: test and fix for lines after the first, a refactor to use line-relative points, then test and fix for aligned lines.
Checklist
Breaking changes
None
Obsoletions / Deprecations
None
Fixed issues
I didn't find any issues relating to this.