From af2f6a03d561b2ea87c1c2cf0cf299403ed5b16c Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Mon, 8 Aug 2022 23:18:07 +0800 Subject: [PATCH] feat: implement logic that pressing enter key without shift key in text nodes --- .../lib/service/editor_service.dart | 4 +- .../enter_in_edge_of_text_node_handler.dart | 94 ---------------- ...er_without_shift_in_text_node_handler.dart | 105 ++++++++++++++++++ 3 files changed, 107 insertions(+), 96 deletions(-) delete mode 100644 frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_in_edge_of_text_node_handler.dart create mode 100644 frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/editor_service.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/editor_service.dart index ba3d883c77..78f7bb76fa 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/service/editor_service.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/service/editor_service.dart @@ -13,7 +13,7 @@ import 'package:flowy_editor/service/internal_key_event_handlers/arrow_keys_hand import 'package:flowy_editor/service/internal_key_event_handlers/copy_paste_handler.dart'; import 'package:flowy_editor/service/internal_key_event_handlers/delete_nodes_handler.dart'; import 'package:flowy_editor/service/internal_key_event_handlers/delete_text_handler.dart'; -import 'package:flowy_editor/service/internal_key_event_handlers/enter_in_edge_of_text_node_handler.dart'; +import 'package:flowy_editor/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart'; import 'package:flowy_editor/service/internal_key_event_handlers/slash_handler.dart'; import 'package:flowy_editor/service/internal_key_event_handlers/update_text_style_by_command_x_handler.dart'; import 'package:flowy_editor/service/internal_key_event_handlers/whitespace_handler.dart'; @@ -39,7 +39,7 @@ List defaultKeyEventHandler = [ flowyDeleteNodesHandler, arrowKeysHandler, copyPasteKeysHandler, - enterInEdgeOfTextNodeHandler, + enterWithoutShiftInTextNodesHandler, updateTextStyleByCommandXHandler, whiteSpaceHandler, ]; diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_in_edge_of_text_node_handler.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_in_edge_of_text_node_handler.dart deleted file mode 100644 index 9ea754e162..0000000000 --- a/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_in_edge_of_text_node_handler.dart +++ /dev/null @@ -1,94 +0,0 @@ -import 'dart:collection'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import 'package:flowy_editor/document/node.dart'; -import 'package:flowy_editor/document/position.dart'; -import 'package:flowy_editor/document/selection.dart'; -import 'package:flowy_editor/document/text_delta.dart'; -import 'package:flowy_editor/extensions/node_extensions.dart'; -import 'package:flowy_editor/extensions/path_extensions.dart'; -import 'package:flowy_editor/operation/transaction_builder.dart'; -import 'package:flowy_editor/render/rich_text/rich_text_style.dart'; -import 'package:flowy_editor/service/keyboard_service.dart'; - -FlowyKeyEventHandler enterInEdgeOfTextNodeHandler = (editorState, event) { - if (event.logicalKey != LogicalKeyboardKey.enter) { - return KeyEventResult.ignored; - } - - final nodes = editorState.service.selectionService.currentSelectedNodes; - final selection = editorState.service.selectionService.currentSelection.value; - if (selection == null || - nodes.length != 1 || - nodes.first is! TextNode || - !selection.isCollapsed) { - return KeyEventResult.ignored; - } - - final textNode = nodes.first as TextNode; - if (textNode.selectable!.end() == selection.end) { - if (textNode.subtype != null && textNode.delta.length == 0) { - TransactionBuilder(editorState) - ..deleteNode(textNode) - ..insertNode( - textNode.path, - textNode.copyWith( - children: LinkedList(), - delta: Delta([TextInsert('')]), - attributes: {}, - ), - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path, - offset: 0, - ), - ) - ..commit(); - } else { - final needCopyAttributes = StyleKey.globalStyleKeys - .where((key) => key != StyleKey.heading) - .contains(textNode.subtype); - TransactionBuilder(editorState) - ..insertNode( - textNode.path.next, - textNode.copyWith( - children: LinkedList(), - delta: Delta([TextInsert('')]), - attributes: needCopyAttributes ? textNode.attributes : {}, - ), - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path.next, - offset: 0, - ), - ) - ..commit(); - } - - return KeyEventResult.handled; - } else if (textNode.selectable!.start() == selection.start) { - TransactionBuilder(editorState) - ..insertNode( - textNode.path, - textNode.copyWith( - children: LinkedList(), - delta: Delta([TextInsert('')]), - attributes: {}, - ), - ) - ..afterSelection = Selection.collapsed( - Position( - path: textNode.path.next, - offset: 0, - ), - ) - ..commit(); - return KeyEventResult.handled; - } - - return KeyEventResult.ignored; -}; diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart new file mode 100644 index 0000000000..8a53c1e9f4 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'package:flowy_editor/document/node.dart'; +import 'package:flowy_editor/document/position.dart'; +import 'package:flowy_editor/document/selection.dart'; +import 'package:flowy_editor/extensions/path_extensions.dart'; +import 'package:flowy_editor/operation/transaction_builder.dart'; +import 'package:flowy_editor/render/rich_text/rich_text_style.dart'; +import 'package:flowy_editor/service/keyboard_service.dart'; + +/// Handle some cases where enter is pressed and shift is not pressed. +/// +/// 1. Multiple selection and the selected nodes are [TextNode] +/// 1.1 delete the nodes expect for the first and the last, +/// and delete the text in the first and the last node by case. +/// 2. Single selection and the selected node is [TextNode] +/// 2.1 split the node into two nodes with style +/// 2.2 or insert a empty text node before. +FlowyKeyEventHandler enterWithoutShiftHandler = (editorState, event) { + if (event.logicalKey != LogicalKeyboardKey.enter || event.isShiftPressed) { + return KeyEventResult.ignored; + } + + final nodes = editorState.service.selectionService.currentSelectedNodes; + final textNodes = nodes.whereType().toList(growable: false); + final selection = editorState.service.selectionService.currentSelection.value; + + if (selection == null || nodes.length != textNodes.length) { + return KeyEventResult.ignored; + } + + // Multiple selection + if (!selection.isSingle) { + final length = textNodes.length; + final List subTextNodes = + length >= 3 ? textNodes.sublist(1, textNodes.length - 2) : []; + final afterSelection = Selection.collapsed( + Position(path: textNodes.first.path.next, offset: 0), + ); + TransactionBuilder(editorState) + ..deleteText( + textNodes.first, + selection.start.offset, + textNodes.first.toRawString().length, + ) + ..deleteNodes(subTextNodes) + ..deleteText( + textNodes.last, + 0, + selection.end.offset, + ) + ..afterSelection = afterSelection + ..commit(); + return KeyEventResult.handled; + } + + // Single selection and the selected node is [TextNode] + if (textNodes.length != 1) { + return KeyEventResult.ignored; + } + + final textNode = textNodes.first; + + // If selection is collapsed and position.start.offset == 0, + // insert a empty text node before. + if (selection.isCollapsed && selection.start.offset == 0) { + final afterSelection = Selection.collapsed( + Position(path: textNode.path.next, offset: 0), + ); + TransactionBuilder(editorState) + ..insertNode( + textNode.path, + TextNode.empty(), + ) + ..afterSelection = afterSelection + ..commit(); + return KeyEventResult.handled; + } + + // Otherwise, + // split the node into two nodes with style + final needCopyAttributes = StyleKey.globalStyleKeys + .where((key) => key != StyleKey.heading) + .contains(textNode.subtype); + final afterSelection = Selection.collapsed( + Position(path: textNode.path.next, offset: 0), + ); + TransactionBuilder(editorState) + ..deleteText( + textNode, + selection.start.offset, + textNode.toRawString().length, + ) + ..insertNode( + textNode.path.next, + textNode.copyWith( + attributes: needCopyAttributes ? textNode.attributes : {}, + delta: textNode.delta.slice(selection.end.offset), + ), + ) + ..afterSelection = afterSelection + ..commit(); + return KeyEventResult.handled; +};