Correct text selection pivot points (#71756)
diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index f162a7a..ce888fd 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart
@@ -678,6 +678,8 @@ newSelection = newSelection.copyWith(extentOffset: textSelection.extentOffset); } } else { + // The directional arrows move the TextSelection.extentOffset, while the + // base remains fixed. if (rightArrow && newSelection.extentOffset < _plainText.length) { int nextExtent; if (!shift && !wordModifier && !lineModifier && newSelection.start != newSelection.end) { @@ -1849,6 +1851,9 @@ } /// Select text between the global positions [from] and [to]. + /// + /// [from] corresponds to the [TextSelection.baseOffset], and [to] corresponds + /// to the [TextSelection.extentOffset]. void selectPositionAt({ required Offset from, Offset? to, required SelectionChangedCause cause }) { assert(cause != null); assert(from != null); @@ -1861,12 +1866,8 @@ ? null : _textPainter.getPositionForOffset(globalToLocal(to - _paintOffset)); - int baseOffset = fromPosition.offset; - int extentOffset = fromPosition.offset; - if (toPosition != null) { - baseOffset = math.min(fromPosition.offset, toPosition.offset); - extentOffset = math.max(fromPosition.offset, toPosition.offset); - } + final int baseOffset = fromPosition.offset; + final int extentOffset = toPosition?.offset ?? fromPosition.offset; final TextSelection newSelection = TextSelection( baseOffset: baseOffset,
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 917b6e5..6e9f924 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart
@@ -1458,8 +1458,8 @@ await gesture.up(); await tester.pumpAndSettle(); - expect(controller.selection.baseOffset, testValue.indexOf('e')); - expect(controller.selection.extentOffset, testValue.indexOf('g')); + expect(controller.selection.baseOffset, testValue.indexOf('g')); + expect(controller.selection.extentOffset, testValue.indexOf('e')); }); testWidgets('Slow mouse dragging also selects text', (WidgetTester tester) async {
diff --git a/packages/flutter/test/rendering/editable_test.dart b/packages/flutter/test/rendering/editable_test.dart index 742fed2..748c97b 100644 --- a/packages/flutter/test/rendering/editable_test.dart +++ b/packages/flutter/test/rendering/editable_test.dart
@@ -570,8 +570,8 @@ pumpFrame(); expect(currentSelection.isCollapsed, isFalse); - expect(currentSelection.baseOffset, 1); - expect(currentSelection.extentOffset, 3); + expect(currentSelection.baseOffset, 3); + expect(currentSelection.extentOffset, 1); }); test('selection does not flicker as user is dragging', () { @@ -1006,7 +1006,79 @@ await simulateKeyUpEvent(LogicalKeyboardKey.arrowLeft); expect(currentSelection.isCollapsed, true); expect(currentSelection.baseOffset, 2); + }, skip: isBrowser); // https://github.com/flutter/flutter/issues/58068 + test('arrow keys with selection text and shift', () async { + final TextSelectionDelegate delegate = FakeEditableTextState(); + final ViewportOffset viewportOffset = ViewportOffset.zero(); + late TextSelection currentSelection; + final RenderEditable editable = RenderEditable( + backgroundCursorColor: Colors.grey, + selectionColor: Colors.black, + textDirection: TextDirection.ltr, + cursorColor: Colors.red, + offset: viewportOffset, + textSelectionDelegate: delegate, + onSelectionChanged: (TextSelection selection, RenderEditable renderObject, SelectionChangedCause cause) { + currentSelection = selection; + }, + startHandleLayerLink: LayerLink(), + endHandleLayerLink: LayerLink(), + text: const TextSpan( + text: '012345', // Thumbs up + style: TextStyle(height: 1.0, fontSize: 10.0, fontFamily: 'Ahem'), + ), + selection: const TextSelection.collapsed( + offset: 0, + ), + ); + + layout(editable); + editable.hasFocus = true; + + editable.selection = const TextSelection(baseOffset: 2, extentOffset: 4); + pumpFrame(); + + await simulateKeyDownEvent(LogicalKeyboardKey.shift); + await simulateKeyDownEvent(LogicalKeyboardKey.arrowRight); + await simulateKeyUpEvent(LogicalKeyboardKey.arrowRight); + await simulateKeyUpEvent(LogicalKeyboardKey.shift); + expect(currentSelection.isCollapsed, false); + expect(currentSelection.baseOffset, 2); + expect(currentSelection.extentOffset, 5); + + editable.selection = const TextSelection(baseOffset: 4, extentOffset: 2); + pumpFrame(); + + await simulateKeyDownEvent(LogicalKeyboardKey.shift); + await simulateKeyDownEvent(LogicalKeyboardKey.arrowRight); + await simulateKeyUpEvent(LogicalKeyboardKey.arrowRight); + await simulateKeyUpEvent(LogicalKeyboardKey.shift); + expect(currentSelection.isCollapsed, false); + expect(currentSelection.baseOffset, 4); + expect(currentSelection.extentOffset, 3); + + editable.selection = const TextSelection(baseOffset: 2, extentOffset: 4); + pumpFrame(); + + await simulateKeyDownEvent(LogicalKeyboardKey.shift); + await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft); + await simulateKeyUpEvent(LogicalKeyboardKey.arrowLeft); + await simulateKeyUpEvent(LogicalKeyboardKey.shift); + expect(currentSelection.isCollapsed, false); + expect(currentSelection.baseOffset, 2); + expect(currentSelection.extentOffset, 3); + + editable.selection = const TextSelection(baseOffset: 4, extentOffset: 2); + pumpFrame(); + + await simulateKeyDownEvent(LogicalKeyboardKey.shift); + await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft); + await simulateKeyUpEvent(LogicalKeyboardKey.arrowLeft); + await simulateKeyUpEvent(LogicalKeyboardKey.shift); + expect(currentSelection.isCollapsed, false); + expect(currentSelection.baseOffset, 4); + expect(currentSelection.extentOffset, 1); }, skip: isBrowser); // https://github.com/flutter/flutter/issues/58068 group('delete', () {
diff --git a/packages/flutter/test/widgets/selectable_text_test.dart b/packages/flutter/test/widgets/selectable_text_test.dart index bb8b198..7d2cd13 100644 --- a/packages/flutter/test/widgets/selectable_text_test.dart +++ b/packages/flutter/test/widgets/selectable_text_test.dart
@@ -765,8 +765,8 @@ await gesture.up(); await tester.pumpAndSettle(); - expect(controller.selection.baseOffset, 5); - expect(controller.selection.extentOffset, 8); + expect(controller.selection.baseOffset, 8); + expect(controller.selection.extentOffset, 5); }); testWidgets('Slow mouse dragging also selects text', (WidgetTester tester) async {