diff --git a/lib/web_ui/lib/src/engine/text/layout_service.dart b/lib/web_ui/lib/src/engine/text/layout_service.dart index cbaf0456ef764..3273c85f47556 100644 --- a/lib/web_ui/lib/src/engine/text/layout_service.dart +++ b/lib/web_ui/lib/src/engine/text/layout_service.dart @@ -420,8 +420,11 @@ class TextLayoutService { } ui.GlyphInfo? getClosestGlyphInfo(ui.Offset offset) { - final LayoutFragment? fragment = _findLineForY(offset.dy) - ?.closestFragmentAtOffset(offset.dx); + final ParagraphLine? line = _findLineForY(offset.dy); + if (line == null) { + return null; + } + final LayoutFragment? fragment = line.closestFragmentAtOffset(offset.dx - line.left); if (fragment == null) { return null; } @@ -431,8 +434,8 @@ class TextLayoutService { || fragment.line.left + fragment.line.width <= dx || switch (fragment.textDirection!) { // If dx is closer to the trailing edge, no need to check other fragments. - ui.TextDirection.ltr => dx >= (fragment.left + fragment.right) / 2, - ui.TextDirection.rtl => dx <= (fragment.left + fragment.right) / 2, + ui.TextDirection.ltr => dx >= line.left + (fragment.left + fragment.right) / 2, + ui.TextDirection.rtl => dx <= line.left + (fragment.left + fragment.right) / 2, }; final ui.GlyphInfo candidate1 = fragment.getClosestCharacterBox(dx); if (closestGraphemeStartInFragment) { diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index c14bbe01deb7b..49a34f14c4dac 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -380,7 +380,7 @@ class ParagraphLine { final double? minDistance = closestFragment?.distance; if (minDistance == null || minDistance > distance) { - closestFragment = (fragment: fragment, distance: distance); + closestFragment = (fragment: fragment, distance: distance); } } return closestFragment?.fragment; diff --git a/lib/web_ui/test/html/text_test.dart b/lib/web_ui/test/html/text_test.dart index 7d4303188bed0..d7db0eb7707c0 100644 --- a/lib/web_ui/test/html/text_test.dart +++ b/lib/web_ui/test/html/text_test.dart @@ -155,6 +155,29 @@ Future testMain() async { expect(bottomRight?.writingDirection, TextDirection.ltr); }); + test('Basic glyph metrics - hit test - center aligned text in separate fragments', () { + const double fontSize = 10.0; + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( + fontSize: fontSize, + textAlign: TextAlign.center, + fontFamily: 'FlutterTest', + ))..addText('12345\n') + ..addText('1') + ..addText('2') + ..addText('3'); + final Paragraph paragraph = builder.build(); + paragraph.layout(const ParagraphConstraints(width: 50)); + + final GlyphInfo? bottomCenter = paragraph.getClosestGlyphInfoForOffset(const Offset(25.0, 99.0)); + final GlyphInfo? expected = paragraph.getGlyphInfoAt(7); + expect(bottomCenter, equals(expected)); + expect(bottomCenter, isNot(paragraph.getGlyphInfoAt(8))); + + expect(bottomCenter?.graphemeClusterLayoutBounds, const Rect.fromLTWH(20, 10, 10, 10)); + expect(bottomCenter?.graphemeClusterCodeUnitRange, const TextRange(start: 7, end: 8)); + expect(bottomCenter?.writingDirection, TextDirection.ltr); + }); + test('Glyph metrics with grapheme split into different runs', () { const double fontSize = 10; final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(