TF-3911 Support keyboard shortcut actions ESC to close composer on web

This commit is contained in:
dab246
2025-07-29 10:28:42 +07:00
committed by Dat H. Pham
parent f169ff5feb
commit 6c86682111
10 changed files with 151 additions and 50 deletions
@@ -1,11 +1,14 @@
import 'package:core/utils/app_logger.dart';
import 'package:flutter/services.dart';
import 'package:tmail_ui_user/features/base/extensions/logical_key_set_helper.dart';
import 'package:tmail_ui_user/features/base/shortcut/composer/composer_action_shortcut_type.dart';
import 'package:tmail_ui_user/features/base/shortcut/mail/mail_list_action_shortcut_type.dart';
import 'package:tmail_ui_user/features/base/shortcut/mail/mail_view_action_shortcut_type.dart';
import 'package:tmail_ui_user/features/base/shortcut/search/search_action_shortcut_type.dart';
class AppShortcutManager {
static const int escapeKeyCode = 27;
static MailViewActionShortcutType? getMailViewActionFromEvent(KeyEvent event) {
final keysPressed = HardwareKeyboard.instance.logicalKeysPressed;
log('AppShortcutManager::getMailViewActionFromEvent: Keys pressed: $keysPressed');
@@ -53,4 +56,23 @@ class AppShortcutManager {
return null;
}
}
static ComposerActionShortcutType? getComposerActionFromEvent(KeyEvent event) {
final keysPressed = HardwareKeyboard.instance.logicalKeysPressed;
log('AppShortcutManager::getComposerActionFromEvent: Keys pressed: $keysPressed');
if (keysPressed.isOnly(LogicalKeyboardKey.escape)) {
return ComposerActionShortcutType.closeView;
} else {
return null;
}
}
static ComposerActionShortcutType? getComposerActionFromKeyCode(int? keyCode) {
log('AppShortcutManager::getComposerActionFromKeyCode: Keys pressed: $keyCode');
if (keyCode == escapeKeyCode) {
return ComposerActionShortcutType.closeView;
} else {
return null;
}
}
}
@@ -0,0 +1,4 @@
enum ComposerActionShortcutType {
closeView;
}
@@ -61,6 +61,7 @@ import 'package:tmail_ui_user/features/composer/presentation/extensions/auto_cre
import 'package:tmail_ui_user/features/composer/presentation/extensions/get_draft_mailbox_id_for_composer_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/get_outbox_mailbox_id_for_composer_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/get_sent_mailbox_id_for_composer_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_keyboard_shortcut_actions_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_message_failure_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_recipients_collapsed_extensions.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/list_identities_extension.dart';
@@ -208,6 +209,7 @@ class ComposerController extends BaseController
FocusNode? ccAddressFocusNodeKeyboard;
FocusNode? bccAddressFocusNodeKeyboard;
FocusNode? replyToAddressFocusNodeKeyboard;
FocusNode? keyboardShortcutFocusNode;
StreamSubscription<html.Event>? _subscriptionOnDragEnter;
StreamSubscription<html.Event>? _subscriptionOnDragOver;
@@ -258,7 +260,6 @@ class ComposerController extends BaseController
mailboxDashBoardController.ownEmailAddress.value;
late Worker uploadInlineImageWorker;
late Worker dashboardViewStateWorker;
late bool _isEmailBodyLoaded;
ComposerController(
@@ -299,6 +300,7 @@ class ComposerController extends BaseController
_listenStreamEvent();
_beforeReconnectManager.addListener(onBeforeReconnect);
_injectBinding();
onKeyboardShortcutInit();
}
@override
@@ -343,6 +345,7 @@ class ComposerController extends BaseController
} else {
richTextMobileTabletController = null;
}
onKeyboardShortcutDispose();
super.onClose();
}
@@ -374,7 +377,6 @@ class ComposerController extends BaseController
bccEmailAddressController.dispose();
replyToEmailAddressController.dispose();
uploadInlineImageWorker.dispose();
dashboardViewStateWorker.dispose();
scrollController.dispose();
scrollControllerEmailAddress.removeListener(_scrollControllerEmailAddressListener);
scrollControllerEmailAddress.dispose();
@@ -560,18 +562,7 @@ class ComposerController extends BaseController
}
void _scrollControllerEmailAddressListener() {
if (toEmailAddressController.text.isNotEmpty) {
keyToEmailTagEditor.currentState?.closeSuggestionBox();
}
if (ccEmailAddressController.text.isNotEmpty) {
keyCcEmailTagEditor.currentState?.closeSuggestionBox();
}
if (bccEmailAddressController.text.isNotEmpty) {
keyBccEmailTagEditor.currentState?.closeSuggestionBox();
}
if (replyToEmailAddressController.text.isNotEmpty) {
keyReplyToEmailTagEditor.currentState?.closeSuggestionBox();
}
_closeSuggestionBox();
}
void createFocusNodeInput() {
@@ -767,7 +758,7 @@ class ComposerController extends BaseController
}
_sendButtonState = ButtonState.disabled;
clearFocus(context);
clearFocus();
if (toEmailAddressController.text.isNotEmpty
|| ccEmailAddressController.text.isNotEmpty
@@ -1142,7 +1133,7 @@ class ComposerController extends BaseController
}
void openPickAttachmentMenu(BuildContext context, List<Widget> actionTiles) {
clearFocus(context);
clearFocus();
(ContextMenuBuilder(context)
..addHeader((ContextMenuHeaderBuilder(const Key('attachment_picker_context_menu_header_builder'))
@@ -1428,17 +1419,24 @@ class ComposerController extends BaseController
}
}
void clearFocus(BuildContext context) {
log('ComposerController::clearFocus:');
void clearFocus() {
if (PlatformInfo.isMobile) {
htmlEditorApi?.unfocus();
FocusManager.instance.primaryFocus?.unfocus();
} else {
toAddressFocusNode?.unfocus();
ccAddressFocusNode?.unfocus();
bccAddressFocusNode?.unfocus();
replyToAddressFocusNode?.unfocus();
}
FocusScope.of(context).unfocus();
}
void clickOutsideComposer(BuildContext context) {
clearFocus(context);
void clickOutsideComposer() {
clearFocus();
triggerHideRecipientsFiledsWhenUnfocus();
if (PlatformInfo.isWeb) {
refocusKeyboardShortcutFocus();
}
}
void _closeComposerAction({dynamic result, bool closeOverlays = false}) {
@@ -1655,8 +1653,8 @@ class ComposerController extends BaseController
}
}
void insertImage(BuildContext context, double maxWith) async {
clearFocus(context);
void insertImage(BuildContext context, double maxWith) {
clearFocus();
if (responsiveUtils.isMobile(context)) {
maxWithEditor = maxWith - 40;
@@ -1693,8 +1691,8 @@ class ComposerController extends BaseController
}
}
void handleClickDeleteComposer(BuildContext context) {
clearFocus(context);
void handleClickDeleteComposer() {
clearFocus();
_closeComposerAction();
}
@@ -1944,11 +1942,12 @@ class ComposerController extends BaseController
}
_closeComposerButtonState = ButtonState.disabled;
autoCreateEmailTag();
if (_validateCloseComposerWithoutSave()) {
log('ComposerController::handleClickCloseComposer: ARGUMENTS is NULL or EMAIL NOT LOADED');
_closeComposerButtonState = ButtonState.enabled;
clearFocus(context);
clearFocus();
_closeComposerAction();
return;
}
@@ -1956,16 +1955,14 @@ class ComposerController extends BaseController
final isChanged = await _validateEmailChange();
if (isChanged && context.mounted) {
clearFocus(context);
clearFocus();
await _showConfirmDialogSaveMessage(context);
return;
}
if (context.mounted) {
_closeComposerButtonState = ButtonState.enabled;
clearFocus(context);
_closeComposerAction();
}
_closeComposerButtonState = ButtonState.enabled;
clearFocus();
_closeComposerAction();
}
bool _validateCloseComposerWithoutSave() {
@@ -45,7 +45,7 @@ class ComposerView extends GetWidget<ComposerController> {
responsiveUtils: controller.responsiveUtils,
mobile: MobileContainerView(
onCloseViewAction: () => controller.handleClickCloseComposer(context),
onClearFocusAction: () => controller.clearFocus(context),
onClearFocusAction: controller.clearFocus,
backgroundColor: MobileAppBarComposerWidgetStyle.backgroundColor,
childBuilder: (_, constraints) => SafeArea(
left: !controller.responsiveUtils.isLandscapeMobile(context),
@@ -248,7 +248,7 @@ class ComposerView extends GetWidget<ComposerController> {
if (controller.isContentHeightExceeded.isTrue && PlatformInfo.isIOS) {
return ViewEntireMessageWithMessageClippedWidget(
buttonActionName: AppLocalizations.of(context).viewEntireMessage.toUpperCase(),
onViewEntireMessageAction: () => controller.viewEntireContent(context),
onViewEntireMessageAction: controller.viewEntireContent,
topPadding: 12,
);
} else {
@@ -268,7 +268,7 @@ class ComposerView extends GetWidget<ComposerController> {
tablet: TabletContainerView(
keyboardRichTextController: controller.richTextMobileTabletController!.richTextController,
onCloseViewAction: () => controller.handleClickCloseComposer(context),
onClearFocusAction: () => controller.clearFocus(context),
onClearFocusAction: controller.clearFocus,
childBuilder: (_, constraints) => ColoredBox(
color: ComposerStyle.mobileBackgroundColor,
child: Column(
@@ -427,7 +427,7 @@ class ComposerView extends GetWidget<ComposerController> {
if (controller.isContentHeightExceeded.isTrue && PlatformInfo.isIOS) {
return ViewEntireMessageWithMessageClippedWidget(
buttonActionName: AppLocalizations.of(context).viewEntireMessage.toUpperCase(),
onViewEntireMessageAction: () => controller.viewEntireContent(context),
onViewEntireMessageAction: controller.viewEntireContent,
topPadding: 12,
);
} else {
@@ -443,7 +443,7 @@ class ComposerView extends GetWidget<ComposerController> {
imagePaths: controller.imagePaths,
hasReadReceipt: controller.hasRequestReadReceipt.value,
isMarkAsImportant: controller.isMarkAsImportant.value,
deleteComposerAction: () => controller.handleClickDeleteComposer(context),
deleteComposerAction: controller.handleClickDeleteComposer,
saveToDraftAction: () => controller.handleClickSaveAsDraftsButton(context),
sendMessageAction: () => controller.handleClickSendButton(context),
requestReadReceiptAction: () => controller.toggleRequestReadReceipt(context),
@@ -7,10 +7,12 @@ import 'package:model/email/prefix_email_address.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/mixin/message_dialog_action_manager.dart';
import 'package:tmail_ui_user/features/base/widget/dialog_picker/color_dialog_picker.dart';
import 'package:tmail_ui_user/features/base/widget/keyboard/keyboard_handler_wrapper.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/composer_print_draft_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_edit_recipient_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_recipients_collapsed_extensions.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/handle_keyboard_shortcut_actions_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/mark_as_important_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/extensions/remove_draggable_email_address_between_recipient_fields_extension.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/prefix_recipient_state.dart';
@@ -67,12 +69,12 @@ class ComposerView extends GetWidget<ComposerController> {
}
});
return ResponsiveWidget(
final bodyWidget = ResponsiveWidget(
responsiveUtils: controller.responsiveUtils,
mobile: MobileResponsiveContainerView(
childBuilder: (_, constraints) {
return GestureDetector(
onTap: () => controller.clickOutsideComposer(context),
onTap: controller.clickOutsideComposer,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -96,7 +98,7 @@ class ComposerView extends GetWidget<ComposerController> {
toggleMarkAsImportantAction: () => controller.toggleMarkAsImportant(context),
saveToDraftsAction: () => controller.handleClickSaveAsDraftsButton(context),
saveToTemplateAction: () => controller.handleClickSaveAsTemplateButton(context),
deleteComposerAction: () => controller.handleClickDeleteComposer(context),
deleteComposerAction: controller.handleClickDeleteComposer,
)),
ConstrainedBox(
constraints: BoxConstraints(
@@ -231,6 +233,7 @@ class ComposerView extends GetWidget<ComposerController> {
uploadError: uploadError
),
onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb,
onKeyDownEditorAction: controller.onKeyDownEditorAction,
));
}
),
@@ -318,7 +321,7 @@ class ComposerView extends GetWidget<ComposerController> {
desktop: Obx(() => DesktopResponsiveContainerView(
childBuilder: (_, constraints) {
return GestureDetector(
onTap: () => controller.clickOutsideComposer(context),
onTap: controller.clickOutsideComposer,
child: Column(children: [
Obx(() => DesktopAppBarComposerWidget(
imagePaths: controller.imagePaths,
@@ -468,6 +471,7 @@ class ComposerView extends GetWidget<ComposerController> {
uploadError: uploadError
),
onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb,
onKeyDownEditorAction: controller.onKeyDownEditorAction,
);
});
}
@@ -514,7 +518,7 @@ class ComposerView extends GetWidget<ComposerController> {
openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions,
attachFileAction: () => controller.openFilePickerByType(context, FileType.any),
insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
deleteComposerAction: () => controller.handleClickDeleteComposer(context),
deleteComposerAction: controller.handleClickDeleteComposer,
saveToDraftAction: () => controller.handleClickSaveAsDraftsButton(context),
sendMessageAction: () => controller.handleClickSendButton(context),
isEmailChanged: controller.isEmailChanged.isTrue,
@@ -588,7 +592,7 @@ class ComposerView extends GetWidget<ComposerController> {
tablet: TabletResponsiveContainerView(
childBuilder: (_, constraints) {
return GestureDetector(
onTap: () => controller.clickOutsideComposer(context),
onTap: controller.clickOutsideComposer,
child: Column(children: [
Obx(() => DesktopAppBarComposerWidget(
imagePaths: controller.imagePaths,
@@ -735,6 +739,7 @@ class ComposerView extends GetWidget<ComposerController> {
uploadError: uploadError
),
onInitialContentLoadComplete: controller.onInitialContentLoadCompleteWeb,
onKeyDownEditorAction: controller.onKeyDownEditorAction,
));
}
),
@@ -780,7 +785,7 @@ class ComposerView extends GetWidget<ComposerController> {
openRichToolbarAction: controller.richTextWebController!.toggleFormattingOptions,
attachFileAction: () => controller.openFilePickerByType(context, FileType.any),
insertImageAction: () => controller.insertImage(context, constraints.maxWidth),
deleteComposerAction: () => controller.handleClickDeleteComposer(context),
deleteComposerAction: controller.handleClickDeleteComposer,
saveToDraftAction: () => controller.handleClickSaveAsDraftsButton(context),
sendMessageAction: () => controller.handleClickSendButton(context),
isEmailChanged: controller.isEmailChanged.isTrue,
@@ -844,6 +849,16 @@ class ComposerView extends GetWidget<ComposerController> {
},
)
);
if (controller.keyboardShortcutFocusNode != null) {
return KeyboardHandlerWrapper(
focusNode: controller.keyboardShortcutFocusNode!,
onKeyDownEventAction: controller.onKeyDownEventAction,
child: bodyWidget,
);
} else {
return bodyWidget;
}
}
Widget _buildRecipientComposerWidget({
@@ -2,7 +2,6 @@
import 'package:core/presentation/constants/constants_ui.dart';
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/utils/app_logger.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/features/composer/presentation/view/mobile/editor_fullscreen_dialog_view.dart';
@@ -14,8 +13,8 @@ extension HandleContentHeightExceededExtension on ComposerController {
isContentHeightExceeded.value = height == ConstantsUI.composerHtmlContentMaxHeight;
}
Future<void> viewEntireContent(BuildContext context) async {
clearFocus(context);
Future<void> viewEntireContent() async {
clearFocus();
final currentContent = await getContentInEditor();
log('HandleContentHeightExceededExtension::showComposerFullscreen:currentContent = $currentContent');
@@ -0,0 +1,52 @@
import 'package:core/utils/app_logger.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart' hide SearchController;
import 'package:tmail_ui_user/features/base/shortcut/app_shortcut_manager.dart';
import 'package:tmail_ui_user/features/base/shortcut/composer/composer_action_shortcut_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
import 'package:tmail_ui_user/main/routes/route_navigation.dart';
extension HandleKeyboardShortcutActionsExtension on ComposerController {
void onKeyDownEditorAction(int? keyCode) {
log('$runtimeType::onKeyDownEditorAction:🔥keyCode: $keyCode');
final shortcutType = AppShortcutManager.getComposerActionFromKeyCode(keyCode);
log('$runtimeType::onKeyDownEditorAction:🔥Shortcut triggered: $shortcutType');
if (shortcutType == null) return;
handleComposerShortcutAction(shortcutType);
}
void onKeyboardShortcutInit() {
if (PlatformInfo.isWeb) {
keyboardShortcutFocusNode = FocusNode();
keyboardShortcutFocusNode?.addListener(() {
log('$runtimeType::onKeyboardShortcutInit: keyboardShortcutFocusNode: ${keyboardShortcutFocusNode?.hasFocus}');
});
}
}
void onKeyboardShortcutDispose() {
if (PlatformInfo.isWeb) {
keyboardShortcutFocusNode?.dispose();
}
}
void refocusKeyboardShortcutFocus() {
if (keyboardShortcutFocusNode?.hasFocus == false) {
keyboardShortcutFocusNode?.requestFocus();
}
}
void onKeyDownEventAction(KeyEvent event) {
final shortcutType = AppShortcutManager.getComposerActionFromEvent(event);
log('$runtimeType::onKeyDownEventAction:🔥Shortcut triggered: $shortcutType');
if (shortcutType == null) return;
handleComposerShortcutAction(shortcutType);
}
void handleComposerShortcutAction(ComposerActionShortcutType shortcutType) {
log('$runtimeType::handleComposerShortcutAction: 🔥 Shortcut triggered: $shortcutType');
if (currentContext == null) return;
handleClickCloseComposer(currentContext!);
}
}
@@ -57,7 +57,7 @@ extension HandleOpenContextMenuExtension on ComposerController {
handleClickSaveAsTemplateButton(context);
break;
case ComposerActionType.delete:
handleClickDeleteComposer(context);
handleClickDeleteComposer();
break;
}
}
@@ -34,6 +34,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
final OnPasteImageSuccessAction? onPasteImageSuccessAction;
final OnPasteImageFailureAction? onPasteImageFailureAction;
final OnInitialContentLoadComplete? onInitialContentLoadComplete;
final OnKeyDownEditorAction? onKeyDownEditorAction;
const WebEditorView({
super.key,
@@ -55,6 +56,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
this.onPasteImageSuccessAction,
this.onPasteImageFailureAction,
this.onInitialContentLoadComplete,
this.onKeyDownEditorAction,
});
@override
@@ -85,6 +87,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onPasteImageSuccessAction: onPasteImageSuccessAction,
onPasteImageFailureAction: onPasteImageFailureAction,
onInitialContentLoadComplete: onInitialContentLoadComplete,
onKeyDownEditorAction: onKeyDownEditorAction,
);
case EmailActionType.editDraft:
case EmailActionType.editSendingEmail:
@@ -115,6 +118,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onPasteImageSuccessAction: onPasteImageSuccessAction,
onPasteImageFailureAction: onPasteImageFailureAction,
onInitialContentLoadComplete: onInitialContentLoadComplete,
onKeyDownEditorAction: onKeyDownEditorAction,
),
(success) {
if (success is GetEmailContentLoading) {
@@ -147,6 +151,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onPasteImageSuccessAction: onPasteImageSuccessAction,
onPasteImageFailureAction: onPasteImageFailureAction,
onInitialContentLoadComplete: onInitialContentLoadComplete,
onKeyDownEditorAction: onKeyDownEditorAction,
);
}
}
@@ -185,6 +190,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onPasteImageSuccessAction: onPasteImageSuccessAction,
onPasteImageFailureAction: onPasteImageFailureAction,
onInitialContentLoadComplete: onInitialContentLoadComplete,
onKeyDownEditorAction: onKeyDownEditorAction,
);
},
(success) {
@@ -218,6 +224,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onPasteImageSuccessAction: onPasteImageSuccessAction,
onPasteImageFailureAction: onPasteImageFailureAction,
onInitialContentLoadComplete: onInitialContentLoadComplete,
onKeyDownEditorAction: onKeyDownEditorAction,
);
}
}
@@ -241,6 +248,7 @@ class WebEditorView extends StatelessWidget with EditorViewMixin {
onPasteImageSuccessAction: onPasteImageSuccessAction,
onPasteImageFailureAction: onPasteImageFailureAction,
onInitialContentLoadComplete: onInitialContentLoadComplete,
onKeyDownEditorAction: onKeyDownEditorAction,
);
}
}
@@ -23,6 +23,7 @@ typedef OnPasteImageFailureAction = Function(
String? base64,
UploadError uploadError);
typedef OnInitialContentLoadComplete = Function(String text);
typedef OnKeyDownEditorAction = Function(int? keyCode);
class WebEditorWidget extends StatefulWidget {
@@ -43,6 +44,7 @@ class WebEditorWidget extends StatefulWidget {
final OnPasteImageSuccessAction? onPasteImageSuccessAction;
final OnPasteImageFailureAction? onPasteImageFailureAction;
final OnInitialContentLoadComplete? onInitialContentLoadComplete;
final OnKeyDownEditorAction? onKeyDownEditorAction;
const WebEditorWidget({
super.key,
@@ -63,6 +65,7 @@ class WebEditorWidget extends StatefulWidget {
this.onPasteImageSuccessAction,
this.onPasteImageFailureAction,
this.onInitialContentLoadComplete,
this.onKeyDownEditorAction,
});
@override
@@ -76,7 +79,7 @@ class _WebEditorState extends State<WebEditorWidget> {
late HtmlEditorController _editorController;
bool _dropListenerRegistered = false;
Function(Event)? _dropListener;
OverlayEntry? _signatureTooltipEntry;
final GlobalKey _signatureTooltipKey = GlobalKey();
double _signatureTooltipLeft = 0;
@@ -197,7 +200,7 @@ class _WebEditorState extends State<WebEditorWidget> {
_showSignatureTooltipAtPosition(
position,
isContentVisible
? AppLocalizations.of(context).hideSignature
? AppLocalizations.of(context).hideSignature
: AppLocalizations.of(context).showSignature,
);
},
@@ -205,6 +208,7 @@ class _WebEditorState extends State<WebEditorWidget> {
log('_WebEditorState::build: onSignatureHoverOut');
_hideSignatureTooltip();
},
onKeyDown: widget.onKeyDownEditorAction,
),
);
}