feat: implement command + K to trigger link menu

This commit is contained in:
Lucas.Xu 2022-08-22 17:51:47 +08:00
parent 1f90f30274
commit 886c1f00e5
4 changed files with 49 additions and 12 deletions

View File

@ -21,12 +21,21 @@ class LinkMenu extends StatefulWidget {
class _LinkMenuState extends State<LinkMenu> {
final _textEditingController = TextEditingController();
final _focusNode = FocusNode();
@override
void initState() {
super.initState();
_textEditingController.text = widget.linkText ?? '';
_focusNode.requestFocus();
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
@ -88,7 +97,7 @@ class _LinkMenuState extends State<LinkMenu> {
Widget _buildInput() {
return TextField(
autofocus: true,
focusNode: _focusNode,
style: const TextStyle(fontSize: 14.0),
textAlign: TextAlign.left,
controller: _textEditingController,
@ -112,11 +121,7 @@ class _LinkMenuState extends State<LinkMenu> {
required VoidCallback onPressed,
}) {
return TextButton.icon(
icon: FlowySvg(
name: iconName,
width: 20.0,
height: 20.0,
),
icon: FlowySvg(name: iconName),
style: TextButton.styleFrom(
minimumSize: const Size.fromHeight(40),
padding: EdgeInsets.zero,

View File

@ -13,12 +13,14 @@ typedef ToolbarShowValidator = bool Function(EditorState editorState);
class ToolbarItem {
ToolbarItem({
required this.id,
required this.icon,
this.tooltipsMessage = '',
required this.validator,
required this.handler,
});
final String id;
final Widget icon;
final String tooltipsMessage;
final ToolbarShowValidator validator;
@ -26,6 +28,7 @@ class ToolbarItem {
factory ToolbarItem.divider() {
return ToolbarItem(
id: 'divider',
icon: const FlowySvg(name: 'toolbar/divider'),
validator: (editorState) => true,
handler: (editorState, context) {},
@ -35,18 +38,21 @@ class ToolbarItem {
List<ToolbarItem> defaultToolbarItems = [
ToolbarItem(
id: 'appflowy.toolbar.h1',
tooltipsMessage: 'Heading 1',
icon: const FlowySvg(name: 'toolbar/h1'),
validator: _onlyShowInSingleTextSelection,
handler: (editorState, context) => formatHeading(editorState, StyleKey.h1),
),
ToolbarItem(
id: 'appflowy.toolbar.h2',
tooltipsMessage: 'Heading 2',
icon: const FlowySvg(name: 'toolbar/h2'),
validator: _onlyShowInSingleTextSelection,
handler: (editorState, context) => formatHeading(editorState, StyleKey.h2),
),
ToolbarItem(
id: 'appflowy.toolbar.h3',
tooltipsMessage: 'Heading 3',
icon: const FlowySvg(name: 'toolbar/h3'),
validator: _onlyShowInSingleTextSelection,
@ -54,24 +60,28 @@ List<ToolbarItem> defaultToolbarItems = [
),
ToolbarItem.divider(),
ToolbarItem(
id: 'appflowy.toolbar.bold',
tooltipsMessage: 'Bold',
icon: const FlowySvg(name: 'toolbar/bold'),
validator: _showInTextSelection,
handler: (editorState, context) => formatBold(editorState),
),
ToolbarItem(
id: 'appflowy.toolbar.italic',
tooltipsMessage: 'Italic',
icon: const FlowySvg(name: 'toolbar/italic'),
validator: _showInTextSelection,
handler: (editorState, context) => formatItalic(editorState),
),
ToolbarItem(
id: 'appflowy.toolbar.underline',
tooltipsMessage: 'Underline',
icon: const FlowySvg(name: 'toolbar/underline'),
validator: _showInTextSelection,
handler: (editorState, context) => formatUnderline(editorState),
),
ToolbarItem(
id: 'appflowy.toolbar.strikethrough',
tooltipsMessage: 'Strikethrough',
icon: const FlowySvg(name: 'toolbar/strikethrough'),
validator: _showInTextSelection,
@ -79,12 +89,14 @@ List<ToolbarItem> defaultToolbarItems = [
),
ToolbarItem.divider(),
ToolbarItem(
id: 'appflowy.toolbar.quote',
tooltipsMessage: 'Quote',
icon: const FlowySvg(name: 'toolbar/quote'),
validator: _onlyShowInSingleTextSelection,
handler: (editorState, context) => formatQuote(editorState),
),
ToolbarItem(
id: 'appflowy.toolbar.bulleted_list',
tooltipsMessage: 'Bulleted list',
icon: const FlowySvg(name: 'toolbar/bulleted_list'),
validator: _onlyShowInSingleTextSelection,
@ -92,12 +104,14 @@ List<ToolbarItem> defaultToolbarItems = [
),
ToolbarItem.divider(),
ToolbarItem(
id: 'appflowy.toolbar.link',
tooltipsMessage: 'Link',
icon: const FlowySvg(name: 'toolbar/link'),
validator: _onlyShowInSingleTextSelection,
handler: (editorState, context) => _showLinkMenu(editorState, context),
),
ToolbarItem(
id: 'appflowy.toolbar.highlight',
tooltipsMessage: 'Highlight',
icon: const FlowySvg(name: 'toolbar/highlight'),
validator: _showInTextSelection,
@ -152,9 +166,7 @@ void _showLinkMenu(EditorState editorState, BuildContext context) {
linkText: linkText,
onSubmitted: (text) {
TransactionBuilder(editorState)
..formatText(node, index, length, {
StyleKey.href: text,
})
..formatText(node, index, length, {StyleKey.href: text})
..commit();
_dismissLinkMenu();
},
@ -164,9 +176,7 @@ void _showLinkMenu(EditorState editorState, BuildContext context) {
},
onRemoveLink: () {
TransactionBuilder(editorState)
..formatText(node, index, length, {
StyleKey.href: null,
})
..formatText(node, index, length, {StyleKey.href: null})
..commit();
_dismissLinkMenu();
},
@ -177,6 +187,7 @@ void _showLinkMenu(EditorState editorState, BuildContext context) {
Overlay.of(context)?.insert(_linkMenuOverlay!);
editorState.service.scrollService?.disable();
editorState.service.keyboardService?.disable();
editorState.service.selectionService.currentSelection
.addListener(_dismissLinkMenu);
}
@ -186,6 +197,7 @@ void _dismissLinkMenu() {
_linkMenuOverlay = null;
_editorState?.service.scrollService?.enable();
_editorState?.service.keyboardService?.enable();
_editorState?.service.selectionService.currentSelection
.removeListener(_dismissLinkMenu);
_editorState = null;

View File

@ -36,6 +36,12 @@ AppFlowyKeyEventHandler updateTextStyleByCommandXHandler =
event.isShiftPressed) {
formatHighlight(editorState);
return KeyEventResult.handled;
} else if (event.logicalKey == LogicalKeyboardKey.keyK) {
if (editorState.service.toolbarService
?.triggerHandler('appflowy.toolbar.link') ==
true) {
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;

View File

@ -11,6 +11,9 @@ abstract class FlowyToolbarService {
/// Hide the toolbar widget.
void hide();
/// Trigger the specified handler.
bool triggerHandler(String id);
}
class FlowyToolbar extends StatefulWidget {
@ -55,6 +58,17 @@ class _FlowyToolbarState extends State<FlowyToolbar>
_toolbarOverlay = null;
}
@override
bool triggerHandler(String id) {
final items = defaultToolbarItems.where((item) => item.id == id);
if (items.length != 1) {
assert(items.length == 1, 'The toolbar item\'s id must be unique');
return false;
}
items.first.handler(widget.editorState, context);
return true;
}
@override
Widget build(BuildContext context) {
return Container(