diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart index 2775bdfee1..55a9230272 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart @@ -48,34 +48,22 @@ mixin FlowySelectionService on State { /// ------------------ Offset ------------------------ - /// Returns selected [Node]s. Empty list would be returned - /// if no nodes are being selected. - /// - /// - /// [start] and [end] are the offsets under the global coordinate system. - /// - /// If end is not null, it means multiple selection, - /// otherwise single selection. - List getNodesInRange(Offset start, [Offset? end]); - /// Return the [Node] or [Null] in single selection. /// - /// [start] is the offset under the global coordinate system. - Node? computeNodeInOffset(Node node, Offset offset); + /// [offset] is under the global coordinate system. + Node? getNodeInOffset(Offset offset); - /// Return the [Node]s in multiple selection. Empty list would be returned + /// Returns selected [Node]s. Empty list would be returned /// if no nodes are in range. /// - /// [start] is the offset under the global coordinate system. - List computeNodesInRange( - Node node, - Offset start, - Offset end, - ); + /// + /// [start] and [end] are under the global coordinate system. + /// + List getNodeInRange(Offset start, Offset end); /// Return [bool] to identify the [Node] is in Range or not. /// - /// [start] and [end] are the offsets under the global coordinate system. + /// [start] and [end] are under the global coordinate system. bool isNodeInRange( Node node, Offset start, @@ -84,7 +72,7 @@ mixin FlowySelectionService on State { /// Return [bool] to identify the [Node] contains [Offset] or not. /// - /// [start] is the offset under the global coordinate system. + /// [offset] is under the global coordinate system. bool isNodeInOffset(Node node, Offset offset); /// ------------------ Offset ------------------------ @@ -214,57 +202,15 @@ class _FlowySelectionState extends State } @override - List getNodesInRange(Offset start, [Offset? end]) { - if (end != null) { - return computeNodesInRange(editorState.document.root, start, end); - } else { - final result = computeNodeInOffset(editorState.document.root, start); - if (result != null) { - return [result]; - } - } - return []; + Node? getNodeInOffset(Offset offset) { + return _lowerBoundInDocument(offset); } @override - Node? computeNodeInOffset(Node node, Offset offset) { - for (final child in node.children) { - final result = computeNodeInOffset(child, offset); - if (result != null) { - return result; - } - } - if (node.parent != null && node.key != null) { - if (isNodeInOffset(node, offset)) { - return node; - } - } - return null; - } - - @override - List computeNodesInRange(Node node, Offset start, Offset end) { - final result = _computeNodesInRange(node, start, end); - if (start.dy <= end.dy) { - // downward - return result; - } else { - // upward - return result.reversed.toList(growable: false); - } - } - - List _computeNodesInRange(Node node, Offset start, Offset end) { - List result = []; - if (node.parent != null && node.key != null) { - if (isNodeInRange(node, start, end)) { - result.add(node); - } - } - for (final child in node.children) { - result.addAll(_computeNodesInRange(child, start, end)); - } - return result; + List getNodeInRange(Offset start, Offset end) { + final startNode = _lowerBoundInDocument(start); + final endNode = _upperBoundInDocument(end); + return NodeIterator(editorState.document, startNode, endNode).toList(); } @override @@ -292,12 +238,12 @@ class _FlowySelectionState extends State void _onDoubleTapDown(TapDownDetails details) { final offset = details.globalPosition; - final nodes = getNodesInRange(offset); - if (nodes.isEmpty) { + final node = getNodeInOffset(offset); + if (node == null) { editorState.updateCursorSelection(null); return; } - final selectable = nodes.first.selectable; + final selectable = node.selectable; if (selectable == null) { editorState.updateCursorSelection(null); return; @@ -327,13 +273,12 @@ class _FlowySelectionState extends State editorState.updateCursorSelection(null); return null; } - final nodes = getNodesInRange(offset); - if (nodes.isEmpty) { + final node = getNodeInOffset(offset); + if (node == null) { editorState.updateCursorSelection(null); return null; } - assert(nodes.length == 1); - final selectable = nodes.first.selectable; + final selectable = node.selectable; if (selectable == null) { editorState.updateCursorSelection(null); return null; @@ -568,7 +513,21 @@ class _FlowySelectionState extends State } } - // find the first node's rect.top <= offset.dy + Node _lowerBoundInDocument(Offset offset) { + final sortedNodes = + editorState.document.root.children.toList(growable: false); + return _lowerBound(sortedNodes, offset, 0, sortedNodes.length); + } + + Node _upperBoundInDocument(Offset offset) { + final sortedNodes = + editorState.document.root.children.toList(growable: false); + return _upperBound(sortedNodes, offset, 0, sortedNodes.length); + } + + /// TODO: Supports multi-level nesting, + /// currently only single-level nesting is supported + // find the first node's rect.bottom <= offset.dy Node _lowerBound(List sortedNodes, Offset offset, int start, int end) { var min = start; var max = end; @@ -583,6 +542,8 @@ class _FlowySelectionState extends State return sortedNodes[min]; } + /// TODO: Supports multi-level nesting, + /// currently only single-level nesting is supported // find the first node's rect.top < offset.dy Node _upperBound( List sortedNodes,