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

Select All via Shortcuts (#85081)

Control + A to select all text is now overridable.
parent b4f36ee4
......@@ -644,7 +644,6 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
};
static final Set<LogicalKeyboardKey> _shortcutKeys = <LogicalKeyboardKey>{
LogicalKeyboardKey.keyA,
LogicalKeyboardKey.keyC,
LogicalKeyboardKey.keyV,
LogicalKeyboardKey.keyX,
......@@ -2232,8 +2231,21 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
_setSelection(nextSelection, cause);
}
// Handles shortcut functionality including cut, copy, paste and select all
// using control/command + (X, C, V, A).
/// Set the current [selection] to contain the entire text value.
///
/// {@macro flutter.rendering.RenderEditable.cause}
void selectAll(SelectionChangedCause cause) {
_setSelection(
selection!.copyWith(
baseOffset: 0,
extentOffset: textSelectionDelegate.textEditingValue.text.length,
),
cause,
);
}
// Handles shortcut functionality including cut, copy, paste using
// using control/command + (X, C, V).
Future<void> _handleShortcuts(LogicalKeyboardKey key) async {
final TextSelection selection = textSelectionDelegate.textEditingValue.selection;
final String text = textSelectionDelegate.textEditingValue.text;
......@@ -2266,14 +2278,6 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
),
);
}
} else if (key == LogicalKeyboardKey.keyA) {
value = TextEditingValue(
text: text,
selection: selection.copyWith(
baseOffset: 0,
extentOffset: textSelectionDelegate.textEditingValue.text.length,
),
);
}
if (value != null) {
_setTextEditingValue(
......
......@@ -66,6 +66,7 @@ class DefaultTextEditingActions extends Actions{
MoveSelectionToEndTextIntent: _MoveSelectionToEndTextAction(),
MoveSelectionToStartTextIntent: _MoveSelectionToStartTextAction(),
MoveSelectionUpTextIntent: _MoveSelectionUpTextAction(),
SelectAllTextIntent: _SelectAllTextAction(),
};
}
......@@ -291,3 +292,10 @@ class _MoveSelectionToStartTextAction extends TextEditingAction<MoveSelectionToS
textEditingActionTarget!.renderEditable.moveSelectionToStart(SelectionChangedCause.keyboard);
}
}
class _SelectAllTextAction extends TextEditingAction<SelectAllTextIntent> {
@override
Object? invoke(SelectAllTextIntent intent, [BuildContext? context]) {
textEditingActionTarget!.renderEditable.selectAll(SelectionChangedCause.keyboard);
}
}
......@@ -187,10 +187,12 @@ class DefaultTextEditingShortcuts extends Shortcuts {
SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): ExtendSelectionLeftTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): ExtendSelectionRightTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): ExtendSelectionUpTextIntent(),
SingleActivator(LogicalKeyboardKey.keyA, control: true): SelectAllTextIntent(),
// The following key combinations have no effect on text editing on this
// platform:
// * End
// * Home
// * Meta + A
// * Meta + arrow down
// * Meta + arrow left
// * Meta + arrow right
......@@ -232,11 +234,13 @@ class DefaultTextEditingShortcuts extends Shortcuts {
SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): ExtendSelectionLeftTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): ExtendSelectionRightTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): ExtendSelectionUpTextIntent(),
SingleActivator(LogicalKeyboardKey.keyA, control: true): SelectAllTextIntent(),
// The following key combinations have no effect on text editing on this
// platform:
// * Meta + arrow down
// * End
// * Home
// * Meta + A
// * Meta + arrow down
// * Meta + arrow left
// * Meta + arrow right
// * Meta + arrow up
......@@ -277,11 +281,13 @@ class DefaultTextEditingShortcuts extends Shortcuts {
SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): ExtendSelectionLeftTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): ExtendSelectionRightTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): ExtendSelectionUpTextIntent(),
SingleActivator(LogicalKeyboardKey.keyA, control: true): SelectAllTextIntent(),
// The following key combinations have no effect on text editing on this
// platform:
// * Meta + arrow down
// * End
// * Home
// * Meta + A
// * Meta + arrow down
// * Meta + arrow left
// * Meta + arrow right
// * Meta + arrow up
......@@ -322,11 +328,13 @@ class DefaultTextEditingShortcuts extends Shortcuts {
SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): ExtendSelectionLeftTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): ExtendSelectionRightTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): ExtendSelectionUpTextIntent(),
SingleActivator(LogicalKeyboardKey.keyA, control: true): SelectAllTextIntent(),
// The following key combinations have no effect on text editing on this
// platform:
// * Meta + arrow down
// * End
// * Home
// * Meta + A
// * Meta + arrow down
// * Meta + arrow left
// * Meta + arrow right
// * Meta + arrow up
......@@ -371,8 +379,10 @@ class DefaultTextEditingShortcuts extends Shortcuts {
SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): ExtendSelectionLeftTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): ExtendSelectionRightTextIntent(),
SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): ExtendSelectionUpTextIntent(),
SingleActivator(LogicalKeyboardKey.keyA, meta: true): SelectAllTextIntent(),
// The following key combinations have no effect on text editing on this
// platform:
// * Control + A
// * Control + arrow left
// * Control + arrow right
// * Control + shift + arrow left
......@@ -416,8 +426,10 @@ class DefaultTextEditingShortcuts extends Shortcuts {
SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): ExtendSelectionUpTextIntent(),
SingleActivator(LogicalKeyboardKey.end, shift: true): ExpandSelectionRightByLineTextIntent(),
SingleActivator(LogicalKeyboardKey.home, shift: true): ExpandSelectionLeftByLineTextIntent(),
SingleActivator(LogicalKeyboardKey.keyA, control: true): SelectAllTextIntent(),
// The following key combinations have no effect on text editing on this
// platform:
// * Meta + A
// * Meta + arrow down
// * Meta + arrow left
// * Meta + arrow right
......@@ -474,6 +486,8 @@ class DefaultTextEditingShortcuts extends Shortcuts {
SingleActivator(LogicalKeyboardKey.end, shift: true): DoNothingAndStopPropagationTextIntent(),
SingleActivator(LogicalKeyboardKey.home, shift: true): DoNothingAndStopPropagationTextIntent(),
SingleActivator(LogicalKeyboardKey.space): DoNothingAndStopPropagationTextIntent(),
SingleActivator(LogicalKeyboardKey.keyA, control: true): DoNothingAndStopPropagationTextIntent(),
SingleActivator(LogicalKeyboardKey.keyA, meta: true): DoNothingAndStopPropagationTextIntent(),
};
static Map<ShortcutActivator, Intent> get _shortcuts {
......
......@@ -266,3 +266,11 @@ class MoveSelectionUpTextIntent extends Intent{
/// Creates an instance of MoveSelectionUpTextIntent.
const MoveSelectionUpTextIntent();
}
/// An [Intent] to select everything in the field.
///
/// {@macro flutter.widgets.TextEditingIntents.seeAlso}
class SelectAllTextIntent extends Intent{
/// Creates an instance of SelectAllTextIntent.
const SelectAllTextIntent();
}
......@@ -7263,6 +7263,69 @@ void main() {
// On web, using keyboard for selection is handled by the browser.
}, skip: kIsWeb);
testWidgets('can override select all via Shortcuts', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: testText);
controller.selection = const TextSelection(
baseOffset: 0,
extentOffset: 0,
affinity: TextAffinity.upstream,
);
await tester.pumpWidget(MaterialApp(
home: Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 400,
child: Shortcuts(
shortcuts: const <ShortcutActivator, Intent>{
SingleActivator(LogicalKeyboardKey.keyA, control: true): MoveSelectionRightTextIntent(),
},
child: EditableText(
maxLines: 10,
controller: controller,
showSelectionHandles: true,
autofocus: true,
focusNode: focusNode,
style: Typography.material2018(platform: TargetPlatform.android).black.subtitle1!,
cursorColor: Colors.blue,
backgroundCursorColor: Colors.grey,
selectionControls: materialTextSelectionControls,
keyboardType: TextInputType.text,
textAlign: TextAlign.right,
),
),
),
),
));
await tester.pump(); // Wait for autofocus to take effect.
expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, 0);
// meta + A moves the cursor right instead of doing select all.
final String targetPlatform = defaultTargetPlatform.toString();
final String platform = targetPlatform.substring(targetPlatform.indexOf('.') + 1).toLowerCase();
await sendKeys(
tester,
<LogicalKeyboardKey>[LogicalKeyboardKey.keyA],
shortcutModifier: true,
platform: platform,
);
await tester.pump();
expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, 1);
await sendKeys(
tester,
<LogicalKeyboardKey>[LogicalKeyboardKey.keyA],
shortcutModifier: true,
platform: platform,
);
await tester.pump();
expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, 2);
// On web, using keyboard for selection is handled by the browser.
}, skip: kIsWeb);
testWidgets('navigating by word', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: 'word word word');
// word wo|rd| word
......@@ -7461,6 +7524,75 @@ void main() {
// On web, using keyboard for selection is handled by the browser.
}, skip: kIsWeb);
testWidgets('can override select all via Actions', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: testText);
controller.selection = const TextSelection(
baseOffset: 0,
extentOffset: 0,
affinity: TextAffinity.upstream,
);
late bool myIntentWasCalled;
await tester.pumpWidget(MaterialApp(
home: Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 400,
child: Actions(
actions: <Type, Action<Intent>>{
SelectAllTextIntent: _MyMoveSelectionRightTextAction(
onInvoke: () {
myIntentWasCalled = true;
},
),
},
child: EditableText(
maxLines: 10,
controller: controller,
showSelectionHandles: true,
autofocus: true,
focusNode: focusNode,
style: Typography.material2018(platform: TargetPlatform.android).black.subtitle1!,
cursorColor: Colors.blue,
backgroundCursorColor: Colors.grey,
selectionControls: materialTextSelectionControls,
keyboardType: TextInputType.text,
textAlign: TextAlign.right,
),
),
),
),
));
await tester.pump(); // Wait for autofocus to take effect.
expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, 0);
// meta + A moves the cursor right instead of doing select all.
final String targetPlatform = defaultTargetPlatform.toString();
final String platform = targetPlatform.substring(targetPlatform.indexOf('.') + 1).toLowerCase();
await sendKeys(
tester,
<LogicalKeyboardKey>[LogicalKeyboardKey.keyA],
shortcutModifier: true,
platform: platform,
);
await tester.pump();
expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, 1);
expect(myIntentWasCalled, isTrue);
await sendKeys(
tester,
<LogicalKeyboardKey>[LogicalKeyboardKey.keyA],
shortcutModifier: true,
platform: platform,
);
await tester.pump();
expect(controller.selection.isCollapsed, isTrue);
expect(controller.selection.baseOffset, 2);
// On web, using keyboard for selection is handled by the browser.
}, skip: kIsWeb);
testWidgets('ignore key event from web platform', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(
text: 'test\ntest',
......
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