@@ -637,7 +637,13 @@ class TextSelectionOverlay {
637
637
);
638
638
}
639
639
640
- late Offset _dragEndPosition;
640
+ // The contact position of the gesture at the current end handle location.
641
+ // Updated when the handle moves.
642
+ late double _endHandleDragPosition;
643
+
644
+ // The distance from _endHandleDragPosition to the center of the line that it
645
+ // corresponds to.
646
+ late double _endHandleDragPositionToCenterOfLine;
641
647
642
648
void _handleSelectionEndHandleDragStart (DragStartDetails details) {
643
649
if (! renderObject.attached) {
@@ -646,10 +652,17 @@ class TextSelectionOverlay {
646
652
647
653
// This adjusts for the fact that the selection handles may not
648
654
// perfectly cover the TextPosition that they correspond to.
649
- final Offset offsetFromHandleToTextPosition = _getOffsetToTextPositionPoint (_selectionOverlay.endHandleType);
650
- _dragEndPosition = details.globalPosition + offsetFromHandleToTextPosition;
651
-
652
- final TextPosition position = renderObject.getPositionForPoint (_dragEndPosition);
655
+ _endHandleDragPosition = details.globalPosition.dy;
656
+ final Offset endPoint =
657
+ renderObject.localToGlobal (_selectionOverlay.selectionEndpoints.last.point);
658
+ final double centerOfLine = endPoint.dy - renderObject.preferredLineHeight / 2 ;
659
+ _endHandleDragPositionToCenterOfLine = centerOfLine - _endHandleDragPosition;
660
+ final TextPosition position = renderObject.getPositionForPoint (
661
+ Offset (
662
+ details.globalPosition.dx,
663
+ centerOfLine,
664
+ ),
665
+ );
653
666
654
667
_selectionOverlay.showMagnifier (
655
668
_buildMagnifier (
@@ -660,14 +673,33 @@ class TextSelectionOverlay {
660
673
);
661
674
}
662
675
676
+ /// Given a handle position and drag position, returns the position of handle
677
+ /// after the drag.
678
+ ///
679
+ /// The handle jumps instantly between lines when the drag reaches a full
680
+ /// line's height away from the original handle position. In other words, the
681
+ /// line jump happens when the contact point would be located at the same
682
+ /// place on the handle at the new line as when the gesture started.
683
+ double _getHandleDy (double dragDy, double handleDy) {
684
+ final double distanceDragged = dragDy - handleDy;
685
+ final int dragDirection = distanceDragged < 0.0 ? - 1 : 1 ;
686
+ final int linesDragged =
687
+ dragDirection * (distanceDragged.abs () / renderObject.preferredLineHeight).floor ();
688
+ return handleDy + linesDragged * renderObject.preferredLineHeight;
689
+ }
690
+
663
691
void _handleSelectionEndHandleDragUpdate (DragUpdateDetails details) {
664
692
if (! renderObject.attached) {
665
693
return ;
666
694
}
667
- _dragEndPosition += details.delta;
668
695
669
- final TextPosition position = renderObject.getPositionForPoint (_dragEndPosition);
670
- final TextSelection currentSelection = TextSelection .fromPosition (position);
696
+ _endHandleDragPosition = _getHandleDy (details.globalPosition.dy, _endHandleDragPosition);
697
+ final Offset adjustedOffset = Offset (
698
+ details.globalPosition.dx,
699
+ _endHandleDragPosition + _endHandleDragPositionToCenterOfLine,
700
+ );
701
+
702
+ final TextPosition position = renderObject.getPositionForPoint (adjustedOffset);
671
703
672
704
if (_selection.isCollapsed) {
673
705
_selectionOverlay.updateMagnifier (_buildMagnifier (
@@ -676,6 +708,7 @@ class TextSelectionOverlay {
676
708
renderEditable: renderObject,
677
709
));
678
710
711
+ final TextSelection currentSelection = TextSelection .fromPosition (position);
679
712
_handleSelectionHandleChanged (currentSelection, isEnd: true );
680
713
return ;
681
714
}
@@ -716,7 +749,13 @@ class TextSelectionOverlay {
716
749
));
717
750
}
718
751
719
- late Offset _dragStartPosition;
752
+ // The contact position of the gesture at the current start handle location.
753
+ // Updated when the handle moves.
754
+ late double _startHandleDragPosition;
755
+
756
+ // The distance from _startHandleDragPosition to the center of the line that
757
+ // it corresponds to.
758
+ late double _startHandleDragPositionToCenterOfLine;
720
759
721
760
void _handleSelectionStartHandleDragStart (DragStartDetails details) {
722
761
if (! renderObject.attached) {
@@ -725,10 +764,17 @@ class TextSelectionOverlay {
725
764
726
765
// This adjusts for the fact that the selection handles may not
727
766
// perfectly cover the TextPosition that they correspond to.
728
- final Offset offsetFromHandleToTextPosition = _getOffsetToTextPositionPoint (_selectionOverlay.startHandleType);
729
- _dragStartPosition = details.globalPosition + offsetFromHandleToTextPosition;
730
-
731
- final TextPosition position = renderObject.getPositionForPoint (_dragStartPosition);
767
+ _startHandleDragPosition = details.globalPosition.dy;
768
+ final Offset startPoint =
769
+ renderObject.localToGlobal (_selectionOverlay.selectionEndpoints.first.point);
770
+ final double centerOfLine = startPoint.dy - renderObject.preferredLineHeight / 2 ;
771
+ _startHandleDragPositionToCenterOfLine = centerOfLine - _startHandleDragPosition;
772
+ final TextPosition position = renderObject.getPositionForPoint (
773
+ Offset (
774
+ details.globalPosition.dx,
775
+ centerOfLine,
776
+ ),
777
+ );
732
778
733
779
_selectionOverlay.showMagnifier (
734
780
_buildMagnifier (
@@ -743,8 +789,13 @@ class TextSelectionOverlay {
743
789
if (! renderObject.attached) {
744
790
return ;
745
791
}
746
- _dragStartPosition += details.delta;
747
- final TextPosition position = renderObject.getPositionForPoint (_dragStartPosition);
792
+
793
+ _startHandleDragPosition = _getHandleDy (details.globalPosition.dy, _startHandleDragPosition);
794
+ final Offset adjustedOffset = Offset (
795
+ details.globalPosition.dx,
796
+ _startHandleDragPosition + _startHandleDragPositionToCenterOfLine,
797
+ );
798
+ final TextPosition position = renderObject.getPositionForPoint (adjustedOffset);
748
799
749
800
if (_selection.isCollapsed) {
750
801
_selectionOverlay.updateMagnifier (_buildMagnifier (
@@ -753,7 +804,8 @@ class TextSelectionOverlay {
753
804
renderEditable: renderObject,
754
805
));
755
806
756
- _handleSelectionHandleChanged (TextSelection .fromPosition (position), isEnd: false );
807
+ final TextSelection currentSelection = TextSelection .fromPosition (position);
808
+ _handleSelectionHandleChanged (currentSelection, isEnd: false );
757
809
return ;
758
810
}
759
811
@@ -813,32 +865,6 @@ class TextSelectionOverlay {
813
865
}
814
866
}
815
867
816
- // Returns the offset that locates a drag on a handle to the correct line of text.
817
- Offset _getOffsetToTextPositionPoint (TextSelectionHandleType type) {
818
- final Size handleSize = selectionControls! .getHandleSize (
819
- renderObject.preferredLineHeight,
820
- );
821
-
822
- // Try to shift center of handle to top by half of handle height.
823
- final double halfHandleHeight = handleSize.height / 2 ;
824
-
825
- // [getHandleAnchor] is used to shift the selection endpoint to the top left
826
- // point of the handle rect when building the handle widget.
827
- // The endpoint is at the bottom of the selection rect, which is also at the
828
- // bottom of the line of text.
829
- // Try to shift the top of the handle to the selection endpoint by the dy of
830
- // the handle's anchor.
831
- final double handleAnchorDy = selectionControls! .getHandleAnchor (type, renderObject.preferredLineHeight).dy;
832
-
833
- // Try to shift the selection endpoint to the center of the correct line by
834
- // using half of the line height.
835
- final double halfPreferredLineHeight = renderObject.preferredLineHeight / 2 ;
836
-
837
- // The x offset is accurate, so we only need to adjust the y position.
838
- final double offsetYFromHandleToTextPosition = handleAnchorDy - halfHandleHeight - halfPreferredLineHeight;
839
- return Offset (0.0 , offsetYFromHandleToTextPosition);
840
- }
841
-
842
868
void _handleSelectionHandleChanged (TextSelection newSelection, {required bool isEnd}) {
843
869
final TextPosition textPosition = isEnd ? newSelection.extent : newSelection.base ;
844
870
selectionDelegate.userUpdateTextEditingValue (
0 commit comments