Bläddra i källkod

Merge pull request #824 from AppFlowy-IO/feat/handle-edit-keys

Feat: handle edit keys
Vincent Chan 2 år sedan
förälder
incheckning
c53df59b48

+ 0 - 1
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';

+ 4 - 0
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<FlowyKeyEventHandler> defaultKeyEventHandlers = [
@@ -19,4 +21,6 @@ List<FlowyKeyEventHandler> defaultKeyEventHandlers = [
   enterWithoutShiftInTextNodesHandler,
   updateTextStyleByCommandXHandler,
   whiteSpaceHandler,
+  selectAllHandler,
+  pageUpDownHandler,
 ];

+ 83 - 22
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<TextNode>().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<TextNode> 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;
 };

+ 31 - 0
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;
+};

+ 26 - 0
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;
+};