Unverified Commit 142dd602 authored by Renzo Olivares's avatar Renzo Olivares Committed by GitHub

Match iOS Longpress behavior with native (#123630)

Match iOS Longpress behavior with native
parent 9f2ac971
......@@ -2005,6 +2005,14 @@ class TextSelectionGestureDetectorBuilder {
// cursor will not move on drag update.
bool? _dragBeganOnPreviousSelection;
// For iOS long press behavior when the field is not focused. iOS uses this value
// to determine if a long press began on a field that was not focused.
//
// If the field was not focused when the long press began, a long press will select
// the word and a long press move will select word-by-word. If the field was
// focused, the cursor moves to the long press position.
bool _longPressStartedWithoutFocus = false;
/// Handler for [TextSelectionGestureDetector.onTapDown].
///
/// By default, it forwards the tap to [RenderEditable.handleTapDown] and sets
......@@ -2240,10 +2248,15 @@ class TextSelectionGestureDetectorBuilder {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
if (!renderEditable.hasFocus) {
_longPressStartedWithoutFocus = true;
renderEditable.selectWord(cause: SelectionChangedCause.longPress);
} else {
renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
}
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
......@@ -2291,10 +2304,18 @@ class TextSelectionGestureDetectorBuilder {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
if (_longPressStartedWithoutFocus) {
renderEditable.selectWordsInRange(
from: details.globalPosition - details.offsetFromOrigin - editableOffset - scrollableOffset,
to: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
} else {
renderEditable.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.longPress,
);
}
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
......@@ -2342,6 +2363,7 @@ class TextSelectionGestureDetectorBuilder {
if (shouldShowSelectionToolbar) {
editableText.showToolbar();
}
_longPressStartedWithoutFocus = false;
_dragStartViewportOffset = 0.0;
_dragStartScrollOffset = 0.0;
}
......
......@@ -1591,6 +1591,7 @@ void main() {
home: Column(
children: <Widget>[
CupertinoTextField(
autofocus: true,
controller: controller,
toolbarOptions: const ToolbarOptions(copy: true),
),
......@@ -1599,6 +1600,9 @@ void main() {
),
);
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
// Long press to put the cursor after the "w".
const int index = 3;
await tester.longPressAt(textOffsetToPosition(tester, index));
......@@ -2060,12 +2064,16 @@ void main() {
CupertinoApp(
home: Center(
child: CupertinoTextField(
autofocus: true,
controller: controller,
),
),
),
);
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
// Long press to put the cursor after the "w".
const int index = 3;
await tester.longPressAt(textOffsetToPosition(tester, index));
......@@ -2830,12 +2838,16 @@ void main() {
CupertinoApp(
home: Center(
child: CupertinoTextField(
autofocus: true,
controller: controller,
),
),
),
);
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField));
await tester.longPressAt(textFieldStart + const Offset(50.0, 5.0));
......@@ -2870,12 +2882,16 @@ void main() {
CupertinoApp(
home: Center(
child: CupertinoTextField(
autofocus: true,
controller: controller,
),
),
),
);
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
final Offset ePos = textOffsetToPosition(tester, 6); // Index of 'Atwate|r'
await tester.longPressAt(ePos);
......@@ -2971,7 +2987,7 @@ void main() {
);
testWidgets(
'long press drag moves the cursor under the drag and shows toolbar on lift on Apple platforms',
'long press drag on a focused TextField moves the cursor under the drag and shows toolbar on lift on Apple platforms',
(WidgetTester tester) async {
final TextEditingController controller = TextEditingController(
text: 'Atwater Peel Sherbrooke Bonaventure',
......@@ -2980,12 +2996,16 @@ void main() {
CupertinoApp(
home: Center(
child: CupertinoTextField(
autofocus: true,
controller: controller,
),
),
),
);
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
final Offset textFieldStart = tester.getTopLeft(find.byType(CupertinoTextField));
final TestGesture gesture =
......@@ -3135,12 +3155,16 @@ void main() {
CupertinoApp(
home: Center(
child: CupertinoTextField(
autofocus: true,
controller: controller,
),
),
),
);
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
final RenderEditable renderEditable = tester.renderObject<RenderEditable>(
find.byElementPredicate((Element element) => element.renderObject is RenderEditable).last,
);
......@@ -3289,12 +3313,16 @@ void main() {
CupertinoApp(
home: Center(
child: CupertinoTextField(
autofocus: true,
controller: controller,
),
),
),
);
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
// Use a position higher than wPos to avoid tapping the context menu on
// desktop.
final Offset pPos = textOffsetToPosition(tester, 9) + const Offset(0.0, -20.0); // Index of 'P|eel'
......@@ -7313,6 +7341,7 @@ void main() {
child: Column(
children: <Widget>[
CupertinoTextField(
autofocus: true,
key: const Key('field0'),
controller: controller,
style: const TextStyle(height: 4, color: ui.Color.fromARGB(100, 0, 0, 0)),
......@@ -7329,6 +7358,9 @@ void main() {
),
);
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
final Offset textFieldStart = tester.getTopLeft(find.byKey(const Key('field0')));
await tester.longPressAt(textFieldStart + const Offset(50.0, 2.0));
......@@ -7363,6 +7395,7 @@ void main() {
child: Column(
children: <Widget>[
CupertinoTextField(
autofocus: true,
key: const Key('field0'),
controller: controller,
style: const TextStyle(height: 4, color: ui.Color.fromARGB(100, 0, 0, 0)),
......@@ -7378,6 +7411,9 @@ void main() {
),
);
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
final Offset textFieldStart = tester.getTopLeft(find.byKey(const Key('field0')));
await tester.longPressAt(textFieldStart + const Offset(50.0, 2.0));
......
......@@ -200,6 +200,7 @@ void main() {
data: const MediaQueryData(size: Size(800.0, 600.0)),
child: Center(
child: CupertinoTextField(
autofocus: true,
controller: controller,
),
),
......@@ -207,6 +208,9 @@ void main() {
),
));
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
// Initially, the menu isn't shown at all.
expect(find.text('Cut'), findsNothing);
expect(find.text('Copy'), findsNothing);
......@@ -432,6 +436,7 @@ void main() {
data: const MediaQueryData(size: Size(800.0, 600.0)),
child: Center(
child: CupertinoTextField(
autofocus: true,
controller: controller,
),
),
......@@ -439,6 +444,9 @@ void main() {
),
));
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
// Initially, the menu isn't shown at all.
expect(find.text(_longLocalizations.cutButtonLabel), findsNothing);
expect(find.text(_longLocalizations.copyButtonLabel), findsNothing);
......@@ -546,6 +554,7 @@ void main() {
data: const MediaQueryData(size: Size(800.0, 600.0)),
child: Center(
child: CupertinoTextField(
autofocus: true,
padding: const EdgeInsets.all(8.0),
controller: controller,
maxLines: 2,
......@@ -555,6 +564,9 @@ void main() {
),
));
// This extra pump is so autofocus can propagate to renderEditable.
await tester.pump();
// Initially, the menu isn't shown at all.
expect(find.text('Cut'), findsNothing);
expect(find.text('Copy'), findsNothing);
......
......@@ -460,8 +460,12 @@ void main() {
expect(dragEndCount, 1);
});
testWidgets('test TextSelectionGestureDetectorBuilder long press on Apple Platforms', (WidgetTester tester) async {
testWidgets('test TextSelectionGestureDetectorBuilder long press on Apple Platforms - focused renderEditable', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester);
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
renderEditable.hasFocus = true;
final TestGesture gesture = await tester.startGesture(
const Offset(200.0, 200.0),
pointer: 0,
......@@ -470,13 +474,29 @@ void main() {
await gesture.up();
await tester.pumpAndSettle();
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
expect(state.showToolbarCalled, isTrue);
expect(renderEditable.selectPositionAtCalled, isTrue);
expect(renderEditable.lastCause, SelectionChangedCause.longPress);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('test TextSelectionGestureDetectorBuilder long press on iOS - renderEditable not focused', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester);
final FakeEditableTextState state = tester.state(find.byType(FakeEditableText));
final FakeRenderEditable renderEditable = tester.renderObject(find.byType(FakeEditable));
final TestGesture gesture = await tester.startGesture(
const Offset(200.0, 200.0),
pointer: 0,
);
await tester.pump(const Duration(seconds: 2));
await gesture.up();
await tester.pumpAndSettle();
expect(state.showToolbarCalled, isTrue);
expect(renderEditable.selectWordCalled, isTrue);
expect(renderEditable.lastCause, SelectionChangedCause.longPress);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
testWidgets('test TextSelectionGestureDetectorBuilder long press on non-Apple Platforms', (WidgetTester tester) async {
await pumpTextSelectionGestureDetectorBuilder(tester);
final TestGesture gesture = await tester.startGesture(
......
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