فهرست منبع

feat: Implement arrow up/down/left/right event handler. #708

Lucas.Xu 2 سال پیش
والد
کامیت
2f86cac8af

+ 10 - 0
frontend/app_flowy/packages/flowy_editor/example/lib/plugin/image_node_widget.dart

@@ -61,6 +61,16 @@ class __ImageNodeWidgetState extends State<_ImageNodeWidget> with Selectable {
     return Offset.zero;
   }
 
+  @override
+  Offset getLeftOfOffset() {
+    return Offset.zero;
+  }
+
+  @override
+  Offset getRightOfOffset() {
+    return Offset.zero;
+  }
+
   @override
   Widget build(BuildContext context) {
     return _build(context);

+ 30 - 0
frontend/app_flowy/packages/flowy_editor/example/lib/plugin/selected_text_node_widget.dart

@@ -1,3 +1,5 @@
+import 'dart:math';
+
 import 'package:example/plugin/debuggable_rich_text.dart';
 import 'package:flowy_editor/flowy_editor.dart';
 import 'package:flutter/foundation.dart';
@@ -108,6 +110,30 @@ class _SelectedTextNodeWidgetState extends State<_SelectedTextNodeWidget>
     return _renderParagraph.localToGlobal(offset);
   }
 
+  @override
+  Offset getLeftOfOffset() {
+    final textSelection = _textSelection;
+    if (textSelection != null) {
+      final leftTextSelection = TextSelection.collapsed(
+        offset: max(0, textSelection.baseOffset - 1),
+      );
+      return getOffsetByTextSelection(leftTextSelection);
+    }
+    return Offset.zero;
+  }
+
+  @override
+  Offset getRightOfOffset() {
+    final textSelection = _textSelection;
+    if (textSelection != null) {
+      final leftTextSelection = TextSelection.collapsed(
+        offset: min(node.toRawString().length, textSelection.extentOffset + 1),
+      );
+      return getOffsetByTextSelection(leftTextSelection);
+    }
+    return Offset.zero;
+  }
+
   @override
   Widget build(BuildContext context) {
     Widget richText;
@@ -117,6 +143,10 @@ class _SelectedTextNodeWidgetState extends State<_SelectedTextNodeWidget>
       richText = RichText(key: _textKey, text: node.toTextSpan());
     }
 
+    if (node.children.isEmpty) {
+      return richText;
+    }
+
     return Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [

+ 5 - 3
frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart

@@ -1,4 +1,5 @@
 import 'dart:async';
+import 'package:flowy_editor/service/service.dart';
 import 'package:flutter/material.dart';
 
 import 'package:flowy_editor/document/node.dart';
@@ -19,13 +20,14 @@ class ApplyOptions {
   });
 }
 
-// TODO
-final selectionServiceKey = GlobalKey();
-
 class EditorState {
   final StateTree document;
   final RenderPlugins renderPlugins;
   List<Node> selectedNodes = [];
+
+  // Service reference.
+  final service = FlowyService();
+
   final UndoManager undoManager = UndoManager();
   Selection? cursorSelection;
 

+ 6 - 0
frontend/app_flowy/packages/flowy_editor/lib/render/selection/selectable.dart

@@ -11,6 +11,12 @@ mixin Selectable<T extends StatefulWidget> on State<T> {
   /// The return result must be an local offset.
   Rect getCursorRect(Offset start);
 
+  /// Returns one unit offset to the left of the offset
+  Offset getLeftOfOffset(/* Cause */);
+
+  /// Returns one unit offset to the right of the offset
+  Offset getRightOfOffset(/* Cause */);
+
   /// For [TextNode] only.
   TextSelection? getTextSelection();
 

+ 4 - 1
frontend/app_flowy/packages/flowy_editor/lib/service/editor_service.dart

@@ -1,3 +1,4 @@
+import 'package:flowy_editor/service/flowy_key_event_handlers/arrow_keys_handler.dart';
 import 'package:flowy_editor/service/flowy_key_event_handlers/delete_nodes_handler.dart';
 import 'package:flowy_editor/service/flowy_key_event_handlers/delete_single_text_node_handler.dart';
 import 'package:flowy_editor/service/keyboard_service.dart';
@@ -26,12 +27,14 @@ class _FlowyEditorState extends State<FlowyEditor> {
   @override
   Widget build(BuildContext context) {
     return FlowySelection(
-      key: selectionServiceKey,
+      key: editorState.service.selectionServiceKey,
       editorState: editorState,
       child: FlowyKeyboard(
+        key: editorState.service.keyboardServiceKey,
         handlers: [
           flowyDeleteNodesHandler,
           deleteSingleTextNodeHandler,
+          arrowKeysHandler,
           ...widget.keyEventHandler,
         ],
         editorState: editorState,

+ 37 - 0
frontend/app_flowy/packages/flowy_editor/lib/service/flowy_key_event_handlers/arrow_keys_handler.dart

@@ -0,0 +1,37 @@
+import 'package:flowy_editor/extensions/object_extensions.dart';
+import 'package:flowy_editor/flowy_editor.dart';
+import 'package:flowy_editor/service/keyboard_service.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+FlowyKeyEventHandler arrowKeysHandler = (editorState, event) {
+  if (event.logicalKey != LogicalKeyboardKey.arrowUp &&
+      event.logicalKey != LogicalKeyboardKey.arrowDown &&
+      event.logicalKey != LogicalKeyboardKey.arrowLeft &&
+      event.logicalKey != LogicalKeyboardKey.arrowRight) {
+    return KeyEventResult.ignored;
+  }
+
+  // TODO: Up and Down
+
+  // Left and Right
+  final selectedNodes = editorState.selectedNodes;
+  if (selectedNodes.length != 1) {
+    return KeyEventResult.ignored;
+  }
+
+  final node = selectedNodes.first.unwrapOrNull<TextNode>();
+  final selectable = node?.key?.currentState?.unwrapOrNull<Selectable>();
+  Offset? offset;
+  if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
+    offset = selectable?.getLeftOfOffset();
+  } else if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
+    offset = selectable?.getRightOfOffset();
+  }
+  final selectionService = editorState.service.selectionService;
+  if (offset != null) {
+    selectionService.updateCursor(offset);
+    return KeyEventResult.handled;
+  }
+  return KeyEventResult.ignored;
+};

+ 2 - 4
frontend/app_flowy/packages/flowy_editor/lib/service/flowy_key_event_handlers/delete_single_text_node_handler.dart

@@ -33,8 +33,7 @@ FlowyKeyEventHandler deleteSingleTextNodeHandler = (editorState, event) {
               final previous = node!.previous! as TextNode;
               final newTextSelection = TextSelection.collapsed(
                   offset: previous.toRawString().length);
-              final selectionService =
-                  selectionServiceKey.currentState as FlowySelectionService;
+              final selectionService = editorState.service.selectionService;
               final previousSelectable =
                   previous.key?.currentState?.unwrapOrNull<Selectable>();
               final newOfset = previousSelectable
@@ -58,8 +57,7 @@ FlowyKeyEventHandler deleteSingleTextNodeHandler = (editorState, event) {
               ..commit();
             final newTextSelection =
                 TextSelection.collapsed(offset: textSelection.baseOffset - 1);
-            final selectionService =
-                selectionServiceKey.currentState as FlowySelectionService;
+            final selectionService = editorState.service.selectionService;
             final newOfset =
                 selectable.getOffsetByTextSelection(newTextSelection);
             selectionService.updateCursor(newOfset);

+ 1 - 1
frontend/app_flowy/packages/flowy_editor/lib/service/keyboard_service.dart

@@ -46,7 +46,7 @@ class _FlowyKeyboardState extends State<FlowyKeyboard> {
     }
 
     for (final handler in widget.handlers) {
-      debugPrint('handle keyboard event $event by $handler');
+      // debugPrint('handle keyboard event $event by $handler');
 
       KeyEventResult result = handler(widget.editorState, event);
 

+ 15 - 0
frontend/app_flowy/packages/flowy_editor/lib/service/service.dart

@@ -0,0 +1,15 @@
+import 'package:flowy_editor/service/selection_service.dart';
+import 'package:flutter/material.dart';
+
+class FlowyService {
+  // selection service
+  final selectionServiceKey = GlobalKey(debugLabel: 'flowy_selection_service');
+  FlowySelectionService get selectionService {
+    assert(selectionServiceKey.currentState != null &&
+        selectionServiceKey.currentState is FlowySelectionService);
+    return selectionServiceKey.currentState! as FlowySelectionService;
+  }
+
+  // keyboard service
+  final keyboardServiceKey = GlobalKey(debugLabel: 'flowy_keyboard_service');
+}