// 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.backspace, control: true, shift: pressShift): <String>['deleteBackwardByDecomposingPreviousCharacter:'], 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; }