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 ...@@ -59,6 +59,31 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur
final _SelectableTextState _state; 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 @override
void onForcePressStart(ForcePressDetails details) { void onForcePressStart(ForcePressDetails details) {
super.onForcePressStart(details); super.onForcePressStart(details);
...@@ -72,15 +97,37 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur ...@@ -72,15 +97,37 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur
// Not required. // 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 @override
void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) { void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
if (delegate.selectionEnabled) { if (!delegate.selectionEnabled) {
renderEditable.selectWordsInRange( return;
from: details.globalPosition - details.offsetFromOrigin,
to: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
} }
// 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 @override
...@@ -100,14 +147,6 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur ...@@ -100,14 +147,6 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur
} }
_state.widget.onTap?.call(); _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. /// A run of selectable text with a single style.
......
...@@ -2131,6 +2131,14 @@ class TextSelectionGestureDetectorBuilder { ...@@ -2131,6 +2131,14 @@ class TextSelectionGestureDetectorBuilder {
: scrollableState.position.pixels; : 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 // 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 // tap. Mac uses this value to reset to the original selection when an
// inversion of the base and offset happens. // inversion of the base and offset happens.
...@@ -2498,9 +2506,11 @@ class TextSelectionGestureDetectorBuilder { ...@@ -2498,9 +2506,11 @@ class TextSelectionGestureDetectorBuilder {
final Offset editableOffset = renderEditable.maxLines == 1 final Offset editableOffset = renderEditable.maxLines == 1
? Offset(renderEditable.offset.pixels - _dragStartViewportOffset, 0.0) ? Offset(renderEditable.offset.pixels - _dragStartViewportOffset, 0.0)
: Offset(0.0, renderEditable.offset.pixels - _dragStartViewportOffset); : 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( final Offset scrollableOffset = Offset(
0.0, !scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
_scrollPosition - _dragStartScrollOffset, scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
); );
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
...@@ -2839,9 +2849,11 @@ class TextSelectionGestureDetectorBuilder { ...@@ -2839,9 +2849,11 @@ class TextSelectionGestureDetectorBuilder {
final Offset editableOffset = renderEditable.maxLines == 1 final Offset editableOffset = renderEditable.maxLines == 1
? Offset(renderEditable.offset.pixels - _dragStartViewportOffset, 0.0) ? Offset(renderEditable.offset.pixels - _dragStartViewportOffset, 0.0)
: Offset(0.0, renderEditable.offset.pixels - _dragStartViewportOffset); : 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( final Offset scrollableOffset = Offset(
0.0, !scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
_scrollPosition - _dragStartScrollOffset, scrollingOnVerticalAxis ? effectiveScrollPosition : 0.0,
); );
final Offset dragStartGlobalPosition = details.globalPosition - details.offsetFromOrigin; 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