Unverified Commit 4c1d887e authored by Renzo Olivares's avatar Renzo Olivares Committed by GitHub

Set selection on tap down for desktop platforms and tap up for mobile (#105505)

Co-authored-by: 's avatarRenzo Olivares <roliv@google.com>
parent 2c13db3b
...@@ -1454,6 +1454,9 @@ class TextSelectionGestureDetectorBuilder { ...@@ -1454,6 +1454,9 @@ class TextSelectionGestureDetectorBuilder {
/// * [TextSelectionGestureDetector.onTapDown], which triggers this callback. /// * [TextSelectionGestureDetector.onTapDown], which triggers this callback.
@protected @protected
void onTapDown(TapDownDetails details) { void onTapDown(TapDownDetails details) {
if (!delegate.selectionEnabled) {
return;
}
renderEditable.handleTapDown(details); renderEditable.handleTapDown(details);
// The selection overlay should only be shown when the user is interacting // The selection overlay should only be shown when the user is interacting
// through a touch screen (via either a finger or a stylus). A mouse shouldn't // through a touch screen (via either a finger or a stylus). A mouse shouldn't
...@@ -1465,13 +1468,21 @@ class TextSelectionGestureDetectorBuilder { ...@@ -1465,13 +1468,21 @@ class TextSelectionGestureDetectorBuilder {
|| kind == PointerDeviceKind.stylus; || kind == PointerDeviceKind.stylus;
// Handle shift + click selection if needed. // Handle shift + click selection if needed.
if (_isShiftPressed && renderEditable.selection?.baseOffset != null) { final bool isShiftPressedValid = _isShiftPressed && renderEditable.selection?.baseOffset != null;
_isShiftTapping = true;
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.iOS: case TargetPlatform.iOS:
// On mobile platforms the selection is set on tap up.
if (_isShiftTapping) {
_isShiftTapping = false;
}
break;
case TargetPlatform.macOS: case TargetPlatform.macOS:
// On these platforms, a shift-tapped unfocused field expands from 0, // On macOS, a shift-tapped unfocused field expands from 0, not from the
// not from the previous selection. // previous selection.
if (isShiftPressedValid) {
_isShiftTapping = true;
final TextSelection? fromSelection = renderEditable.hasFocus final TextSelection? fromSelection = renderEditable.hasFocus
? null ? null
: const TextSelection.collapsed(offset: 0); : const TextSelection.collapsed(offset: 0);
...@@ -1480,14 +1491,23 @@ class TextSelectionGestureDetectorBuilder { ...@@ -1480,14 +1491,23 @@ class TextSelectionGestureDetectorBuilder {
SelectionChangedCause.tap, SelectionChangedCause.tap,
fromSelection, fromSelection,
); );
return;
}
// On macOS, a tap/click places the selection in a precise position.
// This differs from iOS/iPadOS, where if the gesture is done by a touch
// then the selection moves to the closest word edge, instead of a
// precise position.
renderEditable.selectPosition(cause: SelectionChangedCause.tap);
break; break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux: case TargetPlatform.linux:
case TargetPlatform.windows: case TargetPlatform.windows:
if (isShiftPressedValid) {
_isShiftTapping = true;
_extendSelection(details.globalPosition, SelectionChangedCause.tap); _extendSelection(details.globalPosition, SelectionChangedCause.tap);
break; return;
} }
renderEditable.selectPosition(cause: SelectionChangedCause.tap);
break;
} }
} }
...@@ -1547,15 +1567,42 @@ class TextSelectionGestureDetectorBuilder { ...@@ -1547,15 +1567,42 @@ class TextSelectionGestureDetectorBuilder {
/// this callback. /// this callback.
@protected @protected
void onSingleTapUp(TapUpDetails details) { void onSingleTapUp(TapUpDetails details) {
if (delegate.selectionEnabled) {
// Handle shift + click selection if needed.
final bool isShiftPressedValid = _isShiftPressed && renderEditable.selection?.baseOffset != null;
switch (defaultTargetPlatform) {
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
// On desktop platforms the selection is set on tap down.
if (_isShiftTapping) { if (_isShiftTapping) {
_isShiftTapping = false; _isShiftTapping = false;
}
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
if (isShiftPressedValid) {
_isShiftTapping = true;
_extendSelection(details.globalPosition, SelectionChangedCause.tap);
return; return;
} }
renderEditable.selectPosition(cause: SelectionChangedCause.tap);
if (delegate.selectionEnabled) { break;
switch (defaultTargetPlatform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
case TargetPlatform.macOS: if (isShiftPressedValid) {
// On iOS, a shift-tapped unfocused field expands from 0, not from
// the previous selection.
_isShiftTapping = true;
final TextSelection? fromSelection = renderEditable.hasFocus
? null
: const TextSelection.collapsed(offset: 0);
_expandSelection(
details.globalPosition,
SelectionChangedCause.tap,
fromSelection,
);
return;
}
switch (details.kind) { switch (details.kind) {
case PointerDeviceKind.mouse: case PointerDeviceKind.mouse:
case PointerDeviceKind.trackpad: case PointerDeviceKind.trackpad:
...@@ -1566,18 +1613,11 @@ class TextSelectionGestureDetectorBuilder { ...@@ -1566,18 +1613,11 @@ class TextSelectionGestureDetectorBuilder {
break; break;
case PointerDeviceKind.touch: case PointerDeviceKind.touch:
case PointerDeviceKind.unknown: case PointerDeviceKind.unknown:
// On macOS/iOS/iPadOS a touch tap places the cursor at the edge // On iOS/iPadOS a touch tap places the cursor at the edge of the word.
// of the word.
renderEditable.selectWordEdge(cause: SelectionChangedCause.tap); renderEditable.selectWordEdge(cause: SelectionChangedCause.tap);
break; break;
} }
break; break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
renderEditable.selectPosition(cause: SelectionChangedCause.tap);
break;
} }
} }
} }
......
...@@ -1755,7 +1755,7 @@ void main() { ...@@ -1755,7 +1755,7 @@ void main() {
// But don't trigger the toolbar. // But don't trigger the toolbar.
expect(find.byType(CupertinoButton), findsNothing); expect(find.byType(CupertinoButton), findsNothing);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
testWidgets( testWidgets(
'slow double tap does not trigger double tap', 'slow double tap does not trigger double tap',
...@@ -1763,6 +1763,9 @@ void main() { ...@@ -1763,6 +1763,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -1773,18 +1776,16 @@ void main() { ...@@ -1773,18 +1776,16 @@ void main() {
), ),
); );
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); final Offset pos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'.
await tester.tapAt(textFieldStart + const Offset(50.0, 5.0)); await tester.tapAt(pos);
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
await tester.tapAt(textFieldStart + const Offset(50.0, 5.0)); await tester.tapAt(pos);
await tester.pump(); await tester.pump();
// Plain collapsed selection. // Plain collapsed selection.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
);
// No toolbar. // No toolbar.
expect(find.byType(CupertinoButton), findsNothing); expect(find.byType(CupertinoButton), findsNothing);
...@@ -1893,6 +1894,9 @@ void main() { ...@@ -1893,6 +1894,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -1903,19 +1907,20 @@ void main() { ...@@ -1903,19 +1907,20 @@ void main() {
), ),
); );
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); final Offset ePos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'.
final Offset pPos = textOffsetToPosition(tester, 9); // Index of 'P|eel'.
await tester.tapAt(textFieldStart + const Offset(50.0, 5.0));
await tester.tapAt(ePos);
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap moved the cursor. // First tap moved the cursor.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
const TextSelection.collapsed(offset: 8),
); await tester.tapAt(pPos);
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Second tap selects the word around the cursor. // Second tap selects the word around the cursor.
...@@ -1979,6 +1984,9 @@ void main() { ...@@ -1979,6 +1984,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -1989,29 +1997,27 @@ void main() { ...@@ -1989,29 +1997,27 @@ void main() {
), ),
); );
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); final Offset pPos = textOffsetToPosition(tester, 9); // Index of 'P|eel'.
final Offset ePos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap moved the cursor. // First tap moved the cursor.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
const TextSelection.collapsed(offset: 8),
); await tester.tapAt(pPos);
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0));
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
await tester.tapAt(textFieldStart + const Offset(100.0, 5.0)); await tester.tapAt(ePos);
await tester.pump(); await tester.pump();
// Plain collapsed selection at the edge of first word. In iOS 12, the // Plain collapsed selection at the edge of first word. In iOS 12, the
// first tap after a double tap ends up putting the cursor at where // first tap after a double tap ends up putting the cursor at where
// you tapped instead of the edge like every other single tap. This is // you tapped instead of the edge like every other single tap. This is
// likely a bug in iOS 12 and not present in other versions. // likely a bug in iOS 12 and not present in other versions.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
);
// No toolbar. // No toolbar.
expect(find.byType(CupertinoButton), findsNothing); expect(find.byType(CupertinoButton), findsNothing);
...@@ -2429,6 +2435,9 @@ void main() { ...@@ -2429,6 +2435,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -2439,20 +2448,18 @@ void main() { ...@@ -2439,20 +2448,18 @@ void main() {
), ),
); );
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); final Offset ePos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'
await tester.longPressAt(textFieldStart + const Offset(50.0, 5.0)); await tester.longPressAt(ePos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
await tester.tapAt(textFieldStart + const Offset(50.0, 5.0)); await tester.tapAt(ePos);
await tester.pump(); await tester.pump();
// We ended up moving the cursor to the edge of the same word and dismissed // We ended up moving the cursor to the edge of the same word and dismissed
// the toolbar. // the toolbar.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
);
// The toolbar from the long press is now dismissed by the second tap. // The toolbar from the long press is now dismissed by the second tap.
expect(find.byType(CupertinoButton), findsNothing); expect(find.byType(CupertinoButton), findsNothing);
...@@ -2615,6 +2622,9 @@ void main() { ...@@ -2615,6 +2622,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -2625,25 +2635,24 @@ void main() { ...@@ -2625,25 +2635,24 @@ void main() {
), ),
); );
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); final Offset pPos = textOffsetToPosition(tester, 9); // Index of 'P|eel'
final Offset ePos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap moved the cursor to the beginning of the second word. // First tap moved the cursor to the beginning of the second word.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
const TextSelection.collapsed(offset: 8), await tester.tapAt(pPos);
);
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0));
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
await tester.longPressAt(textFieldStart + const Offset(100.0, 5.0)); await tester.longPressAt(ePos);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Plain collapsed selection at the exact tap position. // Plain collapsed selection at the exact tap position.
expect( expect(
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 6, affinity: TextAffinity.upstream), const TextSelection.collapsed(offset: 6),
); );
// Long press toolbar. // Long press toolbar.
...@@ -2656,6 +2665,9 @@ void main() { ...@@ -2656,6 +2665,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -2666,19 +2678,18 @@ void main() { ...@@ -2666,19 +2678,18 @@ void main() {
), ),
); );
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); final Offset pPos = textOffsetToPosition(tester, 9); // Index of 'P|eel'
final Offset wPos = textOffsetToPosition(tester, 3); // Index of 'Atw|ater'
await tester.longPressAt(textFieldStart + const Offset(50.0, 5.0)); await tester.longPressAt(wPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap moved the cursor. // First tap moved the cursor.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
const TextSelection.collapsed(offset: 8), await tester.tapAt(pPos);
);
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Double tap selection. // Double tap selection.
...@@ -2752,7 +2763,7 @@ void main() { ...@@ -2752,7 +2763,7 @@ void main() {
const TextSelection(baseOffset: 8, extentOffset: 12), const TextSelection(baseOffset: 8, extentOffset: 12),
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3)); expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3));
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
testWidgets('force press selects word', (WidgetTester tester) async { testWidgets('force press selects word', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
...@@ -2798,6 +2809,9 @@ void main() { ...@@ -2798,6 +2809,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -2808,15 +2822,15 @@ void main() { ...@@ -2808,15 +2822,15 @@ void main() {
), ),
); );
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField)); final Offset pPos = textOffsetToPosition(tester, 9); // Index of 'P|eel'
final int pointerValue = tester.nextPointer; final int pointerValue = tester.nextPointer;
final TestGesture gesture = await tester.createGesture(); final TestGesture gesture = await tester.createGesture();
await gesture.downWithCustomEvent( await gesture.downWithCustomEvent(
textFieldStart + const Offset(150.0, 5.0), pPos,
PointerDownEvent( PointerDownEvent(
pointer: pointerValue, pointer: pointerValue,
position: textFieldStart + const Offset(150.0, 5.0), position: pPos,
// iPhone 6 and below report 0 across the board. // iPhone 6 and below report 0 across the board.
pressure: 0, pressure: 0,
pressureMax: 0, pressureMax: 0,
...@@ -2824,11 +2838,10 @@ void main() { ...@@ -2824,11 +2838,10 @@ void main() {
), ),
); );
await gesture.up(); await gesture.up();
// Fall back to a single tap which selects the edge of the word. // Fall back to a single tap which selects the edge of the word on iOS, and
expect( // a precise position on macOS.
controller.selection, expect(controller.selection.isCollapsed, isTrue);
const TextSelection.collapsed(offset: 8), expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
);
await tester.pump(); await tester.pump();
// Falling back to a single tap doesn't trigger a toolbar. // Falling back to a single tap doesn't trigger a toolbar.
...@@ -2839,6 +2852,9 @@ void main() { ...@@ -2839,6 +2852,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'abc def ghi', text: 'abc def ghi',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
...@@ -2860,7 +2876,7 @@ void main() { ...@@ -2860,7 +2876,7 @@ void main() {
await tester.tapAt(ePos, pointer: 7); await tester.tapAt(ePos, pointer: 7);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
expect(controller.selection.isCollapsed, isTrue); expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, 4); expect(controller.selection.baseOffset, isTargetPlatformMobile ? 4 : 5);
await tester.tapAt(ePos, pointer: 7); await tester.tapAt(ePos, pointer: 7);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 4); expect(controller.selection.baseOffset, 4);
...@@ -2897,6 +2913,103 @@ void main() { ...@@ -2897,6 +2913,103 @@ void main() {
expect(controller.selection.extentOffset, 5); expect(controller.selection.extentOffset, 5);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('Selection updates on tap down (Desktop platforms)', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController();
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoTextField(controller: controller),
),
),
);
const String testValue = 'abc def ghi';
await tester.enterText(find.byType(CupertinoTextField), testValue);
// Skip past scrolling animation.
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
final Offset ePos = textOffsetToPosition(tester, 5);
final Offset gPos = textOffsetToPosition(tester, 8);
final TestGesture gesture = await tester.startGesture(ePos, kind: PointerDeviceKind.mouse);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 5);
expect(controller.selection.extentOffset, 5);
await gesture.up();
await tester.pumpAndSettle(kDoubleTapTimeout);
await gesture.down(gPos);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 8);
// This should do nothing. The selection is set on tap down on desktop platforms.
await gesture.up();
expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 8);
},
variant: TargetPlatformVariant.desktop(),
);
testWidgets('Selection updates on tap up (Mobile platforms)', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController();
final bool isTargetPlatformApple = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget(
CupertinoApp(
home: Center(
child: CupertinoTextField(controller: controller),
),
),
);
const String testValue = 'abc def ghi';
await tester.enterText(find.byType(CupertinoTextField), testValue);
// Skip past scrolling animation.
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
final Offset ePos = textOffsetToPosition(tester, 5);
final Offset gPos = textOffsetToPosition(tester, 8);
final TestGesture gesture = await tester.startGesture(ePos, kind: PointerDeviceKind.mouse);
await gesture.up();
await tester.pumpAndSettle(kDoubleTapTimeout);
await gesture.down(gPos);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 5);
expect(controller.selection.extentOffset, 5);
await gesture.up();
await tester.pumpAndSettle(kDoubleTapTimeout);
expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 8);
final TestGesture touchGesture = await tester.startGesture(ePos);
await touchGesture.up();
await tester.pumpAndSettle(kDoubleTapTimeout);
// On iOS, a tap to select, selects the word edge instead of the exact tap position.
expect(controller.selection.baseOffset, isTargetPlatformApple ? 4 : 5);
expect(controller.selection.extentOffset, isTargetPlatformApple ? 4 : 5);
// Selection should stay the same since it is set on tap up for mobile platforms.
await touchGesture.down(gPos);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, isTargetPlatformApple ? 4 : 5);
expect(controller.selection.extentOffset, isTargetPlatformApple ? 4 : 5);
await touchGesture.up();
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 8);
},
variant: TargetPlatformVariant.mobile(),
);
testWidgets('Can select text by dragging with a mouse', (WidgetTester tester) async { testWidgets('Can select text by dragging with a mouse', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
...@@ -5191,6 +5304,7 @@ void main() { ...@@ -5191,6 +5304,7 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -5214,11 +5328,17 @@ void main() { ...@@ -5214,11 +5328,17 @@ void main() {
pointer: 7, pointer: 7,
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
if (isTargetPlatformMobile) {
await gesture.up();
}
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8); expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 23); expect(controller.selection.extentOffset, 23);
// Expand the selection a bit. // Expand the selection a bit.
if (isTargetPlatformMobile) {
await gesture.down(textOffsetToPosition(tester, 24));
}
await gesture.moveTo(textOffsetToPosition(tester, 28)); await gesture.moveTo(textOffsetToPosition(tester, 28));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8); expect(controller.selection.baseOffset, 8);
...@@ -5287,6 +5407,8 @@ void main() { ...@@ -5287,6 +5407,8 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android
|| defaultTargetPlatform == TargetPlatform.fuchsia;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -5310,11 +5432,17 @@ void main() { ...@@ -5310,11 +5432,17 @@ void main() {
pointer: 7, pointer: 7,
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
if (isTargetPlatformMobile) {
await gesture.up();
}
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8); expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 23); expect(controller.selection.extentOffset, 23);
// Expand the selection a bit. // Expand the selection a bit.
if (isTargetPlatformMobile) {
await gesture.down(textOffsetToPosition(tester, 24));
}
await gesture.moveTo(textOffsetToPosition(tester, 28)); await gesture.moveTo(textOffsetToPosition(tester, 28));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8); expect(controller.selection.baseOffset, 8);
...@@ -5383,6 +5511,7 @@ void main() { ...@@ -5383,6 +5511,7 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -5406,11 +5535,17 @@ void main() { ...@@ -5406,11 +5535,17 @@ void main() {
pointer: 7, pointer: 7,
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
if (isTargetPlatformMobile) {
await gesture.up();
}
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 23); expect(controller.selection.baseOffset, 23);
expect(controller.selection.extentOffset, 8); expect(controller.selection.extentOffset, 8);
// Expand the selection a bit. // Expand the selection a bit.
if (isTargetPlatformMobile) {
await gesture.down(textOffsetToPosition(tester, 7));
}
await gesture.moveTo(textOffsetToPosition(tester, 5)); await gesture.moveTo(textOffsetToPosition(tester, 5));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 23); expect(controller.selection.baseOffset, 23);
...@@ -5479,6 +5614,8 @@ void main() { ...@@ -5479,6 +5614,8 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android
|| defaultTargetPlatform == TargetPlatform.fuchsia;
await tester.pumpWidget( await tester.pumpWidget(
CupertinoApp( CupertinoApp(
home: Center( home: Center(
...@@ -5502,11 +5639,17 @@ void main() { ...@@ -5502,11 +5639,17 @@ void main() {
pointer: 7, pointer: 7,
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
if (isTargetPlatformMobile) {
await gesture.up();
}
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 23); expect(controller.selection.baseOffset, 23);
expect(controller.selection.extentOffset, 8); expect(controller.selection.extentOffset, 8);
// Expand the selection a bit. // Expand the selection a bit.
if (isTargetPlatformMobile) {
await gesture.down(textOffsetToPosition(tester, 7));
}
await gesture.moveTo(textOffsetToPosition(tester, 5)); await gesture.moveTo(textOffsetToPosition(tester, 5));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 23); expect(controller.selection.baseOffset, 23);
......
...@@ -475,8 +475,6 @@ void main() { ...@@ -475,8 +475,6 @@ void main() {
final TestGesture down1 = await tester.startGesture(tester.getCenter(find.byType(TextField).first), kind: PointerDeviceKind.mouse); final TestGesture down1 = await tester.startGesture(tester.getCenter(find.byType(TextField).first), kind: PointerDeviceKind.mouse);
await tester.pump(); await tester.pump();
await tester.pumpAndSettle();
await down1.up();
await down1.removePointer(); await down1.removePointer();
expect(focusNodeA.hasFocus, true); expect(focusNodeA.hasFocus, true);
......
...@@ -908,6 +908,9 @@ void main() { ...@@ -908,6 +908,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -933,7 +936,7 @@ void main() { ...@@ -933,7 +936,7 @@ void main() {
// First tap moved the cursor. // First tap moved the cursor.
expect( expect(
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 8), TextSelection.collapsed(offset: isTargetPlatformMobile ? 8 : 9),
); );
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(textfieldStart + const Offset(150.0, 9.0));
await tester.pump(); await tester.pump();
...@@ -1084,12 +1087,12 @@ void main() { ...@@ -1084,12 +1087,12 @@ void main() {
), ),
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset pos = textOffsetToPosition(tester, 9); // Index of 'P|eel'
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pos);
await tester.pump(); await tester.pump();
// Selected text shows 'Copy', and not 'Paste', 'Cut', 'Select all'. // Selected text shows 'Copy', and not 'Paste', 'Cut', 'Select all'.
...@@ -1901,6 +1904,99 @@ void main() { ...@@ -1901,6 +1904,99 @@ void main() {
expect(controller.selection.baseOffset, testValue.length); expect(controller.selection.baseOffset, testValue.length);
}); });
testWidgets('Selection updates on tap down (Desktop platforms)', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController();
await tester.pumpWidget(
MaterialApp(
home: Material(
child: TextField(controller: controller),
),
),
);
const String testValue = 'abc def ghi';
await tester.enterText(find.byType(TextField), testValue);
await skipPastScrollingAnimation(tester);
final Offset ePos = textOffsetToPosition(tester, 5);
final Offset gPos = textOffsetToPosition(tester, 8);
final TestGesture gesture = await tester.startGesture(ePos, kind: PointerDeviceKind.mouse);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 5);
expect(controller.selection.extentOffset, 5);
await gesture.up();
await tester.pumpAndSettle(kDoubleTapTimeout);
await gesture.down(gPos);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 8);
// This should do nothing. The selection is set on tap down on desktop platforms.
await gesture.up();
expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 8);
},
variant: TargetPlatformVariant.desktop(),
);
testWidgets('Selection updates on tap up (Mobile platforms)', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController();
final bool isTargetPlatformApple = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget(
MaterialApp(
home: Material(
child: TextField(controller: controller),
),
),
);
const String testValue = 'abc def ghi';
await tester.enterText(find.byType(TextField), testValue);
await skipPastScrollingAnimation(tester);
final Offset ePos = textOffsetToPosition(tester, 5);
final Offset gPos = textOffsetToPosition(tester, 8);
final TestGesture gesture = await tester.startGesture(ePos, kind: PointerDeviceKind.mouse);
await gesture.up();
await tester.pumpAndSettle(kDoubleTapTimeout);
await gesture.down(gPos);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 5);
expect(controller.selection.extentOffset, 5);
await gesture.up();
await tester.pumpAndSettle(kDoubleTapTimeout);
expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 8);
final TestGesture touchGesture = await tester.startGesture(ePos);
await touchGesture.up();
await tester.pumpAndSettle(kDoubleTapTimeout);
// On iOS a tap to select, selects the word edge instead of the exact tap position.
expect(controller.selection.baseOffset, isTargetPlatformApple ? 4 : 5);
expect(controller.selection.extentOffset, isTargetPlatformApple ? 4 : 5);
// Selection should stay the same since it is set on tap up for mobile platforms.
await touchGesture.down(gPos);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, isTargetPlatformApple ? 4 : 5);
expect(controller.selection.extentOffset, isTargetPlatformApple ? 4 : 5);
await touchGesture.up();
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 8);
},
variant: TargetPlatformVariant.mobile(),
);
testWidgets('Can select text by dragging with a mouse', (WidgetTester tester) async { testWidgets('Can select text by dragging with a mouse', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
...@@ -7110,7 +7206,7 @@ void main() { ...@@ -7110,7 +7206,7 @@ void main() {
// But don't trigger the toolbar. // But don't trigger the toolbar.
expect(find.byType(CupertinoButton), findsNothing); expect(find.byType(CupertinoButton), findsNothing);
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
testWidgets( testWidgets(
...@@ -7186,6 +7282,9 @@ void main() { ...@@ -7186,6 +7282,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -7198,18 +7297,16 @@ void main() { ...@@ -7198,18 +7297,16 @@ void main() {
), ),
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset pos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'.
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(pos);
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(pos);
await tester.pump(); await tester.pump();
// Plain collapsed selection. // Plain collapsed selection.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
);
// No toolbar. // No toolbar.
expect(find.byType(CupertinoButton), findsNothing); expect(find.byType(CupertinoButton), findsNothing);
...@@ -7223,6 +7320,9 @@ void main() { ...@@ -7223,6 +7320,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -7235,21 +7335,22 @@ void main() { ...@@ -7235,21 +7335,22 @@ void main() {
), ),
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset pPos = textOffsetToPosition(tester, 9); // Index of 'P|eel'.
final Offset wPos = textOffsetToPosition(tester, 3); // Index of 'Atw|ater'.
// This tap just puts the cursor somewhere different than where the double // This tap just puts the cursor somewhere different than where the double
// tap will occur to test that the double tap moves the existing cursor first. // tap will occur to test that the double tap moves the existing cursor first.
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(wPos);
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap moved the cursor. // First tap moved the cursor.
expect( expect(
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 8), TextSelection.collapsed(offset: isTargetPlatformMobile ? 8 : 9),
); );
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pPos);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Second tap selects the word around the cursor. // Second tap selects the word around the cursor.
...@@ -7261,7 +7362,7 @@ void main() { ...@@ -7261,7 +7362,7 @@ void main() {
// Selected text shows 3 toolbar buttons. // Selected text shows 3 toolbar buttons.
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3)); expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3));
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
testWidgets( testWidgets(
...@@ -7679,6 +7780,9 @@ void main() { ...@@ -7679,6 +7780,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -7691,29 +7795,28 @@ void main() { ...@@ -7691,29 +7795,28 @@ void main() {
), ),
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset pPos = textOffsetToPosition(tester, 9); // Index of 'P|eel'.
final Offset ePos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap moved the cursor. // First tap moved the cursor.
expect( expect(
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 8), TextSelection.collapsed(offset: isTargetPlatformMobile ? 8 : 9),
); );
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
await tester.tapAt(textfieldStart + const Offset(100.0, 9.0)); await tester.tapAt(ePos);
await tester.pump(); await tester.pump();
// Plain collapsed selection at the edge of first word. In iOS 12, the // Plain collapsed selection at the edge of first word on iOS. In iOS 12,
// first tap after a double tap ends up putting the cursor at where // the first tap after a double tap ends up putting the cursor at where
// you tapped instead of the edge like every other single tap. This is // you tapped instead of the edge like every other single tap. This is
// likely a bug in iOS 12 and not present in other versions. // likely a bug in iOS 12 and not present in other versions.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
);
// No toolbar. // No toolbar.
expect(find.byType(CupertinoButton), findsNothing); expect(find.byType(CupertinoButton), findsNothing);
...@@ -7796,6 +7899,9 @@ void main() { ...@@ -7796,6 +7899,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -7808,20 +7914,18 @@ void main() { ...@@ -7808,20 +7914,18 @@ void main() {
), ),
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset ePos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'
await tester.longPressAt(textfieldStart + const Offset(50.0, 9.0)); await tester.longPressAt(ePos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
await tester.tapAt(textfieldStart + const Offset(50.0, 9.0)); await tester.tapAt(ePos);
await tester.pump(); await tester.pump();
// We ended up moving the cursor to the edge of the same word and dismissed // We ended up moving the cursor to the edge of the same word and dismissed
// the toolbar. // the toolbar.
expect( expect(controller.selection.isCollapsed, isTrue);
controller.selection, expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
);
// Collapsed toolbar shows 2 buttons. // Collapsed toolbar shows 2 buttons.
expect(find.byType(CupertinoButton), findsNothing); expect(find.byType(CupertinoButton), findsNothing);
...@@ -8358,6 +8462,9 @@ void main() { ...@@ -8358,6 +8462,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -8370,19 +8477,20 @@ void main() { ...@@ -8370,19 +8477,20 @@ void main() {
), ),
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset pPos = textOffsetToPosition(tester, 9); // Index of 'P|eel'
final Offset ePos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap moved the cursor to the beginning of the second word. // First tap moved the cursor to the beginning of the second word.
expect( expect(
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 8), TextSelection.collapsed(offset: isTargetPlatformMobile ? 8 : 9),
); );
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
await tester.longPressAt(textfieldStart + const Offset(100.0, 9.0)); await tester.longPressAt(ePos);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Plain collapsed selection at the exact tap position. // Plain collapsed selection at the exact tap position.
...@@ -8403,6 +8511,9 @@ void main() { ...@@ -8403,6 +8511,9 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
// On iOS/iPadOS, during a tap we select the edge of the word closest to the tap.
// On macOS, we select the precise position of the tap.
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -8415,23 +8526,24 @@ void main() { ...@@ -8415,23 +8526,24 @@ void main() {
), ),
); );
final Offset textfieldStart = tester.getTopLeft(find.byType(TextField)); final Offset pPos = textOffsetToPosition(tester, 9); // Index of 'P|eel'
final Offset wPos = textOffsetToPosition(tester, 3); // Index of 'Atw|ater'
await tester.longPressAt(textfieldStart + const Offset(50.0, 9.0)); await tester.longPressAt(wPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
expect( expect(
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 3), const TextSelection.collapsed(offset: 3),
); );
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pPos);
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
// First tap moved the cursor. // First tap moved the cursor.
expect( expect(
controller.selection, controller.selection,
const TextSelection.collapsed(offset: 8), TextSelection.collapsed(offset: isTargetPlatformMobile ? 8 : 9),
); );
await tester.tapAt(textfieldStart + const Offset(150.0, 9.0)); await tester.tapAt(pPos);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// Double tap selection. // Double tap selection.
...@@ -8568,7 +8680,7 @@ void main() { ...@@ -8568,7 +8680,7 @@ void main() {
); );
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3)); expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3));
}, },
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }), variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
); );
testWidgets( testWidgets(
...@@ -10959,6 +11071,7 @@ void main() { ...@@ -10959,6 +11071,7 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -10982,11 +11095,18 @@ void main() { ...@@ -10982,11 +11095,18 @@ void main() {
pointer: 7, pointer: 7,
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
if (isTargetPlatformMobile) {
await gesture.up();
}
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8); expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 23); expect(controller.selection.extentOffset, 23);
// Expand the selection a bit. // Expand the selection a bit.
if (isTargetPlatformMobile) {
await gesture.down(textOffsetToPosition(tester, 24));
}
await tester.pumpAndSettle();
await gesture.moveTo(textOffsetToPosition(tester, 28)); await gesture.moveTo(textOffsetToPosition(tester, 28));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8); expect(controller.selection.baseOffset, 8);
...@@ -11055,6 +11175,8 @@ void main() { ...@@ -11055,6 +11175,8 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android
|| defaultTargetPlatform == TargetPlatform.fuchsia;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -11078,11 +11200,17 @@ void main() { ...@@ -11078,11 +11200,17 @@ void main() {
pointer: 7, pointer: 7,
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
if (isTargetPlatformMobile) {
await gesture.up();
}
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8); expect(controller.selection.baseOffset, 8);
expect(controller.selection.extentOffset, 23); expect(controller.selection.extentOffset, 23);
// Expand the selection a bit. // Expand the selection a bit.
if (isTargetPlatformMobile) {
await gesture.down(textOffsetToPosition(tester, 23));
}
await gesture.moveTo(textOffsetToPosition(tester, 28)); await gesture.moveTo(textOffsetToPosition(tester, 28));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 8); expect(controller.selection.baseOffset, 8);
...@@ -11151,6 +11279,7 @@ void main() { ...@@ -11151,6 +11279,7 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -11174,11 +11303,17 @@ void main() { ...@@ -11174,11 +11303,17 @@ void main() {
pointer: 7, pointer: 7,
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
if (isTargetPlatformMobile) {
await gesture.up();
}
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 23); expect(controller.selection.baseOffset, 23);
expect(controller.selection.extentOffset, 8); expect(controller.selection.extentOffset, 8);
// Expand the selection a bit. // Expand the selection a bit.
if (isTargetPlatformMobile) {
await gesture.down(textOffsetToPosition(tester, 7));
}
await gesture.moveTo(textOffsetToPosition(tester, 5)); await gesture.moveTo(textOffsetToPosition(tester, 5));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 23); expect(controller.selection.baseOffset, 23);
...@@ -11247,6 +11382,8 @@ void main() { ...@@ -11247,6 +11382,8 @@ void main() {
final TextEditingController controller = TextEditingController( final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure', text: 'Atwater Peel Sherbrooke Bonaventure',
); );
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android
|| defaultTargetPlatform == TargetPlatform.fuchsia;
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
home: Material( home: Material(
...@@ -11270,11 +11407,17 @@ void main() { ...@@ -11270,11 +11407,17 @@ void main() {
pointer: 7, pointer: 7,
kind: PointerDeviceKind.mouse, kind: PointerDeviceKind.mouse,
); );
if (isTargetPlatformMobile) {
await gesture.up();
}
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 23); expect(controller.selection.baseOffset, 23);
expect(controller.selection.extentOffset, 8); expect(controller.selection.extentOffset, 8);
// Expand the selection a bit. // Expand the selection a bit.
if (isTargetPlatformMobile) {
await gesture.down(textOffsetToPosition(tester, 8));
}
await gesture.moveTo(textOffsetToPosition(tester, 5)); await gesture.moveTo(textOffsetToPosition(tester, 5));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 23); expect(controller.selection.baseOffset, 23);
......
...@@ -557,9 +557,9 @@ void main() { ...@@ -557,9 +557,9 @@ void main() {
switch (defaultTargetPlatform) { switch (defaultTargetPlatform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
case TargetPlatform.macOS:
expect(renderEditable.selectWordEdgeCalled, isTrue); expect(renderEditable.selectWordEdgeCalled, isTrue);
break; break;
case TargetPlatform.macOS:
case TargetPlatform.android: case TargetPlatform.android:
case TargetPlatform.fuchsia: case TargetPlatform.fuchsia:
case TargetPlatform.linux: case TargetPlatform.linux:
......
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