@@ -241,6 +241,7 @@ void main() {
241
241
await tester.pump ();
242
242
await gesture.up ();
243
243
await tester.pumpAndSettle ();
244
+
244
245
expect (controller.selection, const TextSelection (baseOffset: 0 , extentOffset: 5 ));
245
246
expect (find.text ('Cut' ), findsOneWidget);
246
247
expect (find.text ('Copy' ), findsOneWidget);
@@ -282,7 +283,151 @@ void main() {
282
283
expect (controller.selection, const TextSelection (baseOffset: 0 , extentOffset: 0 ));
283
284
expect (find.byType (CupertinoButton ), findsNothing);
284
285
},
285
- variant: TargetPlatformVariant .desktop (),
286
+ variant: const TargetPlatformVariant (< TargetPlatform > { TargetPlatform .macOS }),
287
+ skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
288
+ );
289
+
290
+ testWidgets ('can use the desktop cut/copy/paste buttons on Windows and Linux' , (WidgetTester tester) async {
291
+ final TextEditingController controller = TextEditingController (
292
+ text: 'blah1 blah2' ,
293
+ );
294
+ await tester.pumpWidget (
295
+ MaterialApp (
296
+ home: Material (
297
+ child: TextField (
298
+ controller: controller,
299
+ ),
300
+ ),
301
+ ),
302
+ );
303
+
304
+ // Initially, the menu is not shown and there is no selection.
305
+ expect (find.byType (CupertinoButton ), findsNothing);
306
+ expect (controller.selection, const TextSelection (baseOffset: - 1 , extentOffset: - 1 ));
307
+
308
+ final Offset midBlah1 = textOffsetToPosition (tester, 2 );
309
+
310
+ // Right clicking shows the menu.
311
+ TestGesture gesture = await tester.startGesture (
312
+ midBlah1,
313
+ kind: PointerDeviceKind .mouse,
314
+ buttons: kSecondaryMouseButton,
315
+ );
316
+ await tester.pump ();
317
+ await gesture.up ();
318
+ await gesture.removePointer ();
319
+ await tester.pumpAndSettle ();
320
+ expect (controller.selection, const TextSelection .collapsed (offset: 2 ));
321
+ expect (find.text ('Cut' ), findsNothing);
322
+ expect (find.text ('Copy' ), findsNothing);
323
+ expect (find.text ('Paste' ), findsOneWidget);
324
+ expect (find.text ('Select all' ), findsOneWidget);
325
+
326
+ // Double tap to select the first word, then right click to show the menu.
327
+ final Offset startBlah1 = textOffsetToPosition (tester, 0 );
328
+ gesture = await tester.startGesture (
329
+ startBlah1,
330
+ kind: PointerDeviceKind .mouse,
331
+ );
332
+ await tester.pump ();
333
+ await gesture.up ();
334
+ await tester.pump (const Duration (milliseconds: 100 ));
335
+ await gesture.down (startBlah1);
336
+ await tester.pump ();
337
+ await gesture.up ();
338
+ await gesture.removePointer ();
339
+ await tester.pumpAndSettle ();
340
+ expect (controller.selection, const TextSelection (baseOffset: 0 , extentOffset: 5 ));
341
+ expect (find.text ('Cut' ), findsNothing);
342
+ expect (find.text ('Copy' ), findsNothing);
343
+ expect (find.text ('Paste' ), findsNothing);
344
+ expect (find.text ('Select all' ), findsNothing);
345
+ gesture = await tester.startGesture (
346
+ midBlah1,
347
+ kind: PointerDeviceKind .mouse,
348
+ buttons: kSecondaryMouseButton,
349
+ );
350
+ await tester.pump ();
351
+ await gesture.up ();
352
+ await gesture.removePointer ();
353
+ await tester.pumpAndSettle ();
354
+ expect (controller.selection, const TextSelection (baseOffset: 0 , extentOffset: 5 ));
355
+ expect (find.text ('Cut' ), findsOneWidget);
356
+ expect (find.text ('Copy' ), findsOneWidget);
357
+ expect (find.text ('Paste' ), findsOneWidget);
358
+
359
+ // Copy the first word.
360
+ await tester.tap (find.text ('Copy' ));
361
+ await tester.pumpAndSettle ();
362
+ expect (controller.text, 'blah1 blah2' );
363
+ expect (controller.selection, const TextSelection (baseOffset: 5 , extentOffset: 5 ));
364
+ expect (find.byType (CupertinoButton ), findsNothing);
365
+
366
+ // Paste it at the end.
367
+ gesture = await tester.startGesture (
368
+ textOffsetToPosition (tester, controller.text.length),
369
+ kind: PointerDeviceKind .mouse,
370
+ );
371
+ await tester.pump ();
372
+ await gesture.up ();
373
+ await gesture.removePointer ();
374
+ expect (controller.selection, const TextSelection .collapsed (offset: 11 , affinity: TextAffinity .upstream));
375
+ gesture = await tester.startGesture (
376
+ textOffsetToPosition (tester, controller.text.length),
377
+ kind: PointerDeviceKind .mouse,
378
+ buttons: kSecondaryMouseButton,
379
+ );
380
+ await tester.pump ();
381
+ await gesture.up ();
382
+ await gesture.removePointer ();
383
+ await tester.pumpAndSettle ();
384
+ expect (controller.selection, const TextSelection .collapsed (offset: 11 , affinity: TextAffinity .upstream));
385
+ expect (find.text ('Cut' ), findsNothing);
386
+ expect (find.text ('Copy' ), findsNothing);
387
+ expect (find.text ('Paste' ), findsOneWidget);
388
+ await tester.tap (find.text ('Paste' ));
389
+ await tester.pumpAndSettle ();
390
+ expect (controller.text, 'blah1 blah2blah1' );
391
+ expect (controller.selection, const TextSelection .collapsed (offset: 16 ));
392
+
393
+ // Cut the first word.
394
+ gesture = await tester.startGesture (
395
+ midBlah1,
396
+ kind: PointerDeviceKind .mouse,
397
+ );
398
+ await tester.pump ();
399
+ await gesture.up ();
400
+ await tester.pump (const Duration (milliseconds: 100 ));
401
+ await gesture.down (startBlah1);
402
+ await tester.pump ();
403
+ await gesture.up ();
404
+ await gesture.removePointer ();
405
+ await tester.pumpAndSettle ();
406
+ expect (controller.selection, const TextSelection (baseOffset: 0 , extentOffset: 5 ));
407
+ expect (find.text ('Cut' ), findsNothing);
408
+ expect (find.text ('Copy' ), findsNothing);
409
+ expect (find.text ('Paste' ), findsNothing);
410
+ expect (find.text ('Select all' ), findsNothing);
411
+ gesture = await tester.startGesture (
412
+ textOffsetToPosition (tester, controller.text.length),
413
+ kind: PointerDeviceKind .mouse,
414
+ buttons: kSecondaryMouseButton,
415
+ );
416
+ await tester.pump ();
417
+ await gesture.up ();
418
+ await gesture.removePointer ();
419
+ await tester.pumpAndSettle ();
420
+ expect (controller.selection, const TextSelection (baseOffset: 0 , extentOffset: 5 ));
421
+ expect (find.text ('Cut' ), findsOneWidget);
422
+ expect (find.text ('Copy' ), findsOneWidget);
423
+ expect (find.text ('Paste' ), findsOneWidget);
424
+ await tester.tap (find.text ('Cut' ));
425
+ await tester.pumpAndSettle ();
426
+ expect (controller.text, ' blah2blah1' );
427
+ expect (controller.selection, const TextSelection (baseOffset: 0 , extentOffset: 0 ));
428
+ expect (find.byType (CupertinoButton ), findsNothing);
429
+ },
430
+ variant: const TargetPlatformVariant (< TargetPlatform > { TargetPlatform .linux, TargetPlatform .windows }),
286
431
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
287
432
);
288
433
@@ -10057,9 +10202,9 @@ void main() {
10057
10202
await tester.pumpAndSettle ();
10058
10203
10059
10204
expect (RendererBinding .instance.mouseTracker.debugDeviceActiveCursor (1 ), SystemMouseCursors .text);
10060
- expect (find.text ('Copy ' ), findsOneWidget);
10205
+ expect (find.text ('Paste ' ), findsOneWidget);
10061
10206
10062
- await gesture.moveTo (tester.getCenter (find.text ('Copy ' )));
10207
+ await gesture.moveTo (tester.getCenter (find.text ('Paste ' )));
10063
10208
expect (RendererBinding .instance.mouseTracker.debugDeviceActiveCursor (1 ), SystemMouseCursors .basic);
10064
10209
},
10065
10210
variant: TargetPlatformVariant .desktop (),
@@ -11093,4 +11238,89 @@ void main() {
11093
11238
expect (controller.selection.baseOffset, 23 );
11094
11239
expect (controller.selection.extentOffset, 14 );
11095
11240
}, variant: const TargetPlatformVariant (< TargetPlatform > { TargetPlatform .linux, TargetPlatform .android, TargetPlatform .fuchsia, TargetPlatform .windows }));
11241
+
11242
+ // Regression test for https://github.com/flutter/flutter/issues/101587.
11243
+ testWidgets ('Right clicking menu behavior' , (WidgetTester tester) async {
11244
+ final TextEditingController controller = TextEditingController (
11245
+ text: 'blah1 blah2' ,
11246
+ );
11247
+ await tester.pumpWidget (
11248
+ MaterialApp (
11249
+ home: Material (
11250
+ child: TextField (
11251
+ controller: controller,
11252
+ ),
11253
+ ),
11254
+ ),
11255
+ );
11256
+
11257
+ // Initially, the menu is not shown and there is no selection.
11258
+ expect (find.byType (CupertinoButton ), findsNothing);
11259
+ expect (controller.selection, const TextSelection (baseOffset: - 1 , extentOffset: - 1 ));
11260
+
11261
+ final Offset midBlah1 = textOffsetToPosition (tester, 2 );
11262
+ final Offset midBlah2 = textOffsetToPosition (tester, 8 );
11263
+
11264
+ // Right click the second word.
11265
+ final TestGesture gesture = await tester.startGesture (
11266
+ midBlah2,
11267
+ kind: PointerDeviceKind .mouse,
11268
+ buttons: kSecondaryMouseButton,
11269
+ );
11270
+ addTearDown (gesture.removePointer);
11271
+ await tester.pump ();
11272
+ await gesture.up ();
11273
+ await tester.pumpAndSettle ();
11274
+
11275
+ switch (defaultTargetPlatform) {
11276
+ case TargetPlatform .iOS:
11277
+ case TargetPlatform .macOS:
11278
+ expect (controller.selection, const TextSelection (baseOffset: 6 , extentOffset: 11 ));
11279
+ expect (find.text ('Cut' ), findsOneWidget);
11280
+ expect (find.text ('Copy' ), findsOneWidget);
11281
+ expect (find.text ('Paste' ), findsOneWidget);
11282
+ break ;
11283
+
11284
+ case TargetPlatform .android:
11285
+ case TargetPlatform .fuchsia:
11286
+ case TargetPlatform .linux:
11287
+ case TargetPlatform .windows:
11288
+ expect (controller.selection, const TextSelection .collapsed (offset: 8 ));
11289
+ expect (find.text ('Cut' ), findsNothing);
11290
+ expect (find.text ('Copy' ), findsNothing);
11291
+ expect (find.text ('Paste' ), findsOneWidget);
11292
+ expect (find.text ('Select all' ), findsOneWidget);
11293
+ break ;
11294
+ }
11295
+
11296
+ // Right click the first word.
11297
+ await gesture.down (midBlah1);
11298
+ await tester.pump ();
11299
+ await gesture.up ();
11300
+ await tester.pumpAndSettle ();
11301
+
11302
+ switch (defaultTargetPlatform) {
11303
+ case TargetPlatform .iOS:
11304
+ case TargetPlatform .macOS:
11305
+ expect (controller.selection, const TextSelection (baseOffset: 0 , extentOffset: 5 ));
11306
+ expect (find.text ('Cut' ), findsOneWidget);
11307
+ expect (find.text ('Copy' ), findsOneWidget);
11308
+ expect (find.text ('Paste' ), findsOneWidget);
11309
+ break ;
11310
+
11311
+ case TargetPlatform .android:
11312
+ case TargetPlatform .fuchsia:
11313
+ case TargetPlatform .linux:
11314
+ case TargetPlatform .windows:
11315
+ expect (controller.selection, const TextSelection .collapsed (offset: 8 ));
11316
+ expect (find.text ('Cut' ), findsNothing);
11317
+ expect (find.text ('Copy' ), findsNothing);
11318
+ expect (find.text ('Paste' ), findsNothing);
11319
+ expect (find.text ('Select all' ), findsNothing);
11320
+ break ;
11321
+ }
11322
+ },
11323
+ variant: TargetPlatformVariant .all (),
11324
+ skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
11325
+ );
11096
11326
}
0 commit comments