| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/services.dart'; |
| |
| import 'binding.dart'; |
| |
| /// Processes text input events that were not handled by the framework. |
| abstract class TestTextInputKeyHandler { |
| /// Process key down event that was not handled by the framework. |
| Future<void> handleKeyDownEvent(LogicalKeyboardKey key); |
| |
| /// Process key up event that was not handled by the framework. |
| Future<void> handleKeyUpEvent(LogicalKeyboardKey key); |
| } |
| |
| /// MacOS specific key input handler. This class translates standard macOS text editing shortcuts |
| /// into appropriate selectors similarly to what NSTextInputContext does in Flutter Engine. |
| class MacOSTestTextInputKeyHandler extends TestTextInputKeyHandler { |
| /// Create a new macOS specific text input handler. |
| MacOSTestTextInputKeyHandler(this.client); |
| |
| /// ClientId of TextInput |
| final int client; |
| |
| Future<void> _sendSelectors(List<String> selectors) async { |
| await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger |
| .handlePlatformMessage( |
| SystemChannels.textInput.name, |
| SystemChannels.textInput.codec.encodeMethodCall( |
| MethodCall( |
| 'TextInputClient.performSelectors', <dynamic>[client, selectors]), |
| ), |
| (ByteData? data) {/* response from framework is discarded */}, |
| ); |
| } |
| |
| // These combinations must match NSStandardKeyBindingResponding. |
| static final Map<SingleActivator, List<String>> _macOSActivatorToSelectors = |
| <SingleActivator, List<String>>{ |
| for (final bool pressShift in const <bool>[ |
| true, |
| false |
| ]) ...<SingleActivator, List<String>>{ |
| SingleActivator(LogicalKeyboardKey.backspace, shift: pressShift): |
| <String>['deleteBackward:'], |
| SingleActivator(LogicalKeyboardKey.backspace, |
| alt: true, shift: pressShift): <String>['deleteWordBackward:'], |
| SingleActivator(LogicalKeyboardKey.backspace, |
| meta: true, shift: pressShift): <String>['deleteToBeginningOfLine:'], |
| SingleActivator(LogicalKeyboardKey.delete, shift: pressShift): <String>[ |
| 'deleteForward:' |
| ], |
| SingleActivator(LogicalKeyboardKey.delete, alt: true, shift: pressShift): |
| <String>['deleteWordForward:'], |
| SingleActivator(LogicalKeyboardKey.delete, meta: true, shift: pressShift): |
| <String>['deleteToEndOfLine:'], |
| }, |
| const SingleActivator(LogicalKeyboardKey.arrowLeft): <String>['moveLeft:'], |
| const SingleActivator(LogicalKeyboardKey.arrowRight): <String>[ |
| 'moveRight:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowUp): <String>['moveUp:'], |
| const SingleActivator(LogicalKeyboardKey.arrowDown): <String>['moveDown:'], |
| const SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): <String>[ |
| 'moveLeftAndModifySelection:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): <String>[ |
| 'moveRightAndModifySelection:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): <String>[ |
| 'moveUpAndModifySelection:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowDown, shift: true): <String>[ |
| 'moveDownAndModifySelection:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowLeft, alt: true): <String>[ |
| 'moveWordLeft:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowRight, alt: true): <String>[ |
| 'moveWordRight:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowUp, alt: true): <String>[ |
| 'moveBackward:', |
| 'moveToBeginningOfParagraph:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowDown, alt: true): <String>[ |
| 'moveForward:', |
| 'moveToEndOfParagraph:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowLeft, alt: true, shift: true): |
| <String>['moveWordLeftAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.arrowRight, |
| alt: true, shift: true): <String>['moveWordRightAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.arrowUp, alt: true, shift: true): |
| <String>['moveParagraphBackwardAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.arrowDown, alt: true, shift: true): |
| <String>['moveParagraphForwardAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.arrowLeft, meta: true): <String>[ |
| 'moveToLeftEndOfLine:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowRight, meta: true): <String>[ |
| 'moveToRightEndOfLine:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowUp, meta: true): <String>[ |
| 'moveToBeginningOfDocument:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowDown, meta: true): <String>[ |
| 'moveToEndOfDocument:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.arrowLeft, |
| meta: true, |
| shift: true): <String>['moveToLeftEndOfLineAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.arrowRight, |
| meta: true, |
| shift: true): <String>['moveToRightEndOfLineAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.arrowUp, meta: true, shift: true): |
| <String>['moveToBeginningOfDocumentAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.arrowDown, |
| meta: true, |
| shift: true): <String>['moveToEndOfDocumentAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.keyA, control: true, shift: true): |
| <String>['moveToBeginningOfParagraphAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.keyA, control: true): <String>[ |
| 'moveToBeginningOfParagraph:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyB, control: true, shift: true): |
| <String>['moveBackwardAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.keyB, control: true): <String>[ |
| 'moveBackward:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyE, control: true, shift: true): |
| <String>['moveToEndOfParagraphAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.keyE, control: true): <String>[ |
| 'moveToEndOfParagraph:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyF, control: true, shift: true): |
| <String>['moveForwardAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.keyF, control: true): <String>[ |
| 'moveForward:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyK, control: true): <String>[ |
| 'deleteToEndOfParagraph' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyL, control: true): <String>[ |
| 'centerSelectionInVisibleArea' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyN, control: true): <String>[ |
| 'moveDown:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyN, control: true, shift: true): |
| <String>['moveDownAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.keyO, control: true): <String>[ |
| 'insertNewlineIgnoringFieldEditor:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyP, control: true): <String>[ |
| 'moveUp:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyP, control: true, shift: true): |
| <String>['moveUpAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.keyT, control: true): <String>[ |
| 'transpose:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyV, control: true): <String>[ |
| 'pageDown:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.keyV, control: true, shift: true): |
| <String>['pageDownAndModifySelection:'], |
| const SingleActivator(LogicalKeyboardKey.keyY, control: true): <String>[ |
| 'yank:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.quoteSingle, control: true): |
| <String>['insertSingleQuoteIgnoringSubstitution:'], |
| const SingleActivator(LogicalKeyboardKey.quote, control: true): <String>[ |
| 'insertDoubleQuoteIgnoringSubstitution:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.home): <String>[ |
| 'scrollToBeginningOfDocument:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.end): <String>[ |
| 'scrollToEndOfDocument:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.home, shift: true): <String>[ |
| 'moveToBeginningOfDocumentAndModifySelection:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.end, shift: true): <String>[ |
| 'moveToEndOfDocumentAndModifySelection:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.pageUp): <String>['scrollPageUp:'], |
| const SingleActivator(LogicalKeyboardKey.pageDown): <String>[ |
| 'scrollPageDown:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.pageUp, shift: true): <String>[ |
| 'pageUpAndModifySelection:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.pageDown, shift: true): <String>[ |
| 'pageDownAndModifySelection:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.escape): <String>[ |
| 'cancelOperation:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.enter): <String>['insertNewline:'], |
| const SingleActivator(LogicalKeyboardKey.enter, alt: true): <String>[ |
| 'insertNewlineIgnoringFieldEditor:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.enter, control: true): <String>[ |
| 'insertLineBreak:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.tab): <String>['insertTab:'], |
| const SingleActivator(LogicalKeyboardKey.tab, alt: true): <String>[ |
| 'insertTabIgnoringFieldEditor:' |
| ], |
| const SingleActivator(LogicalKeyboardKey.tab, shift: true): <String>[ |
| 'insertBacktab:' |
| ], |
| }; |
| |
| @override |
| Future<void> handleKeyDownEvent(LogicalKeyboardKey key) async { |
| if (key == LogicalKeyboardKey.shift || |
| key == LogicalKeyboardKey.shiftLeft || |
| key == LogicalKeyboardKey.shiftRight) { |
| _shift = true; |
| } else if (key == LogicalKeyboardKey.alt || |
| key == LogicalKeyboardKey.altLeft || |
| key == LogicalKeyboardKey.altRight) { |
| _alt = true; |
| } else if (key == LogicalKeyboardKey.meta || |
| key == LogicalKeyboardKey.metaLeft || |
| key == LogicalKeyboardKey.metaRight) { |
| _meta = true; |
| } else if (key == LogicalKeyboardKey.control || |
| key == LogicalKeyboardKey.controlLeft || |
| key == LogicalKeyboardKey.controlRight) { |
| _control = true; |
| } else { |
| for (final MapEntry<SingleActivator, List<String>> entry |
| in _macOSActivatorToSelectors.entries) { |
| final SingleActivator activator = entry.key; |
| if (activator.triggers.first == key && |
| activator.shift == _shift && |
| activator.alt == _alt && |
| activator.meta == _meta && |
| activator.control == _control) { |
| await _sendSelectors(entry.value); |
| return; |
| } |
| } |
| } |
| } |
| |
| @override |
| Future<void> handleKeyUpEvent(LogicalKeyboardKey key) async { |
| if (key == LogicalKeyboardKey.shift || |
| key == LogicalKeyboardKey.shiftLeft || |
| key == LogicalKeyboardKey.shiftRight) { |
| _shift = false; |
| } else if (key == LogicalKeyboardKey.alt || |
| key == LogicalKeyboardKey.altLeft || |
| key == LogicalKeyboardKey.altRight) { |
| _alt = false; |
| } else if (key == LogicalKeyboardKey.meta || |
| key == LogicalKeyboardKey.metaLeft || |
| key == LogicalKeyboardKey.metaRight) { |
| _meta = false; |
| } else if (key == LogicalKeyboardKey.control || |
| key == LogicalKeyboardKey.controlLeft || |
| key == LogicalKeyboardKey.controlRight) { |
| _control = false; |
| } |
| } |
| |
| bool _shift = false; |
| bool _alt = false; |
| bool _meta = false; |
| bool _control = false; |
| } |