瀏覽代碼

docs: documentation for selection_service

Lucas.Xu 2 年之前
父節點
當前提交
ae0012ba37

+ 7 - 6
frontend/app_flowy/packages/flowy_editor/lib/flowy_editor.dart

@@ -1,15 +1,16 @@
 library flowy_editor;
 
-export 'src/document/state_tree.dart';
 export 'src/document/node.dart';
 export 'src/document/path.dart';
+export 'src/document/position.dart';
+export 'src/document/selection.dart';
+export 'src/document/state_tree.dart';
 export 'src/document/text_delta.dart';
-export 'src/render/selection/selectable.dart';
+export 'src/editor_state.dart';
+export 'src/operation/operation.dart';
 export 'src/operation/transaction.dart';
 export 'src/operation/transaction_builder.dart';
-export 'src/operation/operation.dart';
-export 'src/editor_state.dart';
+export 'src/render/selection/selectable.dart';
 export 'src/service/editor_service.dart';
-export 'src/document/selection.dart';
-export 'src/document/position.dart';
 export 'src/service/render_plugin_service.dart';
+export 'src/service/service.dart';

+ 30 - 15
frontend/app_flowy/packages/flowy_editor/lib/src/document/selection.dart

@@ -2,15 +2,26 @@ import 'package:flowy_editor/src/document/path.dart';
 import 'package:flowy_editor/src/document/position.dart';
 import 'package:flowy_editor/src/extensions/path_extensions.dart';
 
+/// Selection represents the selected area or the cursor area in the editor.
+///
+/// [Selection] is directional.
+///
+/// 1. forward,the end position is before the start position.
+/// 2. backward, the end position is after the start position.
+/// 3. collapsed, the end position is equal to the start position.
 class Selection {
-  final Position start;
-  final Position end;
-
+  /// Create a selection with [start], [end].
   Selection({
     required this.start,
     required this.end,
   });
 
+  /// Create a selection with [Path], [startOffset] and [endOffset].
+  ///
+  /// The [endOffset] is optional.
+  ///
+  /// This constructor will return a collapsed [Selection] if [endOffset] is null.
+  ///
   Selection.single({
     required Path path,
     required int startOffset,
@@ -18,10 +29,21 @@ class Selection {
   })  : start = Position(path: path, offset: startOffset),
         end = Position(path: path, offset: endOffset ?? startOffset);
 
+  /// Create a collapsed selection with [position].
   Selection.collapsed(Position position)
       : start = position,
         end = position;
 
+  final Position start;
+  final Position end;
+
+  bool get isCollapsed => start == end;
+  bool get isSingle => pathEquals(start.path, end.path);
+  bool get isForward =>
+      start.path >= end.path && !pathEquals(start.path, end.path);
+  bool get isBackward =>
+      start.path <= end.path && !pathEquals(start.path, end.path);
+
   Selection collapse({bool atStart = false}) {
     if (atStart) {
       return Selection(start: start, end: start);
@@ -30,13 +52,6 @@ class Selection {
     }
   }
 
-  bool get isCollapsed => start == end;
-  bool get isSingle => pathEquals(start.path, end.path);
-  bool get isUpward =>
-      start.path >= end.path && !pathEquals(start.path, end.path);
-  bool get isDownward =>
-      start.path <= end.path && !pathEquals(start.path, end.path);
-
   Selection copyWith({Position? start, Position? end}) {
     return Selection(
       start: start ?? this.start,
@@ -46,13 +61,10 @@ class Selection {
 
   Selection copy() => Selection(start: start, end: end);
 
-  @override
-  String toString() => '[Selection] start = $start, end = $end';
-
   Map<String, dynamic> toJson() {
     return {
-      "start": start.toJson(),
-      "end": end.toJson(),
+      'start': start.toJson(),
+      'end': end.toJson(),
     };
   }
 
@@ -69,4 +81,7 @@ class Selection {
 
   @override
   int get hashCode => Object.hash(start, end);
+
+  @override
+  String toString() => '[Selection] start = $start, end = $end';
 }

+ 2 - 22
frontend/app_flowy/packages/flowy_editor/lib/src/service/editor_service.dart

@@ -1,3 +1,4 @@
+import 'package:flowy_editor/src/service/internal_key_event_handlers/default_key_event_handlers.dart';
 import 'package:flutter/material.dart';
 
 import 'package:flowy_editor/src/editor_state.dart';
@@ -9,15 +10,6 @@ import 'package:flowy_editor/src/render/rich_text/number_list_text.dart';
 import 'package:flowy_editor/src/render/rich_text/quoted_text.dart';
 import 'package:flowy_editor/src/render/rich_text/rich_text.dart';
 import 'package:flowy_editor/src/service/input_service.dart';
-import 'package:flowy_editor/src/service/internal_key_event_handlers/arrow_keys_handler.dart';
-import 'package:flowy_editor/src/service/internal_key_event_handlers/copy_paste_handler.dart';
-import 'package:flowy_editor/src/service/internal_key_event_handlers/delete_nodes_handler.dart';
-import 'package:flowy_editor/src/service/internal_key_event_handlers/delete_text_handler.dart';
-import 'package:flowy_editor/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart';
-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/redo_undo_handler.dart';
 import 'package:flowy_editor/src/service/keyboard_service.dart';
 import 'package:flowy_editor/src/service/render_plugin_service.dart';
 import 'package:flowy_editor/src/service/scroll_service.dart';
@@ -34,18 +26,6 @@ NodeWidgetBuilders defaultBuilders = {
   'text/quote': QuotedTextNodeWidgetBuilder(),
 };
 
-List<FlowyKeyEventHandler> defaultKeyEventHandler = [
-  deleteTextHandler,
-  slashShortcutHandler,
-  flowyDeleteNodesHandler,
-  arrowKeysHandler,
-  copyPasteKeysHandler,
-  redoUndoKeysHandler,
-  enterWithoutShiftInTextNodesHandler,
-  updateTextStyleByCommandXHandler,
-  whiteSpaceHandler,
-];
-
 class FlowyEditor extends StatefulWidget {
   const FlowyEditor({
     Key? key,
@@ -98,7 +78,7 @@ class _FlowyEditorState extends State<FlowyEditor> {
             child: FlowyKeyboard(
               key: editorState.service.keyboardServiceKey,
               handlers: [
-                ...defaultKeyEventHandler,
+                ...defaultKeyEventHandlers,
                 ...widget.keyEventHandlers,
               ],
               editorState: editorState,

+ 22 - 0
frontend/app_flowy/packages/flowy_editor/lib/src/service/internal_key_event_handlers/default_key_event_handlers.dart

@@ -0,0 +1,22 @@
+import 'package:flowy_editor/src/service/internal_key_event_handlers/arrow_keys_handler.dart';
+import 'package:flowy_editor/src/service/internal_key_event_handlers/copy_paste_handler.dart';
+import 'package:flowy_editor/src/service/internal_key_event_handlers/delete_nodes_handler.dart';
+import 'package:flowy_editor/src/service/internal_key_event_handlers/delete_text_handler.dart';
+import 'package:flowy_editor/src/service/internal_key_event_handlers/enter_without_shift_in_text_node_handler.dart';
+import 'package:flowy_editor/src/service/internal_key_event_handlers/redo_undo_handler.dart';
+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/keyboard_service.dart';
+
+List<FlowyKeyEventHandler> defaultKeyEventHandlers = [
+  deleteTextHandler,
+  slashShortcutHandler,
+  flowyDeleteNodesHandler,
+  arrowKeysHandler,
+  copyPasteKeysHandler,
+  redoUndoKeysHandler,
+  enterWithoutShiftInTextNodesHandler,
+  updateTextStyleByCommandXHandler,
+  whiteSpaceHandler,
+];

+ 41 - 78
frontend/app_flowy/packages/flowy_editor/lib/src/service/selection_service.dart

@@ -15,67 +15,60 @@ import 'package:flowy_editor/src/render/selection/cursor_widget.dart';
 import 'package:flowy_editor/src/render/selection/selectable.dart';
 import 'package:flowy_editor/src/render/selection/selection_widget.dart';
 
-/// Process selection and cursor
+/// [FlowySelectionService] is responsible for processing
+/// the [Selection] changes and updates.
+///
+/// Usually, this service can be obtained by the following code.
+/// ```dart
+/// final selectionService = editorState.service.selectionService;
+///
+/// /** get current selection value*/
+/// final selection = selectionService.currentSelection.value;
+///
+/// /** get current selected nodes*/
+/// final nodes = selectionService.currentSelectedNodes;
+/// ```
+///
 mixin FlowySelectionService<T extends StatefulWidget> on State<T> {
-  /// Returns the current [Selection]
+  /// The current [Selection] in editor.
+  ///
+  /// The value is null if there is no nodes are selected.
   ValueNotifier<Selection?> get currentSelection;
 
-  /// Returns the current selected [Node]s.
+  /// The current selected [Node]s in editor.
+  ///
+  /// The order of the result is determined according to the [currentSelection].
+  /// The result are ordered from back to front if the selection is forward.
+  /// The result are ordered from front to back if the selection is backward.
   ///
-  /// The order of the return is determined according to the selected order.
+  /// For example, Here is an array of selected nodes, [n1, n2, n3].
+  /// The result will be [n3, n2, n1] if the selection is forward,
+  ///   and [n1, n2, n3] if the selection is backward.
+  ///
+  /// Returns empty result if there is no nodes are selected.
   List<Node> get currentSelectedNodes;
 
-  /// Update the selection or cursor.
+  /// Updates the selection.
   ///
-  /// If selection is collapsed, this method will
-  ///   update the position of the cursor.
-  /// Otherwise, will update the selection.
+  /// The editor will update selection area and popup list area
+  /// if the [selection] is not collapsed,
+  /// otherwise, will update the cursor area.
   void updateSelection(Selection selection);
 
-  /// Clear the selection or cursor.
+  /// Clears the selection area, cursor area and the popup list area.
   void clearSelection();
 
-  /// ------------------ Selection ------------------------
-
-  List<Rect> rects();
-
-  Position? hitTest(Offset? offset);
-
-  ///
+  /// Returns the [Node]s in [Selection].
   List<Node> getNodesInSelection(Selection selection);
 
-  /// ------------------ Selection ------------------------
-
-  /// ------------------ Offset ------------------------
-
-  /// Return the [Node] or [Null] in single selection.
+  /// Returns the [Node] containing to the offset.
   ///
-  /// [offset] is under the global coordinate system.
+  /// [offset] must be under the global coordinate system.
   Node? getNodeInOffset(Offset offset);
 
-  /// Returns selected [Node]s. Empty list would be returned
-  ///   if no nodes are in range.
-  ///
-  ///
-  /// [start] and [end] are under the global coordinate system.
-  ///
-  List<Node> getNodeInRange(Offset start, Offset end);
-
-  /// Return [bool] to identify the [Node] is in Range or not.
-  ///
-  /// [start] and [end] are under the global coordinate system.
-  bool isNodeInRange(
-    Node node,
-    Offset start,
-    Offset end,
-  );
-
-  /// Return [bool] to identify the [Node] contains [Offset] or not.
-  ///
-  /// [offset] is under the global coordinate system.
-  bool isNodeInOffset(Node node, Offset offset);
-
-  /// ------------------ Offset ------------------------
+  // TODO: need to be documented.
+  List<Rect> rects();
+  Position? hitTest(Offset? offset);
 }
 
 class FlowySelection extends StatefulWidget {
@@ -207,36 +200,6 @@ class _FlowySelectionState extends State<FlowySelection>
     return _lowerBoundInDocument(offset);
   }
 
-  @override
-  List<Node> getNodeInRange(Offset start, Offset end) {
-    final startNode = _lowerBoundInDocument(start);
-    final endNode = _upperBoundInDocument(end);
-    return NodeIterator(editorState.document, startNode, endNode).toList();
-  }
-
-  @override
-  bool isNodeInOffset(Node node, Offset offset) {
-    final renderBox = node.renderBox;
-    if (renderBox != null) {
-      final boxOffset = renderBox.localToGlobal(Offset.zero);
-      final boxRect = boxOffset & renderBox.size;
-      return boxRect.contains(offset);
-    }
-    return false;
-  }
-
-  @override
-  bool isNodeInRange(Node node, Offset start, Offset end) {
-    final renderBox = node.renderBox;
-    if (renderBox != null) {
-      final rect = Rect.fromPoints(start, end);
-      final boxOffset = renderBox.localToGlobal(Offset.zero);
-      final boxRect = boxOffset & renderBox.size;
-      return rect.overlaps(boxRect);
-    }
-    return false;
-  }
-
   void _onDoubleTapDown(TapDownDetails details) {
     final offset = details.globalPosition;
     final node = getNodeInOffset(offset);
@@ -395,13 +358,13 @@ class _FlowySelectionState extends State<FlowySelection>
         // text: ghijkl
         // text: mn>opqr
         if (index == 0) {
-          if (selection.isDownward) {
+          if (selection.isBackward) {
             newSelection = selection.copyWith(end: selectable.end());
           } else {
             newSelection = selection.copyWith(start: selectable.start());
           }
         } else if (index == nodes.length - 1) {
-          if (selection.isDownward) {
+          if (selection.isBackward) {
             newSelection = selection.copyWith(start: selectable.start());
           } else {
             newSelection = selection.copyWith(end: selectable.end());
@@ -498,7 +461,7 @@ class _FlowySelectionState extends State<FlowySelection>
 
     /// TODO: It is necessary to calculate the relative speed
     ///   according to the gap and move forward more gently.
-    final distance = 10.0;
+    const distance = 10.0;
     if (offset.dy <= topLimit && !isDownward) {
       // up
       editorState.service.scrollService?.scrollTo(dy - distance);

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

@@ -1,10 +1,9 @@
-import 'package:flutter/material.dart';
-
 import 'package:flowy_editor/src/service/keyboard_service.dart';
 import 'package:flowy_editor/src/service/render_plugin_service.dart';
 import 'package:flowy_editor/src/service/scroll_service.dart';
 import 'package:flowy_editor/src/service/selection_service.dart';
 import 'package:flowy_editor/src/service/toolbar_service.dart';
+import 'package:flutter/material.dart';
 
 class FlowyService {
   // selection service