mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: optimize the calculation of cursor position and selection position
This commit is contained in:
parent
7e6c7a2b4e
commit
fb267ace18
@ -48,34 +48,22 @@ mixin FlowySelectionService<T extends StatefulWidget> on State<T> {
|
||||
|
||||
/// ------------------ 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<Node> 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<Node> computeNodesInRange(
|
||||
Node node,
|
||||
Offset start,
|
||||
Offset end,
|
||||
);
|
||||
///
|
||||
/// [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 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<T extends StatefulWidget> on State<T> {
|
||||
|
||||
/// 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<FlowySelection>
|
||||
}
|
||||
|
||||
@override
|
||||
List<Node> 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<Node> 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<Node> _computeNodesInRange(Node node, Offset start, Offset end) {
|
||||
List<Node> 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<Node> 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<FlowySelection>
|
||||
|
||||
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<FlowySelection>
|
||||
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<FlowySelection>
|
||||
}
|
||||
}
|
||||
|
||||
// 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<Node> sortedNodes, Offset offset, int start, int end) {
|
||||
var min = start;
|
||||
var max = end;
|
||||
@ -583,6 +542,8 @@ class _FlowySelectionState extends State<FlowySelection>
|
||||
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<Node> sortedNodes,
|
||||
|
Loading…
Reference in New Issue
Block a user