Skip to content

Commit 8f7e41a

Browse files
authored
Some MacOS control key shortcuts (#103936)
1 parent 2038180 commit 8f7e41a

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

packages/flutter/lib/src/widgets/default_text_editing_shortcuts.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,12 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
310310
const SingleActivator(LogicalKeyboardKey.keyA, meta: true): const SelectAllTextIntent(SelectionChangedCause.keyboard),
311311
const SingleActivator(LogicalKeyboardKey.keyZ, meta: true): const UndoTextIntent(SelectionChangedCause.keyboard),
312312
const SingleActivator(LogicalKeyboardKey.keyZ, shift: true, meta: true): const RedoTextIntent(SelectionChangedCause.keyboard),
313+
const SingleActivator(LogicalKeyboardKey.keyE, control: true): const ExtendSelectionToLineBreakIntent(forward: true, collapseSelection: true),
314+
const SingleActivator(LogicalKeyboardKey.keyA, control: true): const ExtendSelectionToLineBreakIntent(forward: false, collapseSelection: true),
315+
const SingleActivator(LogicalKeyboardKey.keyF, control: true): const ExtendSelectionByCharacterIntent(forward: true, collapseSelection: true),
316+
const SingleActivator(LogicalKeyboardKey.keyB, control: true): const ExtendSelectionByCharacterIntent(forward: false, collapseSelection: true),
317+
const SingleActivator(LogicalKeyboardKey.keyN, control: true): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: true, collapseSelection: true),
318+
const SingleActivator(LogicalKeyboardKey.keyP, control: true): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: true),
313319
// These keys should go to the IME when a field is focused, not to other
314320
// Shortcuts.
315321
const SingleActivator(LogicalKeyboardKey.space): const DoNothingAndStopPropagationTextIntent(),

packages/flutter/test/widgets/editable_text_test.dart

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11982,6 +11982,221 @@ void main() {
1198211982
expect(tester.takeException(), null);
1198311983
// On web, the text selection toolbar cut button is handled by the browser.
1198411984
}, skip: kIsWeb); // [intended]
11985+
11986+
group('Mac document shortcuts', () {
11987+
testWidgets('ctrl-A/E', (WidgetTester tester) async {
11988+
final String targetPlatformString = defaultTargetPlatform.toString();
11989+
final String platform = targetPlatformString.substring(targetPlatformString.indexOf('.') + 1).toLowerCase();
11990+
final TextEditingController controller = TextEditingController(text: testText);
11991+
controller.selection = const TextSelection(
11992+
baseOffset: 0,
11993+
extentOffset: 0,
11994+
affinity: TextAffinity.upstream,
11995+
);
11996+
await tester.pumpWidget(MaterialApp(
11997+
home: Align(
11998+
alignment: Alignment.topLeft,
11999+
child: SizedBox(
12000+
width: 400,
12001+
child: EditableText(
12002+
maxLines: 10,
12003+
controller: controller,
12004+
showSelectionHandles: true,
12005+
autofocus: true,
12006+
focusNode: FocusNode(),
12007+
style: Typography.material2018().black.subtitle1!,
12008+
cursorColor: Colors.blue,
12009+
backgroundCursorColor: Colors.grey,
12010+
selectionControls: materialTextSelectionControls,
12011+
keyboardType: TextInputType.text,
12012+
textAlign: TextAlign.right,
12013+
),
12014+
),
12015+
),
12016+
));
12017+
12018+
await tester.pump(); // Wait for autofocus to take effect.
12019+
12020+
expect(controller.selection.isCollapsed, isTrue);
12021+
expect(controller.selection.baseOffset, 0);
12022+
12023+
await tester.sendKeyDownEvent(
12024+
LogicalKeyboardKey.controlLeft,
12025+
platform: platform,
12026+
);
12027+
await tester.pump();
12028+
await tester.sendKeyEvent(LogicalKeyboardKey.keyE, platform: platform);
12029+
await tester.pump();
12030+
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft, platform: platform);
12031+
await tester.pump();
12032+
12033+
expect(
12034+
controller.selection,
12035+
equals(
12036+
const TextSelection.collapsed(
12037+
offset: 19,
12038+
affinity: TextAffinity.upstream,
12039+
),
12040+
),
12041+
reason: 'on $platform',
12042+
);
12043+
12044+
await tester.sendKeyDownEvent(
12045+
LogicalKeyboardKey.controlLeft,
12046+
platform: platform,
12047+
);
12048+
await tester.pump();
12049+
await tester.sendKeyEvent(LogicalKeyboardKey.keyA, platform: platform);
12050+
await tester.pump();
12051+
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft, platform: platform);
12052+
await tester.pump();
12053+
12054+
expect(
12055+
controller.selection,
12056+
equals(
12057+
const TextSelection.collapsed(
12058+
offset: 0,
12059+
),
12060+
),
12061+
reason: 'on $platform',
12062+
);
12063+
},
12064+
skip: kIsWeb, // [intended] on web these keys are handled by the browser.
12065+
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
12066+
);
12067+
12068+
testWidgets('ctrl-F/B', (WidgetTester tester) async {
12069+
final String targetPlatformString = defaultTargetPlatform.toString();
12070+
final String platform = targetPlatformString.substring(targetPlatformString.indexOf('.') + 1).toLowerCase();
12071+
final TextEditingController controller = TextEditingController(text: testText);
12072+
controller.selection = const TextSelection(
12073+
baseOffset: 0,
12074+
extentOffset: 0,
12075+
affinity: TextAffinity.upstream,
12076+
);
12077+
await tester.pumpWidget(MaterialApp(
12078+
home: Align(
12079+
alignment: Alignment.topLeft,
12080+
child: SizedBox(
12081+
width: 400,
12082+
child: EditableText(
12083+
maxLines: 10,
12084+
controller: controller,
12085+
showSelectionHandles: true,
12086+
autofocus: true,
12087+
focusNode: FocusNode(),
12088+
style: Typography.material2018().black.subtitle1!,
12089+
cursorColor: Colors.blue,
12090+
backgroundCursorColor: Colors.grey,
12091+
selectionControls: materialTextSelectionControls,
12092+
keyboardType: TextInputType.text,
12093+
textAlign: TextAlign.right,
12094+
),
12095+
),
12096+
),
12097+
));
12098+
12099+
await tester.pump(); // Wait for autofocus to take effect.
12100+
12101+
expect(controller.selection.isCollapsed, isTrue);
12102+
expect(controller.selection.baseOffset, 0);
12103+
12104+
await tester.sendKeyDownEvent(
12105+
LogicalKeyboardKey.controlLeft,
12106+
platform: platform,
12107+
);
12108+
await tester.pump();
12109+
await tester.sendKeyEvent(LogicalKeyboardKey.keyF, platform: platform);
12110+
await tester.pump();
12111+
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft, platform: platform);
12112+
await tester.pump();
12113+
12114+
expect(controller.selection.isCollapsed, isTrue);
12115+
expect(controller.selection.baseOffset, 1);
12116+
12117+
await tester.sendKeyDownEvent(
12118+
LogicalKeyboardKey.controlLeft,
12119+
platform: platform,
12120+
);
12121+
await tester.pump();
12122+
await tester.sendKeyEvent(LogicalKeyboardKey.keyB, platform: platform);
12123+
await tester.pump();
12124+
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft, platform: platform);
12125+
await tester.pump();
12126+
12127+
expect(controller.selection.isCollapsed, isTrue);
12128+
expect(controller.selection.baseOffset, 0);
12129+
},
12130+
skip: kIsWeb, // [intended] on web these keys are handled by the browser.
12131+
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
12132+
);
12133+
12134+
testWidgets('ctrl-N/P', (WidgetTester tester) async {
12135+
final String targetPlatformString = defaultTargetPlatform.toString();
12136+
final String platform = targetPlatformString.substring(targetPlatformString.indexOf('.') + 1).toLowerCase();
12137+
final TextEditingController controller = TextEditingController(text: testText);
12138+
controller.selection = const TextSelection(
12139+
baseOffset: 0,
12140+
extentOffset: 0,
12141+
affinity: TextAffinity.upstream,
12142+
);
12143+
await tester.pumpWidget(MaterialApp(
12144+
home: Align(
12145+
alignment: Alignment.topLeft,
12146+
child: SizedBox(
12147+
width: 400,
12148+
child: EditableText(
12149+
maxLines: 10,
12150+
controller: controller,
12151+
showSelectionHandles: true,
12152+
autofocus: true,
12153+
focusNode: FocusNode(),
12154+
style: Typography.material2018().black.subtitle1!,
12155+
cursorColor: Colors.blue,
12156+
backgroundCursorColor: Colors.grey,
12157+
selectionControls: materialTextSelectionControls,
12158+
keyboardType: TextInputType.text,
12159+
textAlign: TextAlign.right,
12160+
),
12161+
),
12162+
),
12163+
));
12164+
12165+
await tester.pump(); // Wait for autofocus to take effect.
12166+
12167+
expect(controller.selection.isCollapsed, isTrue);
12168+
expect(controller.selection.baseOffset, 0);
12169+
12170+
await tester.sendKeyDownEvent(
12171+
LogicalKeyboardKey.controlLeft,
12172+
platform: platform,
12173+
);
12174+
await tester.pump();
12175+
await tester.sendKeyEvent(LogicalKeyboardKey.keyN, platform: platform);
12176+
await tester.pump();
12177+
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft, platform: platform);
12178+
await tester.pump();
12179+
12180+
expect(controller.selection.isCollapsed, isTrue);
12181+
expect(controller.selection.baseOffset, 20);
12182+
12183+
await tester.sendKeyDownEvent(
12184+
LogicalKeyboardKey.controlLeft,
12185+
platform: platform,
12186+
);
12187+
await tester.pump();
12188+
await tester.sendKeyEvent(LogicalKeyboardKey.keyP, platform: platform);
12189+
await tester.pump();
12190+
await tester.sendKeyUpEvent(LogicalKeyboardKey.controlLeft, platform: platform);
12191+
await tester.pump();
12192+
12193+
expect(controller.selection.isCollapsed, isTrue);
12194+
expect(controller.selection.baseOffset, 0);
12195+
},
12196+
skip: kIsWeb, // [intended] on web these keys are handled by the browser.
12197+
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
12198+
);
12199+
});
1198512200
}
1198612201

1198712202
class UnsettableController extends TextEditingController {

0 commit comments

Comments
 (0)