From 13acc3af860cfa7c1dbebb9562a1d16a8cf90c7b Mon Sep 17 00:00:00 2001 From: Morn Date: Wed, 16 Apr 2025 16:56:37 +0800 Subject: [PATCH] fix: auto focus on link name textfield after open the link_edit_menu (#7757) * fix: auto focus on link name textfield after open the link_edit_menu * chore: update pubspec.yaml --- .../desktop_toolbar/link/link_edit_menu.dart | 164 +++++++++++------- frontend/appflowy_flutter/pubspec.lock | 4 +- frontend/appflowy_flutter/pubspec.yaml | 2 +- 3 files changed, 100 insertions(+), 70 deletions(-) diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/desktop_toolbar/link/link_edit_menu.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/desktop_toolbar/link/link_edit_menu.dart index b6a4ad89ba..e90ee22a80 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/desktop_toolbar/link/link_edit_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/desktop_toolbar/link/link_edit_menu.dart @@ -13,6 +13,7 @@ import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; // ignore: implementation_imports import 'package:appflowy_editor/src/editor/util/link_util.dart'; +import 'package:flutter/services.dart'; import 'link_create_menu.dart'; import 'link_search_text_field.dart'; import 'link_styles.dart'; @@ -38,15 +39,14 @@ class LinkEditMenu extends StatefulWidget { } class _LinkEditMenuState extends State { - ValueChanged get onApply => widget.onApply; - ValueChanged get onRemoveLink => widget.onRemoveLink; VoidCallback get onDismiss => widget.onDismiss; late TextEditingController linkNameController = TextEditingController(text: linkInfo.name); - final textFocusNode = FocusNode(); + late FocusNode textFocusNode = FocusNode(onKeyEvent: onFocusKeyEvent); + late FocusNode menuFocusNode = FocusNode(onKeyEvent: onFocusKeyEvent); late LinkInfo linkInfo = widget.linkInfo; late LinkSearchTextField searchTextField; bool isShowingSearchResult = false; @@ -74,12 +74,14 @@ class _LinkEditMenuState extends State { if (mounted) setState(() {}); }, )..searchRecentViews(); + makeSureHasFocus(); } @override void dispose() { linkNameController.dispose(); textFocusNode.dispose(); + menuFocusNode.dispose(); searchTextField.dispose(); super.dispose(); } @@ -91,56 +93,59 @@ class _LinkEditMenuState extends State { final errorHeight = showErrorText ? 20.0 : 0.0; return GestureDetector( onTap: onDismiss, - child: Container( - width: 400, - height: 250 + (showingRecent ? 32 : 0), - color: Colors.white.withAlpha(1), - child: Stack( - children: [ - GestureDetector( - onTap: hideSearchResult, - child: Container( - width: 400, - height: 192 + errorHeight, - decoration: buildToolbarLinkDecoration(context), + child: Focus( + focusNode: menuFocusNode, + child: Container( + width: 400, + height: 250 + (showingRecent ? 32 : 0), + color: Colors.white.withAlpha(1), + child: Stack( + children: [ + GestureDetector( + onTap: hideSearchResult, + child: Container( + width: 400, + height: 192 + errorHeight, + decoration: buildToolbarLinkDecoration(context), + ), ), - ), - Positioned( - top: 16, - left: 20, - child: FlowyText.semibold( - LocaleKeys.document_toolbar_pageOrURL.tr(), - color: LinkStyle.textTertiary, - fontSize: 12, - figmaLineHeight: 16, + Positioned( + top: 16, + left: 20, + child: FlowyText.semibold( + LocaleKeys.document_toolbar_pageOrURL.tr(), + color: LinkStyle.textTertiary, + fontSize: 12, + figmaLineHeight: 16, + ), ), - ), - Positioned( - top: 80 + errorHeight, - left: 20, - child: FlowyText.semibold( - LocaleKeys.document_toolbar_linkName.tr(), - color: LinkStyle.textTertiary, - fontSize: 12, - figmaLineHeight: 16, + Positioned( + top: 80 + errorHeight, + left: 20, + child: FlowyText.semibold( + LocaleKeys.document_toolbar_linkName.tr(), + color: LinkStyle.textTertiary, + fontSize: 12, + figmaLineHeight: 16, + ), ), - ), - Positioned( - top: 144 + errorHeight, - left: 20, - child: buildButtons(), - ), - Positioned( - top: 100 + errorHeight, - left: 20, - child: buildNameTextField(), - ), - Positioned( - top: 36, - left: 20, - child: buildLinkField(), - ), - ], + Positioned( + top: 144 + errorHeight, + left: 20, + child: buildButtons(), + ), + Positioned( + top: 100 + errorHeight, + left: 20, + child: buildNameTextField(), + ), + Positioned( + top: 36, + left: 20, + child: buildLinkField(), + ), + ], + ), ), ), ); @@ -254,23 +259,7 @@ class _LinkEditMenuState extends State { fontColor: Colors.white, fillColor: LinkStyle.fillThemeThick, fontWeight: FontWeight.w400, - onPressed: () { - if (isShowingSearchResult) { - onConfirm(); - return; - } - if (linkInfo.link.isEmpty) { - widget.onRemoveLink(linkInfo); - return; - } - if (linkInfo.link.isEmpty || !isUri(linkInfo.link)) { - setState(() { - showErrorText = true; - }); - return; - } - widget.onApply.call(linkInfo); - }, + onPressed: onApply, ); }, ), @@ -287,6 +276,7 @@ class _LinkEditMenuState extends State { child: TextFormField( autovalidateMode: AutovalidateMode.onUserInteraction, focusNode: textFocusNode, + autofocus: true, textAlign: TextAlign.left, controller: linkNameController, style: TextStyle( @@ -395,6 +385,45 @@ class _LinkEditMenuState extends State { ); } + KeyEventResult onFocusKeyEvent(FocusNode node, KeyEvent key) { + if (key is! KeyDownEvent) return KeyEventResult.ignored; + if (key.logicalKey == LogicalKeyboardKey.enter) { + onApply(); + return KeyEventResult.handled; + } else if (key.logicalKey == LogicalKeyboardKey.escape) { + onDismiss(); + return KeyEventResult.handled; + } + return KeyEventResult.ignored; + } + + Future makeSureHasFocus() async { + final focusNode = textFocusNode; + if (!mounted || focusNode.hasFocus) return; + focusNode.requestFocus(); + WidgetsBinding.instance.addPostFrameCallback((_) { + makeSureHasFocus(); + }); + } + + void onApply() { + if (isShowingSearchResult) { + onConfirm(); + return; + } + if (linkInfo.link.isEmpty) { + widget.onRemoveLink(linkInfo); + return; + } + if (linkInfo.link.isEmpty || !isUri(linkInfo.link)) { + setState(() { + showErrorText = true; + }); + return; + } + widget.onApply.call(linkInfo); + } + void onConfirm() { searchTextField.onSearchResult( onLink: onLinkSelected, @@ -404,6 +433,7 @@ class _LinkEditMenuState extends State { searchTextField.unfocus(); }, ); + menuFocusNode.requestFocus(); } Future getPageView() async { diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index 063b770ce3..c871a41f7e 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -98,8 +98,8 @@ packages: dependency: "direct main" description: path: "." - ref: "2361899" - resolved-ref: "23618990b2f4ab88df67d50598b2b53cd6853e0a" + ref: "680222f" + resolved-ref: "680222f503f90d07c08c99c42764f9b08fd0f46c" url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "5.1.0" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index 3ca505e7a5..e8042d6a57 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -187,7 +187,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "2361899" + ref: "680222f" appflowy_editor_plugins: git: