Unverified Commit 8d5903f7 authored by Justin McCandless's avatar Justin McCandless Committed by GitHub

Pasting collapses the selection and puts it after the pasted content (#98679)

Desktop selection behavior improvement.
parent 735f9abf
......@@ -1716,7 +1716,17 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
return;
}
_replaceText(ReplaceTextIntent(textEditingValue, data.text!, selection, cause));
// After the paste, the cursor should be collapsed and located after the
// pasted content.
final int lastSelectionIndex = math.max(selection.baseOffset, selection.extentOffset);
final TextEditingValue collapsedTextEditingValue = textEditingValue.copyWith(
selection: TextSelection.collapsed(offset: lastSelectionIndex),
);
userUpdateTextEditingValue(
collapsedTextEditingValue.replaced(selection, data.text!),
cause,
);
if (cause == SelectionChangedCause.toolbar) {
// Schedule a call to bringIntoView() after renderEditable updates.
SchedulerBinding.instance.addPostFrameCallback((_) {
......
......@@ -49,6 +49,8 @@ enum HandlePositionInViewport {
leftEdge, rightEdge, within,
}
typedef _VoidFutureCallback = Future<void> Function();
void main() {
final MockClipboard mockClipboard = MockClipboard();
TestWidgetsFlutterBinding.ensureInitialized()
......@@ -11519,6 +11521,140 @@ void main() {
}, variant: TargetPlatformVariant.all(), skip: kIsWeb); // [intended]
});
testWidgets('pasting with the keyboard collapses the selection and places it after the pasted content', (WidgetTester tester) async {
Future<void> testPasteSelection(WidgetTester tester, _VoidFutureCallback paste) async {
final TextEditingController controller = TextEditingController();
await tester.pumpWidget(
MaterialApp(
home: EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
selectionControls: materialTextSelectionControls,
),
),
);
await tester.pump();
expect(controller.text, '');
await tester.enterText(find.byType(EditableText), '12345');
expect(controller.value, const TextEditingValue(
text: '12345',
selection: TextSelection.collapsed(offset: 5),
));
await sendKeys(
tester,
<LogicalKeyboardKey>[
LogicalKeyboardKey.arrowLeft,
LogicalKeyboardKey.arrowLeft,
LogicalKeyboardKey.arrowLeft,
LogicalKeyboardKey.arrowLeft,
LogicalKeyboardKey.arrowLeft,
],
shift: true,
targetPlatform: defaultTargetPlatform,
);
expect(controller.value, const TextEditingValue(
text: '12345',
selection: TextSelection(baseOffset: 5, extentOffset: 0),
));
await sendKeys(
tester,
<LogicalKeyboardKey>[
LogicalKeyboardKey.keyC,
],
shortcutModifier: true,
targetPlatform: defaultTargetPlatform,
);
expect(controller.value, const TextEditingValue(
text: '12345',
selection: TextSelection(baseOffset: 5, extentOffset: 0),
));
// Pasting content of equal length, reversed selection.
await paste();
expect(controller.value, const TextEditingValue(
text: '12345',
selection: TextSelection.collapsed(offset: 5),
));
// Pasting content of longer length, forward selection.
await sendKeys(
tester,
<LogicalKeyboardKey>[
LogicalKeyboardKey.arrowLeft,
],
targetPlatform: defaultTargetPlatform,
);
await sendKeys(
tester,
<LogicalKeyboardKey>[
LogicalKeyboardKey.arrowRight,
],
shift: true,
targetPlatform: defaultTargetPlatform,
);
expect(controller.value, const TextEditingValue(
text: '12345',
selection: TextSelection(baseOffset: 4, extentOffset: 5),
));
await paste();
expect(controller.value, const TextEditingValue(
text: '123412345',
selection: TextSelection.collapsed(offset: 9),
));
// Pasting content of shorter length, forward selection.
await sendKeys(
tester,
<LogicalKeyboardKey>[
LogicalKeyboardKey.keyA,
],
shortcutModifier: true,
targetPlatform: defaultTargetPlatform,
);
expect(controller.value, const TextEditingValue(
text: '123412345',
selection: TextSelection(baseOffset: 0, extentOffset: 9),
));
await paste();
// Pump to allow postFrameCallbacks to finish before dispose.
await tester.pump();
expect(controller.value, const TextEditingValue(
text: '12345',
selection: TextSelection.collapsed(offset: 5),
));
}
// Test pasting with the keyboard.
await testPasteSelection(tester, () {
return sendKeys(
tester,
<LogicalKeyboardKey>[
LogicalKeyboardKey.keyV,
],
shortcutModifier: true,
targetPlatform: defaultTargetPlatform,
);
});
// Test pasting with the toolbar.
await testPasteSelection(tester, () async {
final EditableTextState state =
tester.state<EditableTextState>(find.byType(EditableText));
expect(state.showToolbar(), true);
await tester.pumpAndSettle();
expect(find.text('Paste'), findsOneWidget);
return tester.tap(find.text('Paste'));
});
}, skip: kIsWeb); // [intended]
// Regression test for https://github.com/flutter/flutter/issues/98322.
testWidgets('EditableText consumes ActivateIntent and ButtonActivateIntent', (WidgetTester tester) async {
bool receivedIntent = false;
......
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