@@ -491,101 +491,41 @@ class ClickableRenderParagraph extends RenderBox
491
491
@override
492
492
@protected
493
493
bool hitTestChildren (BoxHitTestResult result, {required Offset position}) {
494
- final children = _getClickableChildren ();
495
- print ('${this .size } => $children ' );
496
- InlineSpan ? midSpanHit = _getHitTarget (position);
497
- if (midSpanHit is HitTestTarget ) {
498
- print ('hit: $midSpanHit ' );
499
- result.add (HitTestEntry (midSpanHit as HitTestTarget ));
494
+ final preferredLineHeight = _textPainter.preferredLineHeight;
495
+ int lines = position.dy ~ / preferredLineHeight;
496
+ final normalizedDy = lines * preferredLineHeight + (preferredLineHeight / 2 );
497
+ final normalizedPosition = Offset (position.dx, normalizedDy);
498
+ final GlyphInfo ? glyph = _textPainter.getClosestGlyphForOffset (normalizedPosition);
499
+ // The hit-test can't fall through the horizontal gaps between visually
500
+ // adjacent characters on the same line, even with a large letter-spacing or
501
+ // text justification, as graphemeClusterLayoutBounds.width is the advance
502
+ // width to the next character, so there's no gap between their
503
+ // graphemeClusterLayoutBounds rects.
504
+ final InlineSpan ? spanHit = glyph != null && glyph.graphemeClusterLayoutBounds.contains (normalizedPosition)
505
+ ? _textPainter.text! .getSpanForPosition (TextPosition (offset: glyph.graphemeClusterCodeUnitRange.start))
506
+ : null ;
507
+ if (spanHit is HitTestTarget ) {
508
+ result.add (HitTestEntry (spanHit as HitTestTarget ));
500
509
return true ;
501
510
}
502
-
503
- if (hitTestInlineChildren (result, position)) {
504
- print ('testling inline' );
511
+ bool hit = hitTestInlineChildren (result, position);
512
+ if (hit && wasHit (result)) {
505
513
return true ;
506
514
}
507
515
508
- return false ;
509
- }
510
-
511
- List <ClickableRenderParagraph > _getClickableChildren () {
512
- return _getChildren (this , '' );
513
- }
514
-
515
- List <ClickableRenderParagraph > _getChildren (ContainerRenderObjectMixin parent, String indent) {
516
- print ('${indent }#childCount: ${parent .childCount }' );
517
- final children = < ClickableRenderParagraph > [];
518
- if (parent.firstChild == null ) return children;
519
- var child = parent.firstChild;
520
- while (child != null ) {
521
- children.addAll (_printChild (child, '$indent \t ' ));
522
- child = parent.childAfter (child);
523
- }
524
- return children;
525
- }
526
-
527
- List <ClickableRenderParagraph > _printChild (RenderObject child, String indent) {
528
- if (child is ClickableRenderParagraph ) {
529
- print ('$indent size: ${child .size } -> ${child .parentData }' );
530
- if (child.childCount == 0 ) {
531
- return [child];
532
- }
533
- }
534
- final children = < ClickableRenderParagraph > [];
535
-
536
- final parentData = child.parentData;
537
- print ('${indent } ${child .runtimeType } -> PARENT: $parentData ' );
538
- if (child is ContainerRenderObjectMixin ) {
539
- print ('${indent }child count: ${child .childCount }' );
540
- children.addAll (_getChildren (child, '$indent \t ' ));
541
- } else if (child is RenderProxyBox ) {
542
- children.addAll (_printChild (child.child! , '$indent \t ' ));
543
- } else if (child is RenderObjectWithChildMixin ) {
544
- children.addAll (_printChild (child.child! , '$indent \t ' ));
545
- } else {
546
- print ('$indent #############' );
516
+ if (preferredLineHeight >= 24 ) return false ;
517
+ final hitTryOffset = math.min (24 - _textPainter.preferredLineHeight, preferredLineHeight / 2 );
518
+ final dec = Offset (position.dx, position.dy + hitTryOffset);
519
+ hit = (hitTestInlineChildren (result, dec));
520
+ if (hit && result.path.any ((entry) => entry is TextSpan )) {
521
+ return true ;
547
522
}
548
- return children;
549
- }
550
523
551
- InlineSpan ? _getHitTarget (Offset position) {
552
- final GlyphInfo ? glyph = _textPainter.getClosestGlyphForOffset (position);
553
- if (glyph != null ) {
554
- var bounds = glyph.graphemeClusterLayoutBounds;
555
- final padding = (30 - bounds.height) / 2 ;
556
- if (padding > 0 ) {
557
- bounds = Rect .fromLTRB (bounds.left, bounds.top - padding, bounds.right, bounds.bottom + padding);
558
- }
559
- final InlineSpan ? spanHit = bounds.contains (position) ? text.getSpanForPosition (TextPosition (offset: glyph.graphemeClusterCodeUnitRange.start)) : null ;
560
- if (spanHit is HitTestTarget ) {
561
- return spanHit;
562
- }
563
- }
564
- return null ;
524
+ final inc = Offset (position.dx, position.dy - hitTryOffset);
525
+ return hitTestInlineChildren (result, inc);
565
526
}
566
527
567
- // bool _hitTestInlineChildren(BoxHitTestResult result, Offset position) {
568
- // RenderBox? child = firstChild;
569
- // print('testing child: $child ($position) => ${parent.runtimeType}');
570
- // while (child != null) {
571
- // final TextParentData childParentData = child.parentData! as TextParentData;
572
- // print('parentData: $childParentData');
573
- // final Offset? childOffset = childParentData.offset;
574
- // if (childOffset == null) {
575
- // return false;
576
- // }
577
- // final bool isHit = result.addWithPaintOffset(
578
- // offset: childOffset,
579
- // position: position,
580
- // hitTest: (BoxHitTestResult result, Offset transformed) => child!.hitTest(result, position: transformed),
581
- // );
582
- // if (isHit) {
583
- // return true;
584
- // }
585
- // child = childAfter(child);
586
- // }
587
- // return false;
588
- // }
528
+ bool wasHit (HitTestResult result) => result.path.any ((entry) => entry.target is TextSpan );
589
529
590
530
bool _needsClipping = false ;
591
531
ui.Shader ? _overflowShader;
@@ -603,11 +543,11 @@ class ClickableRenderParagraph extends RenderBox
603
543
_textPainter.markNeedsLayout ();
604
544
}
605
545
606
- // Placeholder dimensions representing the sizes of child inline widgets.
607
- //
608
- // These need to be cached because the text painter's placeholder dimensions
609
- // will be overwritten during intrinsic width/height calculations and must be
610
- // restored to the original values before final layout and painting.
546
+ // Placeholder dimensions representing the sizes of child inline widgets.
547
+ //
548
+ // These need to be cached because the text painter's placeholder dimensions
549
+ // will be overwritten during intrinsic width/height calculations and must be
550
+ // restored to the original values before final layout and painting.
611
551
List <PlaceholderDimensions >? _placeholderDimensions;
612
552
613
553
double _adjustMaxWidth (double maxWidth) {
@@ -991,10 +931,10 @@ class ClickableRenderParagraph extends RenderBox
991
931
..attributedLabel = attributedLabel;
992
932
}
993
933
994
- // Caches [SemanticsNode]s created during [assembleSemanticsNode] so they
995
- // can be re-used when [assembleSemanticsNode] is called again. This ensures
996
- // stable ids for the [SemanticsNode]s of [TextSpan]s across
997
- // [assembleSemanticsNode] invocations.
934
+ // Caches [SemanticsNode]s created during [assembleSemanticsNode] so they
935
+ // can be re-used when [assembleSemanticsNode] is called again. This ensures
936
+ // stable ids for the [SemanticsNode]s of [TextSpan]s across
937
+ // [assembleSemanticsNode] invocations.
998
938
LinkedHashMap <Key , SemanticsNode >? _cachedChildNodes;
999
939
1000
940
@override
@@ -2971,3 +2911,7 @@ class _SelectableFragment with Selectable, Diagnosticable, ChangeNotifier implem
2971
2911
properties.add (DiagnosticsProperty <String >('fullText' , fullText));
2972
2912
}
2973
2913
}
2914
+
2915
+ class ExtraOffset extends Offset {
2916
+ ExtraOffset (super .dx, super .dy);
2917
+ }
0 commit comments