From c706abf07526aa40a456b32da51fed6b111a2bf8 Mon Sep 17 00:00:00 2001 From: YeungKC Date: Tue, 20 Oct 2020 03:42:03 +0800 Subject: [PATCH] Fix error cursor position for left and right arrow event after text selection (#68402) --- .../flutter/lib/src/rendering/editable.dart | 14 ++++- .../test/material/text_field_test.dart | 2 +- .../flutter/test/rendering/editable_test.dart | 62 +++++++++++++++++++ .../test/widgets/editable_text_test.dart | 19 +++--- 4 files changed, 84 insertions(+), 13 deletions(-) diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index 067ba9b6c6f..5824927d3a0 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -676,14 +676,24 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin { } } else { if (rightArrow && newSelection.extentOffset < _plainText.length) { - final int nextExtent = nextCharacter(newSelection.extentOffset, _plainText); + int nextExtent; + if (!shift && !wordModifier && !lineModifier && newSelection.start != newSelection.end) { + nextExtent = newSelection.end; + } else { + nextExtent = nextCharacter(newSelection.extentOffset, _plainText); + } final int distance = nextExtent - newSelection.extentOffset; newSelection = newSelection.copyWith(extentOffset: nextExtent); if (shift) { _cursorResetLocation += distance; } } else if (leftArrow && newSelection.extentOffset > 0) { - final int previousExtent = previousCharacter(newSelection.extentOffset, _plainText); + int previousExtent; + if (!shift && !wordModifier && !lineModifier && newSelection.start != newSelection.end) { + previousExtent = newSelection.start; + } else { + previousExtent = previousCharacter(newSelection.extentOffset, _plainText); + } final int distance = newSelection.extentOffset - previousExtent; newSelection = newSelection.copyWith(extentOffset: previousExtent); if (shift) { diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index dd94976656b..4f4ab2dbe0b 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -4331,7 +4331,7 @@ void main() { await tester.sendKeyUpEvent(LogicalKeyboardKey.controlRight); await tester.pumpAndSettle(); - const String expected = 'a big a bighouse\njumped over a mouse'; + const String expected = 'a biga big house\njumped over a mouse'; expect(find.text(expected), findsOneWidget, reason: 'Because text contains ${controller.text}'); }); diff --git a/packages/flutter/test/rendering/editable_test.dart b/packages/flutter/test/rendering/editable_test.dart index 61f1a011fa2..f0da93752d5 100644 --- a/packages/flutter/test/rendering/editable_test.dart +++ b/packages/flutter/test/rendering/editable_test.dart @@ -935,6 +935,68 @@ void main() { expect(delegate.textEditingValue.text, ''); }, skip: isBrowser); // https://github.com/flutter/flutter/issues/61021 + test('arrow keys with selection text', () 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.arrowRight); + await simulateKeyUpEvent(LogicalKeyboardKey.arrowRight); + expect(currentSelection.isCollapsed, true); + expect(currentSelection.baseOffset, 4); + + editable.selection = const TextSelection(baseOffset: 4, extentOffset: 2); + pumpFrame(); + + await simulateKeyDownEvent(LogicalKeyboardKey.arrowRight); + await simulateKeyUpEvent(LogicalKeyboardKey.arrowRight); + expect(currentSelection.isCollapsed, true); + expect(currentSelection.baseOffset, 4); + + editable.selection = const TextSelection(baseOffset: 2, extentOffset: 4); + pumpFrame(); + + await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft); + await simulateKeyUpEvent(LogicalKeyboardKey.arrowLeft); + expect(currentSelection.isCollapsed, true); + expect(currentSelection.baseOffset, 2); + + editable.selection = const TextSelection(baseOffset: 4, extentOffset: 2); + pumpFrame(); + + await simulateKeyDownEvent(LogicalKeyboardKey.arrowLeft); + await simulateKeyUpEvent(LogicalKeyboardKey.arrowLeft); + expect(currentSelection.isCollapsed, true); + expect(currentSelection.baseOffset, 2); + + }, skip: isBrowser); // https://github.com/flutter/flutter/issues/58068 + test('getEndpointsForSelection handles empty characters', () { final TextSelectionDelegate delegate = FakeEditableTextState(); final RenderEditable editable = RenderEditable( diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 89865be5484..9c7b2f1314a 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -3989,7 +3989,6 @@ void main() { reason: 'on $platform', ); - // Move forward one character to reset the selection. await sendKeys( tester, [ @@ -4002,8 +4001,8 @@ void main() { selection, equals( const TextSelection( - baseOffset: 21, - extentOffset: 21, + baseOffset: 20, + extentOffset: 20, affinity: TextAffinity.downstream, ), ), @@ -4024,8 +4023,8 @@ void main() { selection, equals( const TextSelection( - baseOffset: 21, - extentOffset: 40, + baseOffset: 20, + extentOffset: 39, affinity: TextAffinity.downstream, ), ), @@ -4049,7 +4048,7 @@ void main() { selection, equals( const TextSelection( - baseOffset: 21, + baseOffset: 20, extentOffset: testText.length, affinity: TextAffinity.downstream, ), @@ -4071,8 +4070,8 @@ void main() { selection, equals( const TextSelection( - baseOffset: 21, - extentOffset: 58, + baseOffset: 20, + extentOffset: 57, affinity: TextAffinity.downstream, ), ), @@ -4094,7 +4093,7 @@ void main() { selection, equals( const TextSelection( - baseOffset: 21, + baseOffset: 20, extentOffset: 72, affinity: TextAffinity.downstream, ), @@ -4117,7 +4116,7 @@ void main() { selection, equals( const TextSelection( - baseOffset: 21, + baseOffset: 20, extentOffset: 55, affinity: TextAffinity.downstream, ),