From 11943ce7e76cb6212221c2c56752d42beaf1e9ed Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 7 Sep 2018 10:36:52 -0700 Subject: [PATCH] Revert "Shortcuts (#21083)" (#21556) This reverts commit 6e7f3e6f5a11341edd5c2519cbe1c7bc178cb8e9. --- .../flutter/lib/src/rendering/editable.dart | 98 +------ .../flutter/lib/src/services/text_input.dart | 17 -- .../lib/src/widgets/editable_text.dart | 7 +- .../lib/src/widgets/text_selection.dart | 19 +- .../test/material/text_field_test.dart | 249 ------------------ .../flutter/test/rendering/editable_test.dart | 17 -- 6 files changed, 21 insertions(+), 386 deletions(-) diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index 1dd393b96c9..17391e26798 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -135,7 +135,6 @@ class RenderEditable extends RenderBox { Locale locale, double cursorWidth = 1.0, Radius cursorRadius, - @required this.textSelectionDelegate, }) : assert(textAlign != null), assert(textDirection != null, 'RenderEditable created without a textDirection.'), assert(maxLines == null || maxLines > 0), @@ -143,8 +142,7 @@ class RenderEditable extends RenderBox { assert(offset != null), assert(ignorePointer != null), assert(obscureText != null), - assert(textSelectionDelegate != null), - _textPainter = new TextPainter( + _textPainter = new TextPainter( text: text, textAlign: textAlign, textDirection: textDirection, @@ -198,24 +196,12 @@ class RenderEditable extends RenderBox { markNeedsSemanticsUpdate(); } - /// The object that controls the text selection, used by this render object - /// for implementing cut, copy, and paste keyboard shortcuts. - /// - /// It must not be null. It will make cut, copy and paste functionality work - /// with the most recently set [TextSelectionDelegate]. - TextSelectionDelegate textSelectionDelegate; - Rect _lastCaretRect; static const int _kLeftArrowCode = 21; static const int _kRightArrowCode = 22; static const int _kUpArrowCode = 19; static const int _kDownArrowCode = 20; - static const int _kXKeyCode = 52; - static const int _kCKeyCode = 31; - static const int _kVKeyCode = 50; - static const int _kAKeyCode = 29; - static const int _kDelKeyCode = 112; // The extent offset of the current selection int _extentOffset = -1; @@ -235,7 +221,7 @@ class RenderEditable extends RenderBox { static const int _kControlMask = 1 << 12; // https://developer.android.com/reference/android/view/KeyEvent.html#META_CTRL_ON // TODO(goderbauer): doesn't handle extended grapheme clusters with more than one Unicode scalar value (https://github.com/flutter/flutter/issues/13404). - void _handleKeyEvent(RawKeyEvent keyEvent) { + void _handleKeyEvent(RawKeyEvent keyEvent){ if (defaultTargetPlatform != TargetPlatform.android) return; @@ -260,11 +246,6 @@ class RenderEditable extends RenderBox { final bool upArrow = pressedKeyCode == _kUpArrowCode; final bool downArrow = pressedKeyCode == _kDownArrowCode; final bool arrow = leftArrow || rightArrow || upArrow || downArrow; - final bool aKey = pressedKeyCode == _kAKeyCode; - final bool xKey = pressedKeyCode == _kXKeyCode; - final bool vKey = pressedKeyCode == _kVKeyCode; - final bool cKey = pressedKeyCode == _kCKeyCode; - final bool del = pressedKeyCode == _kDelKeyCode; // We will only move select or more the caret if an arrow is pressed if (arrow) { @@ -281,12 +262,7 @@ class RenderEditable extends RenderBox { newOffset = _handleShift(rightArrow, leftArrow, shift, newOffset); _extentOffset = newOffset; - } else if (ctrl && (xKey || vKey || cKey || aKey)) { - // _handleShortcuts depends on being started in the same stack invocation as the _handleKeyEvent method - _handleShortcuts(pressedKeyCode); } - if (del) - _handleDelete(); } // Handles full word traversal using control. @@ -392,7 +368,7 @@ class RenderEditable extends RenderBox { onSelectionChanged( new TextSelection.fromPosition( new TextPosition( - offset: newOffset + offset: newOffset ) ), this, @@ -402,74 +378,6 @@ class RenderEditable extends RenderBox { return newOffset; } - // Handles shortcut functionality including cut, copy, paste and select all - // using control + (X, C, V, A). - void _handleShortcuts(int pressedKeyCode) async { - switch (pressedKeyCode) { - case _kCKeyCode: - if (!selection.isCollapsed) { - Clipboard.setData( - new ClipboardData(text: selection.textInside(text.text))); - } - break; - case _kXKeyCode: - if (!selection.isCollapsed) { - Clipboard.setData( - new ClipboardData(text: selection.textInside(text.text))); - textSelectionDelegate.textEditingValue = new TextEditingValue( - text: selection.textBefore(text.text) - + selection.textAfter(text.text), - selection: new TextSelection.collapsed(offset: selection.start), - ); - } - break; - case _kVKeyCode: - // Snapshot the input before using `await`. - // See https://github.com/flutter/flutter/issues/11427 - final TextEditingValue value = textSelectionDelegate.textEditingValue; - final ClipboardData data = await Clipboard.getData(Clipboard.kTextPlain); - if (data != null) { - textSelectionDelegate.textEditingValue = new TextEditingValue( - text: value.selection.textBefore(value.text) - + data.text - + value.selection.textAfter(value.text), - selection: new TextSelection.collapsed( - offset: value.selection.start + data.text.length - ), - ); - } - break; - case _kAKeyCode: - _baseOffset = 0; - _extentOffset = textSelectionDelegate.textEditingValue.text.length; - onSelectionChanged( - new TextSelection( - baseOffset: 0, - extentOffset: textSelectionDelegate.textEditingValue.text.length, - ), - this, - SelectionChangedCause.keyboard, - ); - break; - default: - assert(false); - } - } - - int _handleDelete() { - if (selection.textAfter(text.text).isNotEmpty) { - textSelectionDelegate.textEditingValue = new TextEditingValue( - text: selection.textBefore(text.text) - + selection.textAfter(text.text).substring(1), - selection: new TextSelection.collapsed(offset: selection.start)); - } else { - textSelectionDelegate.textEditingValue = new TextEditingValue( - text: selection.textBefore(text.text), - selection: new TextSelection.collapsed(offset: selection.start) - ); - } - } - /// Marks the render object as needing to be laid out again and have its text /// metrics recomputed. /// diff --git a/packages/flutter/lib/src/services/text_input.dart b/packages/flutter/lib/src/services/text_input.dart index 48512c028ac..0e08b73d61d 100644 --- a/packages/flutter/lib/src/services/text_input.dart +++ b/packages/flutter/lib/src/services/text_input.dart @@ -534,23 +534,6 @@ class TextEditingValue { ); } -/// An interface for manipulating the selection, to be used by the implementor -/// of the toolbar widget. -abstract class TextSelectionDelegate { - /// Gets the current text input. - TextEditingValue get textEditingValue; - - /// Sets the current text input (replaces the whole line). - set textEditingValue(TextEditingValue value); - - /// Hides the text selection toolbar. - void hideToolbar(); - - /// Brings the provided [TextPosition] into the visible area of the text - /// input. - void bringIntoView(TextPosition position); -} - /// An interface to receive information from [TextInput]. /// /// See also: diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index cfb313fe493..8cc1edc9003 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -904,7 +904,6 @@ class EditableTextState extends State with AutomaticKeepAliveClien rendererIgnoresPointer: widget.rendererIgnoresPointer, cursorWidth: widget.cursorWidth, cursorRadius: widget.cursorRadius, - textSelectionDelegate: this, ), ), ); @@ -968,7 +967,6 @@ class _Editable extends LeafRenderObjectWidget { this.rendererIgnoresPointer = false, this.cursorWidth, this.cursorRadius, - this.textSelectionDelegate, }) : assert(textDirection != null), assert(rendererIgnoresPointer != null), super(key: key); @@ -992,7 +990,6 @@ class _Editable extends LeafRenderObjectWidget { final bool rendererIgnoresPointer; final double cursorWidth; final Radius cursorRadius; - final TextSelectionDelegate textSelectionDelegate; @override RenderEditable createRenderObject(BuildContext context) { @@ -1015,7 +1012,6 @@ class _Editable extends LeafRenderObjectWidget { obscureText: obscureText, cursorWidth: cursorWidth, cursorRadius: cursorRadius, - textSelectionDelegate: textSelectionDelegate, ); } @@ -1039,7 +1035,6 @@ class _Editable extends LeafRenderObjectWidget { ..ignorePointer = rendererIgnoresPointer ..obscureText = obscureText ..cursorWidth = cursorWidth - ..cursorRadius = cursorRadius - ..textSelectionDelegate = textSelectionDelegate; + ..cursorRadius = cursorRadius; } } diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart index 07d0b8bf1e4..b84e058363c 100644 --- a/packages/flutter/lib/src/widgets/text_selection.dart +++ b/packages/flutter/lib/src/widgets/text_selection.dart @@ -16,8 +16,6 @@ import 'gesture_detector.dart'; import 'overlay.dart'; import 'transitions.dart'; -export 'package:flutter/services.dart' show TextSelectionDelegate; - /// Which type of selection handle to be displayed. /// /// With mixed-direction text, both handles may be the same type. Examples: @@ -62,6 +60,23 @@ enum _TextSelectionHandlePosition { start, end } /// Used by [TextSelectionOverlay.onSelectionOverlayChanged]. typedef void TextSelectionOverlayChanged(TextEditingValue value, Rect caretRect); +/// An interface for manipulating the selection, to be used by the implementor +/// of the toolbar widget. +abstract class TextSelectionDelegate { + /// Gets the current text input. + TextEditingValue get textEditingValue; + + /// Sets the current text input (replaces the whole line). + set textEditingValue(TextEditingValue value); + + /// Hides the text selection toolbar. + void hideToolbar(); + + /// Brings the provided [TextPosition] into the visible area of the text + /// input. + void bringIntoView(TextPosition position); +} + /// An interface for building the selection UI, to be provided by the /// implementor of the toolbar widget. /// diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 8d33181ab66..5d3151a8345 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -1919,255 +1919,6 @@ void main() { }); }); - const int _kXKeyCode = 52; - const int _kCKeyCode = 31; - const int _kVKeyCode = 50; - const int _kAKeyCode = 29; - const int _kDelKeyCode = 112; - - testWidgets('Copy paste test', (WidgetTester tester) async{ - final FocusNode focusNode = new FocusNode(); - final TextEditingController controller = new TextEditingController(); - final TextField textField = - new TextField( - controller: controller, - maxLines: 3, - ); - - String clipboardContent = ''; - SystemChannels.platform - .setMockMethodCallHandler((MethodCall methodCall) async { - if (methodCall.method == 'Clipboard.setData') - clipboardContent = methodCall.arguments['text']; - else if (methodCall.method == 'Clipboard.getData') - return {'text': clipboardContent}; - return null; - }); - - await tester.pumpWidget( - new MaterialApp( - home: new Material( - child: new RawKeyboardListener( - focusNode: focusNode, - onKey: null, - child: textField, - ), - ), - ), - ); - - const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19 - await tester.enterText(find.byType(TextField), testValue); - - await tester.idle(); - await tester.tap(find.byType(TextField)); - await tester.pumpAndSettle(); - - // Select the first 5 characters - for (int i = 0; i < 5; i += 1) { - sendKeyEventWithCode(22, true, true, false); // RIGHT_ARROW keydown shift - await tester.pumpAndSettle(); - sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup - await tester.pumpAndSettle(); - } - - // Copy them - sendKeyEventWithCode(_kCKeyCode, true, false, true); // keydown control - await tester.pumpAndSettle(); - sendKeyEventWithCode(_kCKeyCode, false, false, false); // keyup control - await tester.pumpAndSettle(); - - expect(clipboardContent, 'a big'); - - sendKeyEventWithCode(22, true, false, false); // RIGHT_ARROW keydown - await tester.pumpAndSettle(); - sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup - await tester.pumpAndSettle(); - - // Paste them - sendKeyEventWithCode(_kVKeyCode, true, false, true); // Control V keydown - await tester.pumpAndSettle(); - await tester.pump(const Duration(milliseconds: 200)); - - sendKeyEventWithCode(_kVKeyCode, false, false, false); // Control V keyup - await tester.pumpAndSettle(); - - const String expected = 'a biga big house\njumped over a mouse'; - expect(find.text(expected), findsOneWidget); - }); - - testWidgets('Cut test', (WidgetTester tester) async{ - final FocusNode focusNode = new FocusNode(); - final TextEditingController controller = new TextEditingController(); - final TextField textField = - new TextField( - controller: controller, - maxLines: 3, - ); - String clipboardContent = ''; - SystemChannels.platform - .setMockMethodCallHandler((MethodCall methodCall) async { - if (methodCall.method == 'Clipboard.setData') - clipboardContent = methodCall.arguments['text']; - else if (methodCall.method == 'Clipboard.getData') - return {'text': clipboardContent}; - return null; - }); - - await tester.pumpWidget( - new MaterialApp( - home: new Material( - child: new RawKeyboardListener( - focusNode: focusNode, - onKey: null, - child: textField, - ), - ), - ), - ); - - const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19 - await tester.enterText(find.byType(TextField), testValue); - - await tester.idle(); - await tester.tap(find.byType(TextField)); - await tester.pumpAndSettle(); - - // Select the first 5 characters - for (int i = 0; i < 5; i += 1) { - sendKeyEventWithCode(22, true, true, false); // RIGHT_ARROW keydown shift - await tester.pumpAndSettle(); - sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup - await tester.pumpAndSettle(); - } - - // Cut them - sendKeyEventWithCode(_kXKeyCode, true, false, true); // keydown control X - await tester.pumpAndSettle(); - sendKeyEventWithCode(_kXKeyCode, false, false, false); // keyup control X - await tester.pumpAndSettle(); - - expect(clipboardContent, 'a big'); - - for (int i = 0; i < 5; i += 1) { - sendKeyEventWithCode(22, true, false, false); // RIGHT_ARROW keydown - await tester.pumpAndSettle(); - sendKeyEventWithCode(22, false, false, false); // RIGHT_ARROW keyup - await tester.pumpAndSettle(); - } - - // Paste them - sendKeyEventWithCode(_kVKeyCode, true, false, true); // Control V keydown - await tester.pumpAndSettle(); - await tester.pump(const Duration(milliseconds: 200)); - - sendKeyEventWithCode(_kVKeyCode, false, false, false); // Control V keyup - await tester.pumpAndSettle(); - - const String expected = ' housa bige\njumped over a mouse'; - expect(find.text(expected), findsOneWidget); - }); - - testWidgets('Select all test', (WidgetTester tester) async{ - final FocusNode focusNode = new FocusNode(); - final TextEditingController controller = new TextEditingController(); - final TextField textField = - new TextField( - controller: controller, - maxLines: 3, - ); - - await tester.pumpWidget( - new MaterialApp( - home: new Material( - child: new RawKeyboardListener( - focusNode: focusNode, - onKey: null, - child: textField, - ), - ), - ), - ); - - const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19 - await tester.enterText(find.byType(TextField), testValue); - - await tester.idle(); - await tester.tap(find.byType(TextField)); - await tester.pumpAndSettle(); - - // Select All - sendKeyEventWithCode(_kAKeyCode, true, false, true); // keydown control A - await tester.pumpAndSettle(); - sendKeyEventWithCode(_kAKeyCode, false, false, true); // keyup control A - await tester.pumpAndSettle(); - - // Delete them - sendKeyEventWithCode(_kDelKeyCode, true, false, false); // DEL keydown - await tester.pumpAndSettle(); - await tester.pump(const Duration(milliseconds: 200)); - - sendKeyEventWithCode(_kDelKeyCode, false, false, false); // DEL keyup - await tester.pumpAndSettle(); - - const String expected = ''; - expect(find.text(expected), findsOneWidget); - }); - - testWidgets('Delete test', (WidgetTester tester) async{ - final FocusNode focusNode = new FocusNode(); - final TextEditingController controller = new TextEditingController(); - final TextField textField = - new TextField( - controller: controller, - maxLines: 3, - ); - - await tester.pumpWidget( - new MaterialApp( - home: new Material( - child: new RawKeyboardListener( - focusNode: focusNode, - onKey: null, - child: textField, - ), - ), - ), - ); - - const String testValue = 'a big house\njumped over a mouse'; // 11 \n 19 - await tester.enterText(find.byType(TextField), testValue); - - await tester.idle(); - await tester.tap(find.byType(TextField)); - await tester.pumpAndSettle(); - - // Delete - for (int i = 0; i < 6; i += 1) { - sendKeyEventWithCode(_kDelKeyCode, true, false, false); // keydown DEL - await tester.pumpAndSettle(); - sendKeyEventWithCode(_kDelKeyCode, false, false, false); // keyup DEL - await tester.pumpAndSettle(); - } - - const String expected = 'house\njumped over a mouse'; - expect(find.text(expected), findsOneWidget); - - sendKeyEventWithCode(_kAKeyCode, true, false, true); // keydown control A - await tester.pumpAndSettle(); - sendKeyEventWithCode(_kAKeyCode, false, false, true); // keyup control A - await tester.pumpAndSettle(); - - - sendKeyEventWithCode(_kDelKeyCode, true, false, false); // keydown DEL - await tester.pumpAndSettle(); - sendKeyEventWithCode(_kDelKeyCode, false, false, false); // keyup DEL - await tester.pumpAndSettle(); - - const String expected2 = ''; - expect(find.text(expected2), findsOneWidget); - }); - testWidgets('Changing positions of text fields', (WidgetTester tester) async{ final FocusNode focusNode = new FocusNode(); diff --git a/packages/flutter/test/rendering/editable_test.dart b/packages/flutter/test/rendering/editable_test.dart index 2949f205696..1fd0c7575b0 100644 --- a/packages/flutter/test/rendering/editable_test.dart +++ b/packages/flutter/test/rendering/editable_test.dart @@ -4,25 +4,9 @@ import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/services.dart'; - -class FakeEditableTextState extends TextSelectionDelegate { - @override - TextEditingValue get textEditingValue {} - - @override - set textEditingValue(TextEditingValue value) {} - - @override - void hideToolbar() {} - - @override - void bringIntoView(TextPosition position) {} -} void main() { test('editable intrinsics', () { - final TextSelectionDelegate delegate = new FakeEditableTextState(); final RenderEditable editable = new RenderEditable( text: const TextSpan( style: TextStyle(height: 1.0, fontSize: 10.0, fontFamily: 'Ahem'), @@ -32,7 +16,6 @@ void main() { textDirection: TextDirection.ltr, locale: const Locale('ja', 'JP'), offset: new ViewportOffset.zero(), - textSelectionDelegate: delegate, ); expect(editable.getMinIntrinsicWidth(double.infinity), 50.0); expect(editable.getMaxIntrinsicWidth(double.infinity), 50.0);