Kaynağa Gözat

Merge pull request #792 from LucasXu0/feat/mouse_cursor_style

Feat/mouse cursor style
Nathan.fooo 2 yıl önce
ebeveyn
işleme
b9de45a110

+ 1 - 1
frontend/app_flowy/packages/flowy_editor/lib/document/node.dart

@@ -183,7 +183,7 @@ class TextNode extends Node {
         super(children: children ?? LinkedList(), attributes: attributes ?? {});
         super(children: children ?? LinkedList(), attributes: attributes ?? {});
 
 
   TextNode.empty()
   TextNode.empty()
-      : _delta = Delta([TextInsert(' ')]),
+      : _delta = Delta([TextInsert('')]),
         super(
         super(
           type: 'text',
           type: 'text',
           children: LinkedList(),
           children: LinkedList(),

+ 1 - 1
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/checkbox_text.dart

@@ -77,7 +77,7 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
             debugPrint('[Checkbox] onTap...');
             debugPrint('[Checkbox] onTap...');
             TransactionBuilder(widget.editorState)
             TransactionBuilder(widget.editorState)
               ..updateNode(widget.textNode, {
               ..updateNode(widget.textNode, {
-                'checkbox': !check,
+                StyleKey.checkbox: !check,
               })
               })
               ..commit();
               ..commit();
           },
           },

+ 11 - 6
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart

@@ -35,7 +35,7 @@ class FlowyRichText extends StatefulWidget {
     this.cursorHeight,
     this.cursorHeight,
     this.cursorWidth = 2.0,
     this.cursorWidth = 2.0,
     this.textSpanDecorator,
     this.textSpanDecorator,
-    this.placeholderText = 'Type \'/\' for commands',
+    this.placeholderText = ' ',
     this.placeholderTextSpanDecorator,
     this.placeholderTextSpanDecorator,
     required this.textNode,
     required this.textNode,
     required this.editorState,
     required this.editorState,
@@ -138,11 +138,16 @@ class _FlowyRichTextState extends State<FlowyRichText> with Selectable {
   }
   }
 
 
   Widget _buildRichText(BuildContext context) {
   Widget _buildRichText(BuildContext context) {
-    return Stack(
-      children: [
-        _buildPlaceholderText(context),
-        _buildSingleRichText(context),
-      ],
+    return MouseRegion(
+      cursor: SystemMouseCursors.text,
+      child: widget.textNode.toRawString().isEmpty
+          ? Stack(
+              children: [
+                _buildPlaceholderText(context),
+                _buildSingleRichText(context),
+              ],
+            )
+          : _buildSingleRichText(context),
     );
     );
   }
   }
 
 

+ 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/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_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/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/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/update_text_style_by_command_x_handler.dart';
 import 'package:flowy_editor/service/internal_key_event_handlers/whitespace_handler.dart';
 import 'package:flowy_editor/service/internal_key_event_handlers/whitespace_handler.dart';
@@ -39,7 +39,7 @@ List<FlowyKeyEventHandler> defaultKeyEventHandler = [
   flowyDeleteNodesHandler,
   flowyDeleteNodesHandler,
   arrowKeysHandler,
   arrowKeysHandler,
   copyPasteKeysHandler,
   copyPasteKeysHandler,
-  enterInEdgeOfTextNodeHandler,
+  enterWithoutShiftInTextNodesHandler,
   updateTextStyleByCommandXHandler,
   updateTextStyleByCommandXHandler,
   whiteSpaceHandler,
   whiteSpaceHandler,
 ];
 ];

+ 7 - 1
frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/delete_text_handler.dart

@@ -46,8 +46,14 @@ FlowyKeyEventHandler deleteTextHandler = (editorState, event) {
           if (textNode.previous is TextNode) {
           if (textNode.previous is TextNode) {
             final previous = textNode.previous as TextNode;
             final previous = textNode.previous as TextNode;
             transactionBuilder
             transactionBuilder
+              ..mergeText(previous, textNode)
               ..deleteNode(textNode)
               ..deleteNode(textNode)
-              ..mergeText(previous, textNode);
+              ..afterSelection = Selection.collapsed(
+                Position(
+                  path: previous.path,
+                  offset: previous.toRawString().length,
+                ),
+              );
             break;
             break;
           }
           }
         }
         }

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

+ 106 - 0
frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart

@@ -0,0 +1,106 @@
+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 enterWithoutShiftInTextNodesHandler =
+    (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)
+    ..insertNode(
+      textNode.path.next,
+      textNode.copyWith(
+        attributes: needCopyAttributes ? textNode.attributes : {},
+        delta: textNode.delta.slice(selection.end.offset),
+      ),
+    )
+    ..deleteText(
+      textNode,
+      selection.start.offset,
+      textNode.toRawString().length - selection.start.offset,
+    )
+    ..afterSelection = afterSelection
+    ..commit();
+  return KeyEventResult.handled;
+};