Set selection on tap down for desktop platforms and tap up for mobile (#105505)
Co-authored-by: Renzo Olivares <roliv@google.com>
This commit is contained in:
@@ -1454,6 +1454,9 @@ class TextSelectionGestureDetectorBuilder {
|
||||
/// * [TextSelectionGestureDetector.onTapDown], which triggers this callback.
|
||||
@protected
|
||||
void onTapDown(TapDownDetails details) {
|
||||
if (!delegate.selectionEnabled) {
|
||||
return;
|
||||
}
|
||||
renderEditable.handleTapDown(details);
|
||||
// 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
|
||||
@@ -1465,13 +1468,21 @@ class TextSelectionGestureDetectorBuilder {
|
||||
|| kind == PointerDeviceKind.stylus;
|
||||
|
||||
// Handle shift + click selection if needed.
|
||||
if (_isShiftPressed && renderEditable.selection?.baseOffset != null) {
|
||||
_isShiftTapping = true;
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
// On these platforms, a shift-tapped unfocused field expands from 0,
|
||||
// not from the previous selection.
|
||||
final bool isShiftPressedValid = _isShiftPressed && renderEditable.selection?.baseOffset != null;
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.iOS:
|
||||
// On mobile platforms the selection is set on tap up.
|
||||
if (_isShiftTapping) {
|
||||
_isShiftTapping = false;
|
||||
}
|
||||
break;
|
||||
case TargetPlatform.macOS:
|
||||
// On macOS, a shift-tapped unfocused field expands from 0, not from the
|
||||
// previous selection.
|
||||
if (isShiftPressedValid) {
|
||||
_isShiftTapping = true;
|
||||
final TextSelection? fromSelection = renderEditable.hasFocus
|
||||
? null
|
||||
: const TextSelection.collapsed(offset: 0);
|
||||
@@ -1480,14 +1491,23 @@ class TextSelectionGestureDetectorBuilder {
|
||||
SelectionChangedCause.tap,
|
||||
fromSelection,
|
||||
);
|
||||
break;
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
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;
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.windows:
|
||||
if (isShiftPressedValid) {
|
||||
_isShiftTapping = true;
|
||||
_extendSelection(details.globalPosition, SelectionChangedCause.tap);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
renderEditable.selectPosition(cause: SelectionChangedCause.tap);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1547,15 +1567,42 @@ class TextSelectionGestureDetectorBuilder {
|
||||
/// this callback.
|
||||
@protected
|
||||
void onSingleTapUp(TapUpDetails details) {
|
||||
if (_isShiftTapping) {
|
||||
_isShiftTapping = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (delegate.selectionEnabled) {
|
||||
// Handle shift + click selection if needed.
|
||||
final bool isShiftPressedValid = _isShiftPressed && renderEditable.selection?.baseOffset != null;
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.linux:
|
||||
case TargetPlatform.macOS:
|
||||
case TargetPlatform.windows:
|
||||
// On desktop platforms the selection is set on tap down.
|
||||
if (_isShiftTapping) {
|
||||
_isShiftTapping = false;
|
||||
}
|
||||
break;
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
if (isShiftPressedValid) {
|
||||
_isShiftTapping = true;
|
||||
_extendSelection(details.globalPosition, SelectionChangedCause.tap);
|
||||
return;
|
||||
}
|
||||
renderEditable.selectPosition(cause: SelectionChangedCause.tap);
|
||||
break;
|
||||
case TargetPlatform.iOS:
|
||||
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) {
|
||||
case PointerDeviceKind.mouse:
|
||||
case PointerDeviceKind.trackpad:
|
||||
@@ -1566,18 +1613,11 @@ class TextSelectionGestureDetectorBuilder {
|
||||
break;
|
||||
case PointerDeviceKind.touch:
|
||||
case PointerDeviceKind.unknown:
|
||||
// On macOS/iOS/iPadOS a touch tap places the cursor at the edge
|
||||
// of the word.
|
||||
// On iOS/iPadOS a touch tap places the cursor at the edge of the word.
|
||||
renderEditable.selectWordEdge(cause: SelectionChangedCause.tap);
|
||||
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() {
|
||||
|
||||
// But don't trigger the toolbar.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
|
||||
|
||||
testWidgets(
|
||||
'slow double tap does not trigger double tap',
|
||||
@@ -1763,6 +1763,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -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.tapAt(textFieldStart + const Offset(50.0, 5.0));
|
||||
await tester.tapAt(pos);
|
||||
await tester.pump();
|
||||
|
||||
// Plain collapsed selection.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
|
||||
);
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
|
||||
|
||||
// No toolbar.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
@@ -1893,6 +1894,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -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.tapAt(textFieldStart + const Offset(150.0, 5.0));
|
||||
await tester.tapAt(pPos);
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
// First tap moved the cursor.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 8),
|
||||
);
|
||||
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0));
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
|
||||
|
||||
await tester.tapAt(pPos);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Second tap selects the word around the cursor.
|
||||
@@ -1979,6 +1984,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -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));
|
||||
// First tap moved the cursor.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 8),
|
||||
);
|
||||
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0));
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
|
||||
|
||||
await tester.tapAt(pPos);
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
await tester.tapAt(textFieldStart + const Offset(100.0, 5.0));
|
||||
await tester.tapAt(ePos);
|
||||
await tester.pump();
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
|
||||
);
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
|
||||
|
||||
// No toolbar.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
@@ -2429,6 +2435,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -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.tapAt(textFieldStart + const Offset(50.0, 5.0));
|
||||
await tester.tapAt(ePos);
|
||||
await tester.pump();
|
||||
|
||||
// We ended up moving the cursor to the edge of the same word and dismissed
|
||||
// the toolbar.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
|
||||
);
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
|
||||
|
||||
// The toolbar from the long press is now dismissed by the second tap.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
@@ -2615,6 +2622,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -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));
|
||||
// First tap moved the cursor to the beginning of the second word.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 8),
|
||||
);
|
||||
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0));
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
|
||||
await tester.tapAt(pPos);
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
|
||||
await tester.longPressAt(textFieldStart + const Offset(100.0, 5.0));
|
||||
await tester.longPressAt(ePos);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Plain collapsed selection at the exact tap position.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 6, affinity: TextAffinity.upstream),
|
||||
const TextSelection.collapsed(offset: 6),
|
||||
);
|
||||
|
||||
// Long press toolbar.
|
||||
@@ -2656,6 +2665,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -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.tapAt(textFieldStart + const Offset(150.0, 5.0));
|
||||
await tester.tapAt(pPos);
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
// First tap moved the cursor.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 8),
|
||||
);
|
||||
await tester.tapAt(textFieldStart + const Offset(150.0, 5.0));
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
|
||||
await tester.tapAt(pPos);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Double tap selection.
|
||||
@@ -2752,7 +2763,7 @@ void main() {
|
||||
const TextSelection(baseOffset: 8, extentOffset: 12),
|
||||
);
|
||||
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 {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
@@ -2798,6 +2809,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -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 TestGesture gesture = await tester.createGesture();
|
||||
await gesture.downWithCustomEvent(
|
||||
textFieldStart + const Offset(150.0, 5.0),
|
||||
pPos,
|
||||
PointerDownEvent(
|
||||
pointer: pointerValue,
|
||||
position: textFieldStart + const Offset(150.0, 5.0),
|
||||
position: pPos,
|
||||
// iPhone 6 and below report 0 across the board.
|
||||
pressure: 0,
|
||||
pressureMax: 0,
|
||||
@@ -2824,11 +2838,10 @@ void main() {
|
||||
),
|
||||
);
|
||||
await gesture.up();
|
||||
// Fall back to a single tap which selects the edge of the word.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 8),
|
||||
);
|
||||
// Fall back to a single tap which selects the edge of the word on iOS, and
|
||||
// a precise position on macOS.
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 8 : 9);
|
||||
|
||||
await tester.pump();
|
||||
// Falling back to a single tap doesn't trigger a toolbar.
|
||||
@@ -2839,6 +2852,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
CupertinoApp(
|
||||
@@ -2860,7 +2876,7 @@ void main() {
|
||||
await tester.tapAt(ePos, pointer: 7);
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
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.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 4);
|
||||
@@ -2897,6 +2913,103 @@ void main() {
|
||||
expect(controller.selection.extentOffset, 5);
|
||||
}, 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 {
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
@@ -5191,6 +5304,7 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'Atwater Peel Sherbrooke Bonaventure',
|
||||
);
|
||||
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -5214,11 +5328,17 @@ void main() {
|
||||
pointer: 7,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.up();
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
expect(controller.selection.extentOffset, 23);
|
||||
|
||||
// Expand the selection a bit.
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.down(textOffsetToPosition(tester, 24));
|
||||
}
|
||||
await gesture.moveTo(textOffsetToPosition(tester, 28));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
@@ -5287,6 +5407,8 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'Atwater Peel Sherbrooke Bonaventure',
|
||||
);
|
||||
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android
|
||||
|| defaultTargetPlatform == TargetPlatform.fuchsia;
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -5310,11 +5432,17 @@ void main() {
|
||||
pointer: 7,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.up();
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
expect(controller.selection.extentOffset, 23);
|
||||
|
||||
// Expand the selection a bit.
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.down(textOffsetToPosition(tester, 24));
|
||||
}
|
||||
await gesture.moveTo(textOffsetToPosition(tester, 28));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
@@ -5383,6 +5511,7 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'Atwater Peel Sherbrooke Bonaventure',
|
||||
);
|
||||
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -5406,11 +5535,17 @@ void main() {
|
||||
pointer: 7,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.up();
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
expect(controller.selection.extentOffset, 8);
|
||||
|
||||
// Expand the selection a bit.
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.down(textOffsetToPosition(tester, 7));
|
||||
}
|
||||
await gesture.moveTo(textOffsetToPosition(tester, 5));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
@@ -5479,6 +5614,8 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'Atwater Peel Sherbrooke Bonaventure',
|
||||
);
|
||||
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android
|
||||
|| defaultTargetPlatform == TargetPlatform.fuchsia;
|
||||
await tester.pumpWidget(
|
||||
CupertinoApp(
|
||||
home: Center(
|
||||
@@ -5502,11 +5639,17 @@ void main() {
|
||||
pointer: 7,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.up();
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
expect(controller.selection.extentOffset, 8);
|
||||
|
||||
// Expand the selection a bit.
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.down(textOffsetToPosition(tester, 7));
|
||||
}
|
||||
await gesture.moveTo(textOffsetToPosition(tester, 5));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
|
||||
@@ -475,8 +475,6 @@ void main() {
|
||||
|
||||
final TestGesture down1 = await tester.startGesture(tester.getCenter(find.byType(TextField).first), kind: PointerDeviceKind.mouse);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
await down1.up();
|
||||
await down1.removePointer();
|
||||
|
||||
expect(focusNodeA.hasFocus, true);
|
||||
|
||||
@@ -908,6 +908,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -933,7 +936,7 @@ void main() {
|
||||
// First tap moved the cursor.
|
||||
expect(
|
||||
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.pump();
|
||||
@@ -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.tapAt(textfieldStart + const Offset(150.0, 9.0));
|
||||
await tester.tapAt(pos);
|
||||
await tester.pump();
|
||||
|
||||
// Selected text shows 'Copy', and not 'Paste', 'Cut', 'Select all'.
|
||||
@@ -1901,6 +1904,99 @@ void main() {
|
||||
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 {
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
@@ -7110,7 +7206,7 @@ void main() {
|
||||
// But don't trigger the toolbar.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
@@ -7186,6 +7282,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -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.tapAt(textfieldStart + const Offset(50.0, 9.0));
|
||||
await tester.tapAt(pos);
|
||||
await tester.pump();
|
||||
|
||||
// Plain collapsed selection.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
|
||||
);
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
|
||||
|
||||
// No toolbar.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
@@ -7223,6 +7320,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -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
|
||||
// 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.tapAt(textfieldStart + const Offset(150.0, 9.0));
|
||||
await tester.tapAt(pPos);
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
// First tap moved the cursor.
|
||||
expect(
|
||||
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();
|
||||
|
||||
// Second tap selects the word around the cursor.
|
||||
@@ -7261,7 +7362,7 @@ void main() {
|
||||
// Selected text shows 3 toolbar buttons.
|
||||
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3));
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
@@ -7679,6 +7780,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -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));
|
||||
// First tap moved the cursor.
|
||||
expect(
|
||||
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.tapAt(textfieldStart + const Offset(100.0, 9.0));
|
||||
await tester.tapAt(ePos);
|
||||
await tester.pump();
|
||||
|
||||
// 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
|
||||
// Plain collapsed selection at the edge of first word on iOS. In iOS 12,
|
||||
// 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
|
||||
// likely a bug in iOS 12 and not present in other versions.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
|
||||
);
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
|
||||
|
||||
// No toolbar.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
@@ -7796,6 +7899,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -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.tapAt(textfieldStart + const Offset(50.0, 9.0));
|
||||
await tester.tapAt(ePos);
|
||||
await tester.pump();
|
||||
|
||||
// We ended up moving the cursor to the edge of the same word and dismissed
|
||||
// the toolbar.
|
||||
expect(
|
||||
controller.selection,
|
||||
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.upstream),
|
||||
);
|
||||
expect(controller.selection.isCollapsed, isTrue);
|
||||
expect(controller.selection.baseOffset, isTargetPlatformMobile ? 7 : 6);
|
||||
|
||||
// Collapsed toolbar shows 2 buttons.
|
||||
expect(find.byType(CupertinoButton), findsNothing);
|
||||
@@ -8358,6 +8462,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -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));
|
||||
// First tap moved the cursor to the beginning of the second word.
|
||||
expect(
|
||||
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.longPressAt(textfieldStart + const Offset(100.0, 9.0));
|
||||
await tester.longPressAt(ePos);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Plain collapsed selection at the exact tap position.
|
||||
@@ -8403,6 +8511,9 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
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(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -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));
|
||||
expect(
|
||||
controller.selection,
|
||||
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));
|
||||
// First tap moved the cursor.
|
||||
expect(
|
||||
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();
|
||||
|
||||
// Double tap selection.
|
||||
@@ -8568,7 +8680,7 @@ void main() {
|
||||
);
|
||||
expect(find.byType(CupertinoButton), isContextMenuProvidedByPlatform ? findsNothing : findsNWidgets(3));
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }),
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
@@ -10959,6 +11071,7 @@ void main() {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'Atwater Peel Sherbrooke Bonaventure',
|
||||
);
|
||||
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -10982,11 +11095,18 @@ void main() {
|
||||
pointer: 7,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.up();
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
expect(controller.selection.extentOffset, 23);
|
||||
|
||||
// Expand the selection a bit.
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.down(textOffsetToPosition(tester, 24));
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
await gesture.moveTo(textOffsetToPosition(tester, 28));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
@@ -11049,12 +11169,14 @@ void main() {
|
||||
await gesture.up();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
expect(controller.selection.extentOffset, 26);
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
||||
|
||||
testWidgets('can shift + tap + drag to select with a keyboard (non-Apple platforms)', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'Atwater Peel Sherbrooke Bonaventure',
|
||||
);
|
||||
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android
|
||||
|| defaultTargetPlatform == TargetPlatform.fuchsia;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -11078,11 +11200,17 @@ void main() {
|
||||
pointer: 7,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.up();
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
expect(controller.selection.extentOffset, 23);
|
||||
|
||||
// Expand the selection a bit.
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.down(textOffsetToPosition(tester, 23));
|
||||
}
|
||||
await gesture.moveTo(textOffsetToPosition(tester, 28));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
@@ -11145,12 +11273,13 @@ void main() {
|
||||
await gesture.up();
|
||||
expect(controller.selection.baseOffset, 8);
|
||||
expect(controller.selection.extentOffset, 26);
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.windows }));
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.windows }));
|
||||
|
||||
testWidgets('can shift + tap + drag to select with a keyboard, reversed (Apple platforms)', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'Atwater Peel Sherbrooke Bonaventure',
|
||||
);
|
||||
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.iOS;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -11174,11 +11303,17 @@ void main() {
|
||||
pointer: 7,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.up();
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
expect(controller.selection.extentOffset, 8);
|
||||
|
||||
// Expand the selection a bit.
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.down(textOffsetToPosition(tester, 7));
|
||||
}
|
||||
await gesture.moveTo(textOffsetToPosition(tester, 5));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
@@ -11241,12 +11376,14 @@ void main() {
|
||||
await gesture.up();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
expect(controller.selection.extentOffset, 14);
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
|
||||
|
||||
testWidgets('can shift + tap + drag to select with a keyboard, reversed (non-Apple platforms)', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController(
|
||||
text: 'Atwater Peel Sherbrooke Bonaventure',
|
||||
);
|
||||
final bool isTargetPlatformMobile = defaultTargetPlatform == TargetPlatform.android
|
||||
|| defaultTargetPlatform == TargetPlatform.fuchsia;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
@@ -11270,11 +11407,17 @@ void main() {
|
||||
pointer: 7,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
);
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.up();
|
||||
}
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
expect(controller.selection.extentOffset, 8);
|
||||
|
||||
// Expand the selection a bit.
|
||||
if (isTargetPlatformMobile) {
|
||||
await gesture.down(textOffsetToPosition(tester, 8));
|
||||
}
|
||||
await gesture.moveTo(textOffsetToPosition(tester, 5));
|
||||
await tester.pumpAndSettle();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
@@ -11337,7 +11480,7 @@ void main() {
|
||||
await gesture.up();
|
||||
expect(controller.selection.baseOffset, 23);
|
||||
expect(controller.selection.extentOffset, 14);
|
||||
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.linux, TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.windows }));
|
||||
}, 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 {
|
||||
|
||||
@@ -557,9 +557,9 @@ void main() {
|
||||
|
||||
switch (defaultTargetPlatform) {
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.macOS:
|
||||
expect(renderEditable.selectWordEdgeCalled, isTrue);
|
||||
break;
|
||||
case TargetPlatform.macOS:
|
||||
case TargetPlatform.android:
|
||||
case TargetPlatform.fuchsia:
|
||||
case TargetPlatform.linux:
|
||||
|
||||
Reference in New Issue
Block a user