resolveSelection function
ResolvedSelection?
resolveSelection(
- Root root,
- String anchorFragmentId,
- int anchorOffset,
- String focusFragmentId,
- int focusOffset, {
- List<
CaretStop> ? cachedStops, - List<
LogicalLine> ? cachedLines, - FluentDocument? document,
Resolves the selection defined by (anchorFragmentId, anchorOffset) →
(focusFragmentId, focusOffset) in the root tree.
Returns null if:
- anchor == focus (collapsed selection)
- one of the fragments is not found in the tree
- positions are not on the stop rail
Usage example:
final sel = resolveSelection(
document.content,
cursor.anchorId, cursor.anchorOffset,
cursor.focusId, cursor.focusOffset,
);
if (sel != null) {
for (final node in sel.nodes) {
print(node);
}
}
Implementation
ResolvedSelection? resolveSelection(
Root root,
String anchorFragmentId,
int anchorOffset,
String focusFragmentId,
int focusOffset, {
List<CaretStop>? cachedStops,
List<LogicalLine>? cachedLines,
FluentDocument? document,
}) {
if (anchorFragmentId == focusFragmentId && anchorOffset == focusOffset) {
return null;
}
final stops = cachedStops ?? buildAllStops(root);
final lines = cachedLines ?? buildAllLogicalLines(root);
final anchorIdx = findStopIndex(stops, anchorFragmentId, anchorOffset);
final focusIdx = findStopIndex(stops, focusFragmentId, focusOffset);
if (anchorIdx < 0 || focusIdx < 0) return null;
final baseIsAnchor = anchorIdx <= focusIdx;
final baseIdx = baseIsAnchor ? anchorIdx : focusIdx;
final extentIdx = baseIsAnchor ? focusIdx : anchorIdx;
final anchorFragResolved = document?.nodeById(anchorFragmentId) ?? findById(root, anchorFragmentId);
final focusFragResolved = document?.nodeById(focusFragmentId) ?? findById(root, focusFragmentId);
if (anchorFragResolved is! Fragment || focusFragResolved is! Fragment) return null;
final anchorContainer = document?.findLogicalContainerCached(anchorFragmentId) ??
findLogicalContainer(root, anchorFragmentId);
final focusContainer = document?.findLogicalContainerCached(focusFragmentId) ??
findLogicalContainer(root, focusFragmentId);
if (anchorContainer == null || focusContainer == null) return null;
final anchorEndpoint = SelectionEndpoint(
fragment: anchorFragResolved,
offset: anchorOffset,
container: anchorContainer,
);
final focusEndpoint = SelectionEndpoint(
fragment: focusFragResolved,
offset: focusOffset,
container: focusContainer,
);
final baseEndpoint = baseIsAnchor ? anchorEndpoint : focusEndpoint;
final extentEndpoint = baseIsAnchor ? focusEndpoint : anchorEndpoint;
final selectedNodes = <SelectedNode>[];
for (final line in lines) {
if (line.stops.isEmpty) continue;
final firstStop = line.stops.first;
final lastStop = line.stops.last;
final firstIdx = findStopIndex(stops, firstStop.fragmentId, firstStop.offset);
final lastIdx = findStopIndex(stops, lastStop.fragmentId, lastStop.offset);
if (firstIdx < 0 || lastIdx < 0) continue;
final lineMinIdx = firstIdx <= lastIdx ? firstIdx : lastIdx;
final lineMaxIdx = firstIdx >= lastIdx ? firstIdx : lastIdx;
if (lineMaxIdx < baseIdx || lineMinIdx > extentIdx) continue;
final Fragment startFrag;
final int startOff;
final lineContainerId = (line.node as FNode).id;
final isBaseLine = lineContainerId == (baseEndpoint.container as FNode).id;
final isExtentLine = lineContainerId == (extentEndpoint.container as FNode).id;
if (isBaseLine) {
startFrag = baseEndpoint.fragment;
startOff = baseEndpoint.offset;
} else {
final firstStop = line.stops.first;
final firstNode = document?.nodeById(firstStop.fragmentId) ?? findById(root, firstStop.fragmentId);
if (firstNode is! Fragment) continue;
final frag = firstNode;
startFrag = frag;
startOff = 0;
}
final Fragment endFrag;
final int endOff;
if (isExtentLine) {
endFrag = extentEndpoint.fragment;
endOff = extentEndpoint.offset;
} else {
final lastStop = line.stops.last;
final lastNode = document?.nodeById(lastStop.fragmentId) ?? findById(root, lastStop.fragmentId);
if (lastNode is! Fragment) continue;
final frag = lastNode;
endFrag = frag;
endOff = frag.text.length;
}
final isFullySelected = !isBaseLine && !isExtentLine;
selectedNodes.add(SelectedNode(
container: line.node,
startFragment: startFrag,
startOffset: startOff,
endFragment: endFrag,
endOffset: endOff,
isFullySelected: isFullySelected,
));
}
if (selectedNodes.isEmpty) return null;
return ResolvedSelection(
anchor: anchorEndpoint,
focus: focusEndpoint,
base: baseEndpoint,
extent: extentEndpoint,
nodes: selectedNodes,
);
}