Просмотр исходного кода

feat: implement logic that pressing enter key without shift key in text nodes

Lucas.Xu 2 лет назад
Родитель
Сommit
af2f6a03d5

+ 2 - 2
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<FlowyKeyEventHandler> defaultKeyEventHandler = [
   flowyDeleteNodesHandler,
   arrowKeysHandler,
   copyPasteKeysHandler,
-  enterInEdgeOfTextNodeHandler,
+  enterWithoutShiftInTextNodesHandler,
   updateTextStyleByCommandXHandler,
   whiteSpaceHandler,
 ];

+ 0 - 94
frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_in_edge_of_text_node_handler.dart

@@ -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;
-};

+ 105 - 0
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<TextNode>().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<TextNode> 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;
+};