Linux and Windows right clicking text behavior (#101588)
This commit is contained in:
parent
8ffb1d21ef
commit
916b2aa76b
@ -1615,14 +1615,29 @@ class TextSelectionGestureDetectorBuilder {
|
||||
/// By default, selects the word if possible and shows the toolbar.
|
||||
@protected
|
||||
void onSecondaryTap() {
|
||||
if (delegate.selectionEnabled) {
|
||||
if (!_lastSecondaryTapWasOnSelection) {
|
||||
renderEditable.selectWord(cause: SelectionChangedCause.tap);
|
||||
}
|
||||
if (shouldShowSelectionToolbar) {
|
||||
editableText.hideToolbar();
|
||||
editableText.showToolbar();
|
||||
}
|
||||
if (!delegate.selectionEnabled) {
|
||||
return;
|
||||
}
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
if (!_lastSecondaryTapWasOnSelection) {
|
||||
renderEditable.selectWord(cause: SelectionChangedCause.tap);
|
||||
}
|
||||
if (shouldShowSelectionToolbar) {
|
||||
editableText.hideToolbar();
|
||||
editableText.showToolbar();
|
||||
}
|
||||
break;
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
if (!renderEditable.hasFocus) {
|
||||
renderEditable.selectPosition(cause: SelectionChangedCause.tap);
|
||||
}
|
||||
editableText.toggleToolbar();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5496,4 +5496,87 @@ void main() {
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
expect(controller.selection.extentOffset, 14);
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.windows }));
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/101587.
|
||||
testWidgets('Right clicking menu behavior', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'blah1 blah2',
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
child: CupertinoTextField(
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Initially, the menu is not shown and there is no selection.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1));
|
||||
|
||||
final Offset midBlah1 = textOffsetToPosition(tester, 2);
|
||||
final Offset midBlah2 = textOffsetToPosition(tester, 8);
|
||||
|
||||
// Right click the second word.
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
midBlah2,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
addTearDown(gesture.removePointer);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
expect(controller.selection, const TextSelection(baseOffset: 6, extentOffset: 11));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
break;
|
||||
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 8));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
break;
|
||||
}
|
||||
|
||||
// Right click the first word.
|
||||
await gesture.down(midBlah1);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
break;
|
||||
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 8));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsNothing);
|
||||
break;
|
||||
}
|
||||
},
|
||||
variant: TargetPlatformVariant.all(),
|
||||
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
|
||||
);
|
||||
}
|
||||
|
@ -241,6 +241,7 @@ void main() {
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
@ -282,7 +283,151 @@ void main() {
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 0));
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
},
|
||||
variant: TargetPlatformVariant.desktop(),
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS }),
|
||||
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
|
||||
);
|
||||
|
||||
testWidgets('can use the desktop cut/copy/paste buttons on Windows and Linux', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'blah1 blah2',
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Initially, the menu is not shown and there is no selection.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1));
|
||||
|
||||
final Offset midBlah1 = textOffsetToPosition(tester, 2);
|
||||
|
||||
// Right clicking shows the menu.
|
||||
TestGesture gesture = await tester.startGesture(
|
||||
midBlah1,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 2));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
expect(find.text('Select all'), findsOneWidget);
|
||||
|
||||
// Double tap to select the first word, then right click to show the menu.
|
||||
final Offset startBlah1 = textOffsetToPosition(tester, 0);
|
||||
gesture = await tester.startGesture(
|
||||
startBlah1,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
await gesture.down(startBlah1);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsNothing);
|
||||
expect(find.text('Select all'), findsNothing);
|
||||
gesture = await tester.startGesture(
|
||||
midBlah1,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
|
||||
// Copy the first word.
|
||||
await tester.tap(find.text('Copy'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.text, 'blah1 blah2');
|
||||
expect(controller.selection, const TextSelection(baseOffset: 5, extentOffset: 5));
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
|
||||
// Paste it at the end.
|
||||
gesture = await tester.startGesture(
|
||||
textOffsetToPosition(tester, controller.text.length),
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 11, affinity: TextAffinity.upstream));
|
||||
gesture = await tester.startGesture(
|
||||
textOffsetToPosition(tester, controller.text.length),
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 11, affinity: TextAffinity.upstream));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
await tester.tap(find.text('Paste'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.text, 'blah1 blah2blah1');
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 16));
|
||||
|
||||
// Cut the first word.
|
||||
gesture = await tester.startGesture(
|
||||
midBlah1,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
await gesture.down(startBlah1);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsNothing);
|
||||
expect(find.text('Select all'), findsNothing);
|
||||
gesture = await tester.startGesture(
|
||||
textOffsetToPosition(tester, controller.text.length),
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
await tester.tap(find.text('Cut'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.text, ' blah2blah1');
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 0));
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.windows }),
|
||||
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
|
||||
);
|
||||
|
||||
@ -10057,9 +10202,9 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
|
||||
await gesture.moveTo(tester.getCenter(find.text('Copy')));
|
||||
await gesture.moveTo(tester.getCenter(find.text('Paste')));
|
||||
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.basic);
|
||||
},
|
||||
variant: TargetPlatformVariant.desktop(),
|
||||
@ -11093,4 +11238,89 @@ void main() {
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
expect(controller.selection.extentOffset, 14);
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.windows }));
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/101587.
|
||||
testWidgets('Right clicking menu behavior', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'blah1 blah2',
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Initially, the menu is not shown and there is no selection.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1));
|
||||
|
||||
final Offset midBlah1 = textOffsetToPosition(tester, 2);
|
||||
final Offset midBlah2 = textOffsetToPosition(tester, 8);
|
||||
|
||||
// Right click the second word.
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
midBlah2,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
addTearDown(gesture.removePointer);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
expect(controller.selection, const TextSelection(baseOffset: 6, extentOffset: 11));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
break;
|
||||
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 8));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
expect(find.text('Select all'), findsOneWidget);
|
||||
break;
|
||||
}
|
||||
|
||||
// Right click the first word.
|
||||
await gesture.down(midBlah1);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
break;
|
||||
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 8));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsNothing);
|
||||
expect(find.text('Select all'), findsNothing);
|
||||
break;
|
||||
}
|
||||
},
|
||||
variant: TargetPlatformVariant.all(),
|
||||
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
|
||||
);
|
||||
}
|
||||
|
@ -98,7 +98,153 @@ void main() {
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 0));
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
},
|
||||
variant: TargetPlatformVariant.desktop(),
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.macOS }),
|
||||
skip: kIsWeb, // [intended] we don't supply the cut/copy/paste buttons on the web.
|
||||
);
|
||||
|
||||
testWidgets('can use the desktop cut/copy/paste buttons on Windows and Linux', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'blah1 blah2',
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Initially, the menu is not shown and there is no selection.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1));
|
||||
|
||||
final Offset midBlah1 = textOffsetToPosition(tester, 2);
|
||||
|
||||
// Right clicking shows the menu.
|
||||
TestGesture gesture = await tester.startGesture(
|
||||
midBlah1,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 2));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
expect(find.text('Select all'), findsOneWidget);
|
||||
|
||||
// Double tap to select the first word, then right click to show the menu.
|
||||
final Offset startBlah1 = textOffsetToPosition(tester, 0);
|
||||
gesture = await tester.startGesture(
|
||||
startBlah1,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
await gesture.down(startBlah1);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsNothing);
|
||||
expect(find.text('Select all'), findsNothing);
|
||||
gesture = await tester.startGesture(
|
||||
midBlah1,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
|
||||
// Copy the first word.
|
||||
await tester.tap(find.text('Copy'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.text, 'blah1 blah2');
|
||||
expect(controller.selection, const TextSelection(baseOffset: 5, extentOffset: 5));
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
|
||||
// Paste it at the end.
|
||||
gesture = await tester.startGesture(
|
||||
textOffsetToPosition(tester, controller.text.length),
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 11, affinity: TextAffinity.upstream));
|
||||
gesture = await tester.startGesture(
|
||||
textOffsetToPosition(tester, controller.text.length),
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 11, affinity: TextAffinity.upstream));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
await tester.tap(find.text('Paste'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.text, 'blah1 blah2blah1');
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 16));
|
||||
|
||||
// Cut the first word.
|
||||
gesture = await tester.startGesture(
|
||||
midBlah1,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
await gesture.down(startBlah1);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsNothing);
|
||||
expect(find.text('Select all'), findsNothing);
|
||||
gesture = await tester.startGesture(
|
||||
textOffsetToPosition(tester, controller.text.length),
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await gesture.removePointer();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
await tester.tap(find.text('Cut'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.text, ' blah2blah1');
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 0));
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.windows }),
|
||||
skip: kIsWeb, // [intended] we don't supply the cut/copy/paste buttons on the web.
|
||||
);
|
||||
|
||||
@ -171,6 +317,16 @@ void main() {
|
||||
|
||||
final Offset midBlah1 = textOffsetToPosition(tester, 2);
|
||||
|
||||
// Make a selection.
|
||||
await tester.tapAt(midBlah1);
|
||||
await tester.pump();
|
||||
await tester.sendKeyDownEvent(LogicalKeyboardKey.shift);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft);
|
||||
await tester.sendKeyUpEvent(LogicalKeyboardKey.shift);
|
||||
await tester.pump();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 2, extentOffset: 0));
|
||||
|
||||
// Right clicking shows the menu.
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
midBlah1,
|
||||
@ -181,7 +337,6 @@ void main() {
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 11));
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
@ -882,4 +1037,90 @@ void main() {
|
||||
await gesture.moveTo(center);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/101587.
|
||||
testWidgets('Right clicking menu behavior', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'blah1 blah2',
|
||||
);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Initially, the menu is not shown and there is no selection.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
expect(controller.selection, const TextSelection(baseOffset: -1, extentOffset: -1));
|
||||
|
||||
final Offset midBlah1 = textOffsetToPosition(tester, 2);
|
||||
final Offset midBlah2 = textOffsetToPosition(tester, 8);
|
||||
|
||||
// Right click the second word.
|
||||
final TestGesture gesture = await tester.startGesture(
|
||||
midBlah2,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton,
|
||||
);
|
||||
addTearDown(gesture.removePointer);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
expect(controller.selection, const TextSelection(baseOffset: 6, extentOffset: 11));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
break;
|
||||
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 8));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
expect(find.text('Select all'), findsOneWidget);
|
||||
break;
|
||||
}
|
||||
|
||||
// Right click the first word.
|
||||
await gesture.down(midBlah1);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
expect(controller.selection, const TextSelection(baseOffset: 0, extentOffset: 5));
|
||||
expect(find.text('Cut'), findsOneWidget);
|
||||
expect(find.text('Copy'), findsOneWidget);
|
||||
expect(find.text('Paste'), findsOneWidget);
|
||||
break;
|
||||
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
expect(controller.selection, const TextSelection.collapsed(offset: 8));
|
||||
expect(find.text('Cut'), findsNothing);
|
||||
expect(find.text('Copy'), findsNothing);
|
||||
expect(find.text('Paste'), findsNothing);
|
||||
expect(find.text('Select all'), findsNothing);
|
||||
break;
|
||||
}
|
||||
},
|
||||
variant: TargetPlatformVariant.all(),
|
||||
skip: kIsWeb, // [intended] we don't supply the cut/copy/paste buttons on the web.
|
||||
);
|
||||
}
|
||||
|
@ -4613,8 +4613,8 @@ void main() {
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
expect(newSelection!.baseOffset, 4);
|
||||
expect(newSelection!.extentOffset, 7);
|
||||
expect(newSelection!.isCollapsed, isTrue);
|
||||
expect(newSelection!.baseOffset, 5);
|
||||
newSelection = null;
|
||||
|
||||
await tester.tap(find.text('Select all'));
|
||||
|
@ -455,7 +455,7 @@ void main() {
|
||||
expect(renderEditable.selectPositionAtCalled, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('TextSelectionGestureDetectorBuilder right click', (WidgetTester tester) async {
|
||||
testWidgets('TextSelectionGestureDetectorBuilder right click Apple platforms', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/80119
|
||||
await pumpTextSelectionGestureDetectorBuilder(tester);
|
||||
|
||||
@ -496,7 +496,60 @@ void main() {
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
expect(renderEditable.selectWordCalled, isFalse);
|
||||
});
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
|
||||
);
|
||||
|
||||
testWidgets('TextSelectionGestureDetectorBuilder right click non-Apple platforms', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/80119
|
||||
await pumpTextSelectionGestureDetectorBuilder(tester);
|
||||
|
||||
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
|
||||
renderEditable.text = const TextSpan(text: 'one two three four five six seven');
|
||||
await tester.pump();
|
||||
|
||||
final TestGesture gesture = await tester.createGesture(
|
||||
pointer: 0,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryButton,
|
||||
);
|
||||
addTearDown(gesture.removePointer);
|
||||
|
||||
// Get the location of the 10th character
|
||||
final Offset charLocation = renderEditable
|
||||
.getLocalRectForCaret(const TextPosition(offset: 10)).center;
|
||||
final Offset globalCharLocation = charLocation + tester.getTopLeft(find.byType(FakeEditable));
|
||||
|
||||
// Right clicking on an unfocused field should place the cursor, not select
|
||||
// the word.
|
||||
await gesture.down(globalCharLocation);
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
expect(renderEditable.selectWordCalled, isFalse);
|
||||
expect(renderEditable.selectPositionCalled, isTrue);
|
||||
|
||||
// Right clicking on a focused field with selection shouldn't change the
|
||||
// selection.
|
||||
renderEditable.selectPositionCalled = false;
|
||||
renderEditable.selection = const TextSelection(baseOffset: 3, extentOffset: 20);
|
||||
renderEditable.hasFocus = true;
|
||||
await gesture.down(globalCharLocation);
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
expect(renderEditable.selectWordCalled, isFalse);
|
||||
expect(renderEditable.selectPositionCalled, isFalse);
|
||||
|
||||
// Right clicking on a focused field with a reverse (right to left)
|
||||
// selection shouldn't change the selection.
|
||||
renderEditable.selection = const TextSelection(baseOffset: 20, extentOffset: 3);
|
||||
await gesture.down(globalCharLocation);
|
||||
await gesture.up();
|
||||
await tester.pump();
|
||||
expect(renderEditable.selectWordCalled, isFalse);
|
||||
expect(renderEditable.selectPositionCalled, isFalse);
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.linux, TargetPlatform.windows }),
|
||||
);
|
||||
|
||||
testWidgets('test TextSelectionGestureDetectorBuilder tap', (WidgetTester tester) async {
|
||||
await pumpTextSelectionGestureDetectorBuilder(tester);
|
||||
@ -1083,6 +1136,11 @@ class FakeEditableTextState extends EditableTextState {
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
void toggleToolbar() {
|
||||
return;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
@ -1144,11 +1202,21 @@ class FakeRenderEditable extends RenderEditable {
|
||||
selectPositionAtTo = to;
|
||||
}
|
||||
|
||||
bool selectPositionCalled = false;
|
||||
@override
|
||||
void selectPosition({ required SelectionChangedCause cause }) {
|
||||
selectPositionCalled = true;
|
||||
return super.selectPosition(cause: cause);
|
||||
}
|
||||
|
||||
bool selectWordCalled = false;
|
||||
@override
|
||||
void selectWord({ required SelectionChangedCause cause }) {
|
||||
selectWordCalled = true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool hasFocus = false;
|
||||
}
|
||||
|
||||
class CustomTextSelectionControls extends TextSelectionControls {
|
||||
|
Loading…
x
Reference in New Issue
Block a user