SelectableText long press on iOS (#63994)

This commit is contained in:
Justin McCandless 2020-08-28 10:33:05 -07:00 committed by GitHub
parent 2ae25cc2d7
commit 8f370dff65
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 88 deletions

View file

@ -72,25 +72,11 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur
@override
void onSingleLongTapMoveUpdate(LongPressMoveUpdateDetails details) {
if (delegate.selectionEnabled) {
switch (Theme.of(_state.context).platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
renderEditable.selectWordsInRange(
from: details.globalPosition - details.offsetFromOrigin,
to: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
break;
}
renderEditable.selectWordsInRange(
from: details.globalPosition - details.offsetFromOrigin,
to: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
}
}
@ -118,22 +104,8 @@ class _SelectableTextSelectionGestureDetectorBuilder extends TextSelectionGestur
@override
void onSingleLongTapStart(LongPressStartDetails details) {
if (delegate.selectionEnabled) {
switch (Theme.of(_state.context).platform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
break;
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
renderEditable.selectWord(cause: SelectionChangedCause.longPress);
Feedback.forLongPress(_state.context);
break;
}
renderEditable.selectWord(cause: SelectionChangedCause.longPress);
Feedback.forLongPress(_state.context);
}
}
}

View file

@ -2710,7 +2710,7 @@ void main() {
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets(
'long press moves cursor to the exact long press position and shows toolbar',
'long press selects word and shows toolbar (iOS)',
(WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
@ -2730,13 +2730,16 @@ void main() {
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
final TextEditingController controller = editableTextWidget.controller;
// Collapsed cursor for iOS long press.
// The longpressed word is selected.
expect(
controller.selection,
const TextSelection.collapsed(offset: 4, affinity: TextAffinity.upstream),
const TextSelection(
baseOffset: 0,
extentOffset: 7,
),
);
// Collapsed toolbar shows 2 buttons.
// Toolbar shows one button.
expect(find.byType(CupertinoButton), findsNWidgets(1));
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
@ -2828,10 +2831,14 @@ void main() {
final EditableText editableTextWidget = tester.widget(find.byType(EditableText).first);
final TextEditingController controller = editableTextWidget.controller;
// Long press on iOS shows collapsed selection cursor.
// The longpressed word is selected.
expect(
controller.selection,
const TextSelection.collapsed(offset: 4, affinity: TextAffinity.upstream),
const TextSelection(
baseOffset: 0,
extentOffset: 7,
affinity: TextAffinity.downstream,
),
);
// Cursor move doesn't trigger a toolbar initially.
expect(find.byType(CupertinoButton), findsNothing);
@ -2842,7 +2849,11 @@ void main() {
// The selection position is now moved with the drag.
expect(
controller.selection,
const TextSelection.collapsed(offset: 7, affinity: TextAffinity.downstream),
const TextSelection(
baseOffset: 0,
extentOffset: 8,
affinity: TextAffinity.downstream,
),
);
// Still no toolbar.
expect(find.byType(CupertinoButton), findsNothing);
@ -2853,7 +2864,11 @@ void main() {
// The selection position is now moved with the drag.
expect(
controller.selection,
const TextSelection.collapsed(offset: 11, affinity: TextAffinity.upstream),
const TextSelection(
baseOffset: 0,
extentOffset: 12,
affinity: TextAffinity.downstream,
),
);
// Still no toolbar.
expect(find.byType(CupertinoButton), findsNothing);
@ -2864,7 +2879,11 @@ void main() {
// The selection isn't affected by the gesture lift.
expect(
controller.selection,
const TextSelection.collapsed(offset: 11, affinity: TextAffinity.upstream),
const TextSelection(
baseOffset: 0,
extentOffset: 12,
affinity: TextAffinity.downstream,
),
);
// The toolbar now shows up.
expect(find.byType(CupertinoButton), findsNWidgets(1));
@ -2906,7 +2925,7 @@ void main() {
expect(
controller.selection,
const TextSelection.collapsed(offset: 21),
const TextSelection(baseOffset: 13, extentOffset: 23),
);
expect(find.byType(CupertinoButton), findsNothing);
@ -2915,21 +2934,33 @@ void main() {
await tester.pump();
expect(
controller.selection,
const TextSelection.collapsed(offset: 64, affinity: TextAffinity.downstream),
const TextSelection(
baseOffset: 13,
extentOffset: 66,
affinity: TextAffinity.downstream,
),
);
// Keep moving out.
await gesture.moveBy(const Offset(1, 0));
await tester.pump();
expect(
controller.selection,
const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
const TextSelection(
baseOffset: 13,
extentOffset: 66,
affinity: TextAffinity.downstream,
),
);
await gesture.moveBy(const Offset(1, 0));
await tester.pump();
expect(
controller.selection,
const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
); // We're at the edge now.
const TextSelection(
baseOffset: 13,
extentOffset: 66,
affinity: TextAffinity.downstream,
),
);
expect(find.byType(CupertinoButton), findsNothing);
await gesture.up();
@ -2938,7 +2969,11 @@ void main() {
// The selection isn't affected by the gesture lift.
expect(
controller.selection,
const TextSelection.collapsed(offset: 66, affinity: TextAffinity.upstream),
const TextSelection(
baseOffset: 13,
extentOffset: 66,
affinity: TextAffinity.downstream,
),
);
// The toolbar now shows up.
expect(find.byType(CupertinoButton), findsNWidgets(1));
@ -2957,10 +2992,13 @@ void main() {
expect(firstCharEndpoint.length, 1);
// The first character is now offscreen to the left.
expect(firstCharEndpoint[0].point.dx, moreOrLessEquals(-125, epsilon: 1));
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
},
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
skip: true, // https://github.com/flutter/flutter/issues/64059
);
testWidgets(
'long tap after a double tap select is not affected',
'long tap still selects after a double tap select',
(WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
@ -2991,10 +3029,10 @@ void main() {
await tester.longPressAt(selectableTextStart + const Offset(100.0, 5.0));
await tester.pump();
// Plain collapsed selection at the exact tap position.
// Selected the "word" where the tap happened, which is the first space.
expect(
controller.selection,
const TextSelection.collapsed(offset: 7),
const TextSelection(baseOffset: 7, extentOffset: 8),
);
// Long press toolbar.
@ -3933,38 +3971,4 @@ void main() {
TargetPlatform.windows,
}),
);
testWidgets('The handles show after pressing Select All (iOS and Mac)', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: SelectableText('abc def ghi'),
),
),
);
// Long press at 'e' in 'def'.
final Offset ePos = textOffsetToPosition(tester, 5);
await tester.longPressAt(ePos);
await tester.pumpAndSettle();
expect(find.text('Select All'), findsOneWidget);
expect(find.text('Copy'), findsNothing);
expect(find.text('Paste'), findsNothing);
expect(find.text('Cut'), findsNothing);
EditableTextState editableText = tester.state(find.byType(EditableText));
expect(editableText.selectionOverlay.handlesAreVisible, isFalse);
expect(editableText.selectionOverlay.toolbarIsVisible, isTrue);
await tester.tap(find.text('Select All'));
await tester.pumpAndSettle();
expect(find.text('Copy'), findsOneWidget);
expect(find.text('Select All'), findsNothing);
expect(find.text('Paste'), findsNothing);
expect(find.text('Cut'), findsNothing);
editableText = tester.state(find.byType(EditableText));
expect(editableText.selectionOverlay.handlesAreVisible, isTrue);
},
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
);
}