mirror of
https://github.com/flutter/flutter
synced 2024-08-25 02:46:31 +00:00
Adds numpad navigation shortcuts for Linux (#145464)
## Description This PR adds shortcuts related to numpad keys on Linux. ## Related Issue Linux side for https://github.com/flutter/flutter/issues/144936 ## Tests Adds 2 tests.
This commit is contained in:
parent
5fab92f062
commit
859eb2eda9
|
@ -253,8 +253,51 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||
|
||||
static final Map<ShortcutActivator, Intent> _fuchsiaShortcuts = _androidShortcuts;
|
||||
|
||||
static final Map<ShortcutActivator, Intent> _linuxNumpadShortcuts = <ShortcutActivator, Intent>{
|
||||
// When numLock is on, numpad keys shortcuts require shift to be pressed too.
|
||||
const SingleActivator(LogicalKeyboardKey.numpad6, shift: true, numLock: LockState.locked): const ExtendSelectionByCharacterIntent(forward: true, collapseSelection: false),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad4, shift: true, numLock: LockState.locked): const ExtendSelectionByCharacterIntent(forward: false, collapseSelection: false),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad8, shift: true, numLock: LockState.locked): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: false),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad2, shift: true, numLock: LockState.locked): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: true, collapseSelection: false),
|
||||
|
||||
const SingleActivator(LogicalKeyboardKey.numpad6, shift: true, control: true, numLock: LockState.locked): const ExtendSelectionToNextWordBoundaryIntent(forward: true, collapseSelection: false),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad4, shift: true, control: true, numLock: LockState.locked): const ExtendSelectionToNextWordBoundaryIntent(forward: false, collapseSelection: false),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad8, shift: true, control: true, numLock: LockState.locked): const ExtendSelectionToNextParagraphBoundaryIntent(forward: false, collapseSelection: false),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad2, shift: true, control: true, numLock: LockState.locked): const ExtendSelectionToNextParagraphBoundaryIntent(forward: true, collapseSelection: false),
|
||||
|
||||
const SingleActivator(LogicalKeyboardKey.numpad9, shift: true, numLock: LockState.locked): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: false, collapseSelection: false),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad3, shift: true, numLock: LockState.locked): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: true, collapseSelection: false),
|
||||
|
||||
const SingleActivator(LogicalKeyboardKey.numpad7, shift: true, numLock: LockState.locked): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: false),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad1, shift: true, numLock: LockState.locked): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: true, collapseSelection: false),
|
||||
|
||||
const SingleActivator(LogicalKeyboardKey.numpadDecimal, shift: true, numLock: LockState.locked): const DeleteCharacterIntent(forward: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpadDecimal, shift: true, control: true, numLock: LockState.locked): const DeleteToNextWordBoundaryIntent(forward: true),
|
||||
|
||||
// When numLock is off, numpad keys shortcuts require shift not to be pressed.
|
||||
const SingleActivator(LogicalKeyboardKey.numpad6, numLock: LockState.unlocked): const ExtendSelectionByCharacterIntent(forward: true, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad4, numLock: LockState.unlocked): const ExtendSelectionByCharacterIntent(forward: false, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad8, numLock: LockState.unlocked): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad2, numLock: LockState.unlocked): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: true, collapseSelection: true),
|
||||
|
||||
const SingleActivator(LogicalKeyboardKey.numpad6, control: true, numLock: LockState.unlocked): const ExtendSelectionToNextWordBoundaryIntent(forward: true, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad4, control: true, numLock: LockState.unlocked): const ExtendSelectionToNextWordBoundaryIntent(forward: false, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad8, control: true, numLock: LockState.unlocked): const ExtendSelectionToNextParagraphBoundaryIntent(forward: false, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad2, control: true, numLock: LockState.unlocked): const ExtendSelectionToNextParagraphBoundaryIntent(forward: true, collapseSelection: true),
|
||||
|
||||
const SingleActivator(LogicalKeyboardKey.numpad9, numLock: LockState.unlocked): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: false, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad3, numLock: LockState.unlocked): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: true, collapseSelection: true),
|
||||
|
||||
const SingleActivator(LogicalKeyboardKey.numpad7, numLock: LockState.unlocked): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpad1, numLock: LockState.unlocked): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: true, collapseSelection: true),
|
||||
|
||||
const SingleActivator(LogicalKeyboardKey.numpadDecimal, numLock: LockState.unlocked): const DeleteCharacterIntent(forward: true),
|
||||
const SingleActivator(LogicalKeyboardKey.numpadDecimal, control: true, numLock: LockState.unlocked): const DeleteToNextWordBoundaryIntent(forward: true),
|
||||
};
|
||||
|
||||
static final Map<ShortcutActivator, Intent> _linuxShortcuts = <ShortcutActivator, Intent>{
|
||||
..._commonShortcuts,
|
||||
..._linuxNumpadShortcuts,
|
||||
const SingleActivator(LogicalKeyboardKey.home): const ExtendSelectionToLineBreakIntent(forward: false, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.end): const ExtendSelectionToLineBreakIntent(forward: true, collapseSelection: true),
|
||||
const SingleActivator(LogicalKeyboardKey.home, shift: true): const ExtendSelectionToLineBreakIntent(forward: false, collapseSelection: false),
|
||||
|
|
|
@ -486,6 +486,185 @@ void main() {
|
|||
expect(state.lastIntent, isA<ExtendSelectionToLineBreakIntent>());
|
||||
}, variant: macOSOnly);
|
||||
}, skip: kIsWeb); // [intended] specific tests target non-web.
|
||||
|
||||
group('Linux does accept numpad shortcuts', () {
|
||||
testWidgets('when numlock is locked', (WidgetTester tester) async {
|
||||
final FocusNode editable = FocusNode();
|
||||
addTearDown(editable.dispose);
|
||||
final FocusNode spy = FocusNode();
|
||||
addTearDown(spy.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildSpyAboveEditableText(
|
||||
editableFocusNode: editable,
|
||||
spyFocusNode: spy,
|
||||
),
|
||||
);
|
||||
spy.requestFocus();
|
||||
await tester.pump();
|
||||
final ActionSpyState state = tester.state<ActionSpyState>(find.byType(ActionSpy));
|
||||
|
||||
// Lock NumLock.
|
||||
await tester.sendKeyEvent(LogicalKeyboardKey.numLock);
|
||||
expect(HardwareKeyboard.instance.lockModesEnabled.contains(KeyboardLockMode.numLock), isTrue);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad6, shift: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionByCharacterIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionByCharacterIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionByCharacterIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad4, shift: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionByCharacterIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionByCharacterIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionByCharacterIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad8, shift: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentLineIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad2, shift: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentLineIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad9, shift: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentPageIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentPageIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentPageIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad3, shift: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentPageIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentPageIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentPageIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad7, shift: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentLineIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad1, shift: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentLineIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpadDecimal, shift: true));
|
||||
expect(state.lastIntent, isA<DeleteCharacterIntent>());
|
||||
expect((state.lastIntent! as DeleteCharacterIntent).forward, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad6, shift: true, control: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionToNextWordBoundaryIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionToNextWordBoundaryIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionToNextWordBoundaryIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad4, shift: true, control: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionToNextWordBoundaryIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionToNextWordBoundaryIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionToNextWordBoundaryIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad8, shift: true, control: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionToNextParagraphBoundaryIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionToNextParagraphBoundaryIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionToNextParagraphBoundaryIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad2, shift: true, control: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionToNextParagraphBoundaryIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionToNextParagraphBoundaryIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionToNextParagraphBoundaryIntent).collapseSelection, false);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpadDecimal, shift: true, control: true));
|
||||
expect(state.lastIntent, isA<DeleteToNextWordBoundaryIntent>());
|
||||
expect((state.lastIntent! as DeleteToNextWordBoundaryIntent).forward, true);
|
||||
}, variant: TargetPlatformVariant.only(TargetPlatform.linux));
|
||||
|
||||
testWidgets('when numlock is unlocked', (WidgetTester tester) async {
|
||||
final FocusNode editable = FocusNode();
|
||||
addTearDown(editable.dispose);
|
||||
final FocusNode spy = FocusNode();
|
||||
addTearDown(spy.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
buildSpyAboveEditableText(
|
||||
editableFocusNode: editable,
|
||||
spyFocusNode: spy,
|
||||
),
|
||||
);
|
||||
spy.requestFocus();
|
||||
await tester.pump();
|
||||
final ActionSpyState state = tester.state<ActionSpyState>(find.byType(ActionSpy));
|
||||
|
||||
// Verify that NumLock is unlocked.
|
||||
expect(HardwareKeyboard.instance.lockModesEnabled.contains(KeyboardLockMode.numLock), isFalse);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad6));
|
||||
expect(state.lastIntent, isA<ExtendSelectionByCharacterIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionByCharacterIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionByCharacterIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad4));
|
||||
expect(state.lastIntent, isA<ExtendSelectionByCharacterIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionByCharacterIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionByCharacterIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad8));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentLineIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad2));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentLineIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad9));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentPageIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentPageIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentPageIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad3));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentPageIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentPageIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentPageIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad7));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentLineIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad1));
|
||||
expect(state.lastIntent, isA<ExtendSelectionVerticallyToAdjacentLineIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionVerticallyToAdjacentLineIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpadDecimal));
|
||||
expect(state.lastIntent, isA<DeleteCharacterIntent>());
|
||||
expect((state.lastIntent! as DeleteCharacterIntent).forward, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad6, control: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionToNextWordBoundaryIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionToNextWordBoundaryIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionToNextWordBoundaryIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad4, control: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionToNextWordBoundaryIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionToNextWordBoundaryIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionToNextWordBoundaryIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad8, control: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionToNextParagraphBoundaryIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionToNextParagraphBoundaryIntent).forward, false);
|
||||
expect((state.lastIntent! as ExtendSelectionToNextParagraphBoundaryIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpad2, control: true));
|
||||
expect(state.lastIntent, isA<ExtendSelectionToNextParagraphBoundaryIntent>());
|
||||
expect((state.lastIntent! as ExtendSelectionToNextParagraphBoundaryIntent).forward, true);
|
||||
expect((state.lastIntent! as ExtendSelectionToNextParagraphBoundaryIntent).collapseSelection, true);
|
||||
|
||||
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.numpadDecimal, control: true));
|
||||
expect(state.lastIntent, isA<DeleteToNextWordBoundaryIntent>());
|
||||
expect((state.lastIntent! as DeleteToNextWordBoundaryIntent).forward, true);
|
||||
}, variant: TargetPlatformVariant.only(TargetPlatform.linux));
|
||||
}, skip: kIsWeb); // [intended] specific tests target non-web.
|
||||
}
|
||||
|
||||
class ActionSpy extends StatefulWidget {
|
||||
|
@ -508,6 +687,8 @@ class ActionSpyState extends State<ActionSpy> {
|
|||
ExtendSelectionVerticallyToAdjacentLineIntent: CallbackAction<ExtendSelectionVerticallyToAdjacentLineIntent>(onInvoke: _captureIntent),
|
||||
ExtendSelectionToDocumentBoundaryIntent: CallbackAction<ExtendSelectionToDocumentBoundaryIntent>(onInvoke: _captureIntent),
|
||||
ExtendSelectionToNextWordBoundaryOrCaretLocationIntent: CallbackAction<ExtendSelectionToNextWordBoundaryOrCaretLocationIntent>(onInvoke: _captureIntent),
|
||||
ExtendSelectionToNextParagraphBoundaryIntent: CallbackAction<ExtendSelectionToNextParagraphBoundaryIntent>(onInvoke: _captureIntent),
|
||||
ExtendSelectionVerticallyToAdjacentPageIntent: CallbackAction<ExtendSelectionVerticallyToAdjacentPageIntent>(onInvoke: _captureIntent),
|
||||
|
||||
DeleteToLineBreakIntent: CallbackAction<DeleteToLineBreakIntent>(onInvoke: _captureIntent),
|
||||
DeleteToNextWordBoundaryIntent: CallbackAction<DeleteToNextWordBoundaryIntent>(onInvoke: _captureIntent),
|
||||
|
|
Loading…
Reference in a new issue