diff --git a/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart index 0b87fc0fd4..a5f392a4eb 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/copy_paste_handler.dart @@ -1,5 +1,4 @@ import 'package:flowy_editor/flowy_editor.dart'; -import 'package:flowy_editor/src/service/keyboard_service.dart'; import 'package:flowy_editor/src/infra/html_converter.dart'; import 'package:flowy_editor/src/document/node_iterator.dart'; import 'package:flutter/material.dart'; diff --git a/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/default_key_event_handlers.dart b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/default_key_event_handlers.dart index d03211461c..e617804a77 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/default_key_event_handlers.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/default_key_event_handlers.dart @@ -7,6 +7,8 @@ import 'package:flowy_editor/src/service/internal_key_event_handlers/redo_undo_h import 'package:flowy_editor/src/service/internal_key_event_handlers/slash_handler.dart'; import 'package:flowy_editor/src/service/internal_key_event_handlers/update_text_style_by_command_x_handler.dart'; import 'package:flowy_editor/src/service/internal_key_event_handlers/whitespace_handler.dart'; +import 'package:flowy_editor/src/service/internal_key_event_handlers/select_all_handler.dart'; +import 'package:flowy_editor/src/service/internal_key_event_handlers/page_up_down_handler.dart'; import 'package:flowy_editor/src/service/keyboard_service.dart'; List defaultKeyEventHandlers = [ @@ -19,4 +21,6 @@ List defaultKeyEventHandlers = [ enterWithoutShiftInTextNodesHandler, updateTextStyleByCommandXHandler, whiteSpaceHandler, + selectAllHandler, + pageUpDownHandler, ]; diff --git a/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/delete_text_handler.dart b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/delete_text_handler.dart index 5e2fdfd453..5e96e75a99 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/delete_text_handler.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/delete_text_handler.dart @@ -2,14 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flowy_editor/flowy_editor.dart'; -import 'package:flowy_editor/src/service/keyboard_service.dart'; - -// Handle delete text. -FlowyKeyEventHandler deleteTextHandler = (editorState, event) { - if (event.logicalKey != LogicalKeyboardKey.backspace) { - return KeyEventResult.ignored; - } +KeyEventResult _handleBackspace(EditorState editorState, RawKeyEvent event) { final selection = editorState.service.selectionService.currentSelection.value; if (selection == null) { return KeyEventResult.ignored; @@ -22,7 +16,7 @@ FlowyKeyEventHandler deleteTextHandler = (editorState, event) { return KeyEventResult.ignored; } - TransactionBuilder transactionBuilder = TransactionBuilder(editorState); + final transactionBuilder = TransactionBuilder(editorState); if (textNodes.length == 1) { final textNode = textNodes.first; final index = textNode.delta.prevRunePosition(selection.start.offset); @@ -74,23 +68,90 @@ FlowyKeyEventHandler deleteTextHandler = (editorState, event) { } } } else { - final first = textNodes.first; - final last = textNodes.last; - var content = textNodes.last.toRawString(); - content = content.substring(selection.end.offset, content.length); - // Merge the fist and the last text node content, - // and delete the all nodes expect for the first. - transactionBuilder - ..deleteNodes(textNodes.sublist(1)) - ..mergeText( - first, - last, - firstOffset: selection.start.offset, - secondOffset: selection.end.offset, - ); + _deleteNodes(transactionBuilder, textNodes, selection); } transactionBuilder.commit(); return KeyEventResult.handled; +} + +KeyEventResult _handleDelete(EditorState editorState, RawKeyEvent event) { + final selection = editorState.service.selectionService.currentSelection.value; + if (selection == null) { + return KeyEventResult.ignored; + } + final nodes = editorState.service.selectionService.currentSelectedNodes; + // make sure all nodes is [TextNode]. + final textNodes = nodes.whereType().toList(); + if (textNodes.length != nodes.length) { + return KeyEventResult.ignored; + } + + final transactionBuilder = TransactionBuilder(editorState); + if (textNodes.length == 1) { + final textNode = textNodes.first; + if (selection.start.offset >= textNode.delta.length) { + debugPrint("merge next line"); + final nextNode = textNode.next; + if (nextNode == null) { + return KeyEventResult.ignored; + } + if (nextNode is TextNode) { + transactionBuilder.mergeText(textNode, nextNode); + } + transactionBuilder.deleteNode(nextNode); + } else { + final index = textNode.delta.nextRunePosition(selection.start.offset); + if (selection.isCollapsed) { + transactionBuilder.deleteText( + textNode, + selection.start.offset, + index - selection.start.offset, + ); + } else { + transactionBuilder.deleteText( + textNode, + selection.start.offset, + selection.end.offset - selection.start.offset, + ); + } + } + } else { + _deleteNodes(transactionBuilder, textNodes, selection); + } + + transactionBuilder.commit(); + + return KeyEventResult.handled; +} + +void _deleteNodes(TransactionBuilder transactionBuilder, + List textNodes, Selection selection) { + final first = textNodes.first; + final last = textNodes.last; + var content = textNodes.last.toRawString(); + content = content.substring(selection.end.offset, content.length); + // Merge the fist and the last text node content, + // and delete the all nodes expect for the first. + transactionBuilder + ..deleteNodes(textNodes.sublist(1)) + ..mergeText( + first, + last, + firstOffset: selection.start.offset, + secondOffset: selection.end.offset, + ); +} + +// Handle delete text. +FlowyKeyEventHandler deleteTextHandler = (editorState, event) { + if (event.logicalKey == LogicalKeyboardKey.backspace) { + return _handleBackspace(editorState, event); + } + if (event.logicalKey == LogicalKeyboardKey.delete) { + return _handleDelete(editorState, event); + } + + return KeyEventResult.ignored; }; diff --git a/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/page_up_down_handler.dart b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/page_up_down_handler.dart new file mode 100644 index 0000000000..5acdcdcb0d --- /dev/null +++ b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/page_up_down_handler.dart @@ -0,0 +1,31 @@ +import 'package:flowy_editor/flowy_editor.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +double? getEditorHeight(EditorState editorState) { + final renderObj = + editorState.service.scrollServiceKey.currentContext?.findRenderObject(); + if (renderObj is RenderBox) { + return renderObj.size.height; + } + return null; +} + +FlowyKeyEventHandler pageUpDownHandler = (editorState, event) { + if (event.logicalKey == LogicalKeyboardKey.pageUp) { + final scrollHeight = getEditorHeight(editorState); + final scrollService = editorState.service.scrollService; + if (scrollHeight != null && scrollService != null) { + scrollService.scrollTo(scrollService.dy - scrollHeight); + } + return KeyEventResult.handled; + } else if (event.logicalKey == LogicalKeyboardKey.pageDown) { + final scrollHeight = getEditorHeight(editorState); + final scrollService = editorState.service.scrollService; + if (scrollHeight != null && scrollService != null) { + scrollService.scrollTo(scrollService.dy + scrollHeight); + } + return KeyEventResult.handled; + } + return KeyEventResult.ignored; +}; diff --git a/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/select_all_handler.dart b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/select_all_handler.dart new file mode 100644 index 0000000000..f99569218c --- /dev/null +++ b/frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/select_all_handler.dart @@ -0,0 +1,26 @@ +import 'package:flowy_editor/flowy_editor.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +KeyEventResult _selectAll(EditorState editorState) { + if (editorState.document.root.children.isEmpty) { + return KeyEventResult.handled; + } + final firstNode = editorState.document.root.children.first; + final lastNode = editorState.document.root.children.last; + var offset = 0; + if (lastNode is TextNode) { + offset = lastNode.delta.length; + } + editorState.updateCursorSelection(Selection( + start: Position(path: firstNode.path, offset: 0), + end: Position(path: lastNode.path, offset: offset))); + return KeyEventResult.handled; +} + +FlowyKeyEventHandler selectAllHandler = (editorState, event) { + if (event.isMetaPressed && event.logicalKey == LogicalKeyboardKey.keyA) { + return _selectAll(editorState); + } + return KeyEventResult.ignored; +};