New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix TextField
horizontal drag conflicts
#147341
Changes from all commits
1342996
e94fa28
9bffd82
918b061
5b6f1e8
1fe71f9
821a809
2f85589
3b71a60
d253a1f
4d363af
8895ffa
d49da90
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1898,6 +1898,11 @@ class _SelectionHandleOverlayState extends State<_SelectionHandleOverlay> with S | |
math.max((interactiveRect.height - handleRect.height) / 2, 0), | ||
); | ||
|
||
// Make sure a drag is eagerly accepted. This is used on iOS to match the | ||
// behavior where a drag directly on a collapse handle will always win against | ||
// other drag gestures. | ||
final bool eagerlyAcceptDragWhenCollapsed = widget.type == TextSelectionHandleType.collapsed && defaultTargetPlatform == TargetPlatform.iOS; | ||
|
||
return CompositedTransformFollower( | ||
link: widget.handleLayerLink, | ||
offset: interactiveRect.topLeft, | ||
|
@@ -1924,6 +1929,7 @@ class _SelectionHandleOverlayState extends State<_SelectionHandleOverlay> with S | |
(PanGestureRecognizer instance) { | ||
instance | ||
..dragStartBehavior = widget.dragStartBehavior | ||
..gestureSettings = eagerlyAcceptDragWhenCollapsed ? const DeviceGestureSettings(touchSlop: 1.0) : null | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why 1.0? Could it be competing-slop-value minus one, or is 1.0 better? (I know you said you're still thinking about cleaning this up.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the idea of competing-slop-value minus one, or maybe minus something higher than 1 to be sure that it always wins. |
||
..onStart = widget.onSelectionHandleDragStart | ||
..onUpdate = widget.onSelectionHandleDragUpdate | ||
..onEnd = widget.onSelectionHandleDragEnd; | ||
|
@@ -2081,18 +2087,6 @@ class TextSelectionGestureDetectorBuilder { | |
&& selection.end >= textPosition.offset; | ||
} | ||
|
||
/// Returns true if position was on selection. | ||
bool _positionOnSelection(Offset position, TextSelection? targetSelection) { | ||
if (targetSelection == null) { | ||
return false; | ||
} | ||
|
||
final TextPosition textPosition = renderEditable.getPositionForPoint(position); | ||
|
||
return targetSelection.start <= textPosition.offset | ||
&& targetSelection.end >= textPosition.offset; | ||
} | ||
|
||
// Expand the selection to the given global position. | ||
// | ||
// Either base or extent will be moved to the last tapped position, whichever | ||
|
@@ -2203,15 +2197,6 @@ class TextSelectionGestureDetectorBuilder { | |
// inversion of the base and offset happens. | ||
TextSelection? _dragStartSelection; | ||
|
||
// For tap + drag gesture on iOS, whether the position where the drag started | ||
// was on the previous TextSelection. iOS uses this value to determine if | ||
// the cursor should move on drag update. | ||
// | ||
// If the drag started on the previous selection then the cursor will move on | ||
// drag update. If the drag did not start on the previous selection then the | ||
// cursor will not move on drag update. | ||
bool? _dragBeganOnPreviousSelection; | ||
|
||
// For iOS long press behavior when the field is not focused. iOS uses this value | ||
// to determine if a long press began on a field that was not focused. | ||
// | ||
|
@@ -2807,7 +2792,6 @@ class TextSelectionGestureDetectorBuilder { | |
_dragStartSelection = renderEditable.selection; | ||
_dragStartScrollOffset = _scrollPosition; | ||
_dragStartViewportOffset = renderEditable.offset.pixels; | ||
_dragBeganOnPreviousSelection = _positionOnSelection(details.globalPosition, _dragStartSelection); | ||
|
||
if (_TextSelectionGestureDetectorState._getEffectiveConsecutiveTapCount(details.consecutiveTapCount) > 1) { | ||
// Do not set the selection on a consecutive tap and drag. | ||
|
@@ -2839,16 +2823,6 @@ class TextSelectionGestureDetectorBuilder { | |
case PointerDeviceKind.invertedStylus: | ||
case PointerDeviceKind.touch: | ||
case PointerDeviceKind.unknown: | ||
// For iOS platforms, a touch drag does not initiate unless the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is this removed logic covered now? I guess the drag listener on the selection handle? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah the drag listener on the selection handle already has this logic by default. |
||
// editable has focus and the drag began on the previous selection. | ||
assert(_dragBeganOnPreviousSelection != null); | ||
if (renderEditable.hasFocus && _dragBeganOnPreviousSelection!) { | ||
renderEditable.selectPositionAt( | ||
from: details.globalPosition, | ||
cause: SelectionChangedCause.drag, | ||
); | ||
_showMagnifierIfSupportedByPlatform(details.globalPosition); | ||
} | ||
case null: | ||
} | ||
case TargetPlatform.android: | ||
|
@@ -2977,12 +2951,10 @@ class TextSelectionGestureDetectorBuilder { | |
|
||
switch (defaultTargetPlatform) { | ||
case TargetPlatform.iOS: | ||
// With a touch device, nothing should happen, unless there was a double tap, or | ||
// there was a collapsed selection, and the tap/drag position is at the collapsed selection. | ||
// In that case the caret should move with the drag position. | ||
// | ||
// With a mouse device, a drag should select the range from the origin of the drag | ||
// to the current position of the drag. | ||
// | ||
// With a touch device, nothing should happen. | ||
switch (details.kind) { | ||
case PointerDeviceKind.mouse: | ||
case PointerDeviceKind.trackpad: | ||
|
@@ -2995,17 +2967,6 @@ class TextSelectionGestureDetectorBuilder { | |
case PointerDeviceKind.invertedStylus: | ||
case PointerDeviceKind.touch: | ||
case PointerDeviceKind.unknown: | ||
assert(_dragBeganOnPreviousSelection != null); | ||
if (renderEditable.hasFocus | ||
&& _dragStartSelection!.isCollapsed | ||
&& _dragBeganOnPreviousSelection! | ||
) { | ||
renderEditable.selectPositionAt( | ||
from: details.globalPosition, | ||
cause: SelectionChangedCause.drag, | ||
); | ||
return _showMagnifierIfSupportedByPlatform(details.globalPosition); | ||
} | ||
case null: | ||
break; | ||
} | ||
|
@@ -3102,8 +3063,6 @@ class TextSelectionGestureDetectorBuilder { | |
/// callback. | ||
@protected | ||
void onDragSelectionEnd(TapDragEndDetails details) { | ||
_dragBeganOnPreviousSelection = null; | ||
|
||
if (_shouldShowSelectionToolbar && _TextSelectionGestureDetectorState._getEffectiveConsecutiveTapCount(details.consecutiveTapCount) == 2) { | ||
editableText.showToolbar(); | ||
} | ||
|
@@ -3439,6 +3398,7 @@ class _TextSelectionGestureDetectorState extends State<TextSelectionGestureDetec | |
// Text selection should start from the position of the first pointer | ||
// down event. | ||
..dragStartBehavior = DragStartBehavior.down | ||
..eagerVictoryOnDrag = defaultTargetPlatform != TargetPlatform.iOS | ||
..onTapTrackStart = _handleTapTrackStart | ||
..onTapTrackReset = _handleTapTrackReset | ||
..onTapDown = _handleTapDown | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this not set in the previous
if
too?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because in the case of
eagerVictoryOnDrag == true
, it is set immediately when the drag is detected.