Unverified Commit eba38c4b authored by Renzo Olivares's avatar Renzo Olivares Committed by GitHub

Fix text selection edge scrolling when inside a horizontal scrollable (#140250)

Fixes #129590

* Consider `AxisDirection` when calculating scroll offset used in determining TextSelection during a drag/long press drag. Previously it seems that we were assuming the direction was always vertical https://github.com/flutter/flutter/blob/30cc83198544582b858e48c7bb9d761ecdb3d944/packages/flutter/lib/src/widgets/text_selection.dart#L2842-L2844 .
* SelectableText now considers RenderEditable offset changes and Scrollable offset changes when calculating the TextSelection during a long press drag.
parent d83eff44
......@@ -59,6 +59,31 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur
final _SelectableTextState _state;
/// The viewport offset pixels of any [Scrollable] containing the
/// [RenderEditable] at the last drag start.
double _dragStartScrollOffset = 0.0;
/// The viewport offset pixels of the [RenderEditable] at the last drag start.
double _dragStartViewportOffset = 0.0;
double get _scrollPosition {
final ScrollableState? scrollableState =
delegate.editableTextKey.currentContext == null
? null
: Scrollable.maybeOf(delegate.editableTextKey.currentContext!);
return scrollableState == null
? 0.0
: scrollableState.position.pixels;
}
AxisDirection? get _scrollDirection {
final ScrollableState? scrollableState =
delegate.editableTextKey.currentContext == null
? null
: Scrollable.maybeOf(delegate.editableTextKey.currentContext!);
return scrollableState?.axisDirection;
}
@override
void onForcePressStart(ForcePressDetails details) {
super.onForcePressStart(details);
......@@ -72,15 +97,37 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur
// Not required.
}
@override
void onSingleLongTapStart(LongPressStartDetails details) {
if (!delegate.selectionEnabled) {
return;
}
renderEditable.selectWord(cause: SelectionChangedCause.longPress);
Feedback.forLongPress(_state.context);
_dragStartViewportOffset = renderEditable.offset.pixels;
_dragStartScrollOffset = _scrollPosition;
}
@override
void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
if (delegate.selectionEnabled) {
renderEditable.selectWordsInRange(
from: details.globalPosition - details.offsetFromOrigin,
to: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
if (!delegate.selectionEnabled) {
return;
}
// Adjust the drag start offset for possible viewport offset changes.
final Offset editableOffset = renderEditable.maxLines == 1
? Offset(renderEditable.offset.pixels - _dragStartViewportOffset, 0.0)
: Offset(0.0, renderEditable.offset.pixels - _dragStartViewportOffset);
final double effectiveScrollPosition = _scrollPosition - _dragStartScrollOffset;
final bool scrollingOnVerticalAxis = _scrollDirection == AxisDirection.up || _scrollDirection == AxisDirection.down;
final Offset scrollableOffset = Offset(
!scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
);
renderEditable.selectWordsInRange(
from: details.globalPosition - details.offsetFromOrigin - editableOffset - scrollableOffset,
to: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
}
@override
......@@ -100,14 +147,6 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur
}
_state.widget.onTap?.call();
}
@override
void onSingleLongTapStart(LongPressStartDetails details) {
if (delegate.selectionEnabled) {
renderEditable.selectWord(cause: SelectionChangedCause.longPress);
Feedback.forLongPress(_state.context);
}
}
}
/// A run of selectable text with a single style.
......
......@@ -2131,6 +2131,14 @@ class TextSelectionGestureDetectorBuilder {
: scrollableState.position.pixels;
}
AxisDirection? get _scrollDirection {
final ScrollableState? scrollableState =
delegate.editableTextKey.currentContext == null
? null
: Scrollable.maybeOf(delegate.editableTextKey.currentContext!);
return scrollableState?.axisDirection;
}
// For a shift + tap + drag gesture, the TextSelection at the point of the
// tap. Mac uses this value to reset to the original selection when an
// inversion of the base and offset happens.
......@@ -2498,9 +2506,11 @@ class TextSelectionGestureDetectorBuilder {
final Offset editableOffset = renderEditable.maxLines == 1
? Offset(renderEditable.offset.pixels - _dragStartViewportOffset, 0.0)
: Offset(0.0, renderEditable.offset.pixels - _dragStartViewportOffset);
final double effectiveScrollPosition = _scrollPosition - _dragStartScrollOffset;
final bool scrollingOnVerticalAxis = _scrollDirection == AxisDirection.up || _scrollDirection == AxisDirection.down;
final Offset scrollableOffset = Offset(
0.0,
_scrollPosition - _dragStartScrollOffset,
!scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
);
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
......@@ -2839,9 +2849,11 @@ class TextSelectionGestureDetectorBuilder {
final Offset editableOffset = renderEditable.maxLines == 1
? Offset(renderEditable.offset.pixels - _dragStartViewportOffset, 0.0)
: Offset(0.0, renderEditable.offset.pixels - _dragStartViewportOffset);
final double effectiveScrollPosition = _scrollPosition - _dragStartScrollOffset;
final bool scrollingOnVerticalAxis = _scrollDirection == AxisDirection.up || _scrollDirection == AxisDirection.down;
final Offset scrollableOffset = Offset(
0.0,
_scrollPosition - _dragStartScrollOffset,
!scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
);
final Offset dragStartGlobalPosition = details.globalPosition - details.offsetFromOrigin;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment