collectInlineTapRuns function
Every TappableTextSpan in root, as plain-text ranges.
Measured after the subtree, so a span built over TextSpan.children covers everything inside it rather than its own (absent) text — the reason a wrapper is a working tap target here and a dead one with a recognizer. Offsets count a placeholder as the single U+FFFC code unit the engine lays out, so they line up with RenderParagraph.getBoxesForSelection.
Identical in shape to collectInlineCodeRuns.
Implementation
List<InlineTapRun> collectInlineTapRuns(InlineSpan root) {
final runs = <InlineTapRun>[];
var offset = 0;
void visit(InlineSpan span) {
if (span is TextSpan) {
final start = offset;
offset += span.text?.length ?? 0;
final children = span.children;
if (children != null) {
for (final child in children) {
visit(child);
}
}
if (span is TappableTextSpan && offset > start) {
runs.add(
InlineTapRun(
start: start,
end: offset,
onTap: span.onTap,
hoverStyle: span.hoverStyle,
),
);
}
} else if (span is PlaceholderSpan) {
offset += 1;
} else {
offset += span.toPlainText(includePlaceholders: true).length;
}
}
visit(root);
return runs;
}