Unverified Commit b7a425f1 authored by Justin McCandless's avatar Justin McCandless Committed by GitHub

Right click on selection when unfocused should re-focus (#104666)

Bug fix for focusing a field with right click on existing selection (Mac and iOS)
parent d09e4548
......@@ -1622,7 +1622,7 @@ class TextSelectionGestureDetectorBuilder {
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
case TargetPlatform.macOS:
if (!_lastSecondaryTapWasOnSelection) {
if (!_lastSecondaryTapWasOnSelection || !renderEditable.hasFocus) {
renderEditable.selectWord(cause: SelectionChangedCause.tap);
}
if (shouldShowSelectionToolbar) {
......
......@@ -5653,55 +5653,155 @@ void main() {
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
);
testWidgets('Can right click to focus multiple times', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/pull/103228
final FocusNode focusNode1 = FocusNode();
final FocusNode focusNode2 = FocusNode();
final UniqueKey key1 = UniqueKey();
final UniqueKey key2 = UniqueKey();
await tester.pumpWidget(
CupertinoApp(
home: Column(
children: <Widget>[
CupertinoTextField(
key: key1,
focusNode: focusNode1,
),
CupertinoTextField(
key: key2,
focusNode: focusNode2,
),
],
group('Right click focus', () {
testWidgets('Can right click to focus multiple times', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/pull/103228
final FocusNode focusNode1 = FocusNode();
final FocusNode focusNode2 = FocusNode();
final UniqueKey key1 = UniqueKey();
final UniqueKey key2 = UniqueKey();
await tester.pumpWidget(
CupertinoApp(
home: Column(
children: <Widget>[
CupertinoTextField(
key: key1,
focusNode: focusNode1,
),
CupertinoTextField(
key: key2,
focusNode: focusNode2,
),
],
),
),
),
);
);
// Interact with the field to establish the input connection.
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryMouseButton,
);
await tester.pump();
// Interact with the field to establish the input connection.
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryMouseButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
await tester.tapAt(
tester.getCenter(find.byKey(key2)),
buttons: kSecondaryMouseButton,
);
await tester.pump();
await tester.tapAt(
tester.getCenter(find.byKey(key2)),
buttons: kSecondaryMouseButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isFalse);
expect(focusNode2.hasFocus, isTrue);
expect(focusNode1.hasFocus, isFalse);
expect(focusNode2.hasFocus, isTrue);
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryMouseButton,
);
await tester.pump();
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryMouseButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
});
testWidgets('Can right click to focus on previously selected word on Apple platforms', (WidgetTester tester) async {
final FocusNode focusNode1 = FocusNode();
final FocusNode focusNode2 = FocusNode();
final TextEditingController controller = TextEditingController(
text: 'first second',
);
final UniqueKey key1 = UniqueKey();
await tester.pumpWidget(
CupertinoApp(
home: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CupertinoTextField(
key: key1,
controller: controller,
focusNode: focusNode1,
),
Focus(
focusNode: focusNode2,
child: const Text('focusable'),
),
],
),
),
);
// Interact with the field to establish the input connection.
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryMouseButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
// Select the second word.
controller.selection = const TextSelection(
baseOffset: 6,
extentOffset: 12,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(controller.selection.isCollapsed, isFalse);
expect(controller.selection.baseOffset, 6);
expect(controller.selection.extentOffset, 12);
// Unfocus the first field.
focusNode2.requestFocus();
await tester.pumpAndSettle();
expect(focusNode1.hasFocus, isFalse);
expect(focusNode2.hasFocus, isTrue);
// Right click the second word in the first field, which is still selected
// even though the selection is not visible.
await tester.tapAt(
textOffsetToPosition(tester, 8),
buttons: kSecondaryMouseButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(controller.selection.baseOffset, 6);
expect(controller.selection.extentOffset, 12);
// Select everything.
controller.selection = const TextSelection(
baseOffset: 0,
extentOffset: 12,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(controller.selection.baseOffset, 0);
expect(controller.selection.extentOffset, 12);
// Unfocus the first field.
focusNode2.requestFocus();
await tester.pumpAndSettle();
// Right click the first word in the first field.
await tester.tapAt(
textOffsetToPosition(tester, 2),
buttons: kSecondaryMouseButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(controller.selection.baseOffset, 0);
expect(controller.selection.extentOffset, 5);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
});
}
......@@ -11378,57 +11378,158 @@ void main() {
skip: isContextMenuProvidedByPlatform, // [intended] only applies to platforms where we supply the context menu.
);
testWidgets('Can right click to focus multiple times', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/pull/103228
final FocusNode focusNode1 = FocusNode();
final FocusNode focusNode2 = FocusNode();
final UniqueKey key1 = UniqueKey();
final UniqueKey key2 = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Column(
children: <Widget>[
TextField(
key: key1,
focusNode: focusNode1,
),
TextField(
key: key2,
focusNode: focusNode2,
),
],
group('Right click focus', () {
testWidgets('Can right click to focus multiple times', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/pull/103228
final FocusNode focusNode1 = FocusNode();
final FocusNode focusNode2 = FocusNode();
final UniqueKey key1 = UniqueKey();
final UniqueKey key2 = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Column(
children: <Widget>[
TextField(
key: key1,
focusNode: focusNode1,
),
TextField(
key: key2,
focusNode: focusNode2,
),
],
),
),
),
),
);
);
// Interact with the field to establish the input connection.
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryButton,
);
await tester.pump();
// Interact with the field to establish the input connection.
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
await tester.tapAt(
tester.getCenter(find.byKey(key2)),
buttons: kSecondaryButton,
);
await tester.pump();
await tester.tapAt(
tester.getCenter(find.byKey(key2)),
buttons: kSecondaryButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isFalse);
expect(focusNode2.hasFocus, isTrue);
expect(focusNode1.hasFocus, isFalse);
expect(focusNode2.hasFocus, isTrue);
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryButton,
);
await tester.pump();
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
});
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
testWidgets('Can right click to focus on previously selected word on Apple platforms', (WidgetTester tester) async {
final FocusNode focusNode1 = FocusNode();
final FocusNode focusNode2 = FocusNode();
final TextEditingController controller = TextEditingController(
text: 'first second',
);
final UniqueKey key1 = UniqueKey();
await tester.pumpWidget(
MaterialApp(
home: Material(
child: Column(
children: <Widget>[
TextField(
key: key1,
controller: controller,
focusNode: focusNode1,
),
Focus(
focusNode: focusNode2,
child: const Text('focusable'),
),
],
),
),
),
);
// Interact with the field to establish the input connection.
await tester.tapAt(
tester.getCenter(find.byKey(key1)),
buttons: kSecondaryButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
// Select the second word.
controller.selection = const TextSelection(
baseOffset: 6,
extentOffset: 12,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(controller.selection.isCollapsed, isFalse);
expect(controller.selection.baseOffset, 6);
expect(controller.selection.extentOffset, 12);
// Unfocus the first field.
focusNode2.requestFocus();
await tester.pumpAndSettle();
expect(focusNode1.hasFocus, isFalse);
expect(focusNode2.hasFocus, isTrue);
// Right click the second word in the first field, which is still selected
// even though the selection is not visible.
await tester.tapAt(
textOffsetToPosition(tester, 8),
buttons: kSecondaryButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(controller.selection.baseOffset, 6);
expect(controller.selection.extentOffset, 12);
// Select everything.
controller.selection = const TextSelection(
baseOffset: 0,
extentOffset: 12,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(controller.selection.baseOffset, 0);
expect(controller.selection.extentOffset, 12);
// Unfocus the first field.
focusNode2.requestFocus();
await tester.pumpAndSettle();
// Right click the first word in the first field.
await tester.tapAt(
textOffsetToPosition(tester, 2),
buttons: kSecondaryButton,
);
await tester.pump();
expect(focusNode1.hasFocus, isTrue);
expect(focusNode2.hasFocus, isFalse);
expect(controller.selection.baseOffset, 0);
expect(controller.selection.extentOffset, 5);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
});
}
......@@ -1391,12 +1391,14 @@ class FakeRenderEditable extends RenderEditable {
@override
void selectWordsInRange({ required Offset from, Offset? to, required SelectionChangedCause cause }) {
selectWordsInRangeCalled = true;
hasFocus = true;
}
bool selectWordEdgeCalled = false;
@override
void selectWordEdge({ required SelectionChangedCause cause }) {
selectWordEdgeCalled = true;
hasFocus = true;
}
bool selectPositionAtCalled = false;
......@@ -1407,6 +1409,7 @@ class FakeRenderEditable extends RenderEditable {
selectPositionAtCalled = true;
selectPositionAtFrom = from;
selectPositionAtTo = to;
hasFocus = true;
}
bool selectPositionCalled = false;
......@@ -1420,6 +1423,7 @@ class FakeRenderEditable extends RenderEditable {
@override
void selectWord({ required SelectionChangedCause cause }) {
selectWordCalled = true;
hasFocus = true;
}
@override
......
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