renderToString method
Renders the current tree and stringifies the buffer with one
row per line. Leading cells write their grapheme, continuation
cells contribute nothing (their leading cell handles them), and
empty cells are rendered with emptyMark (default · for
visibility against runs of whitespace).
Overlay (inline-image) cells are stamped one per column by an image
placement (unlike a continuation, whose leading cell already contributed
the pair's width), so each occupies a real column. The presenter draws
them as pixels — never as text — but the snapshot must still hold their
columns or every later column shifts left, encoding wrong geometry in
goldens and hiding text that overlaps the image. They render as
imageMark (default #) so the footprint is visible and text painted
over the region breaks the mark's run. # is a real glyph, so a snapshot
that also renders a widget which paints literal # (a ProgressBar, say)
can't tell the two apart — override imageMark with a glyph that
subject doesn't emit for those cases.
Trailing empty cells on a row are trimmed for readability; image marks are content and are never trimmed.
Implementation
String renderToString({
CellSize? size,
String emptyMark = '·',
String imageMark = '#',
}) {
final buffer = render(size: size);
final out = StringBuffer();
for (var row = 0; row < buffer.size.rows; row++) {
// Trim by cell, not by text: a glyph that happens to equal the mark
// is content, and a mark of any length (even empty) trims the same.
var end = buffer.size.cols;
while (end > 0 && buffer.atColRow(end - 1, row).role == CellRole.empty) {
end--;
}
final line = StringBuffer();
for (var col = 0; col < end; col++) {
final cell = buffer.atColRow(col, row);
switch (cell.role) {
case CellRole.empty:
line.write(emptyMark);
case CellRole.leading:
line.write(cell.grapheme);
case CellRole.overlay:
// An image-owned column: mark it so geometry is preserved and the
// footprint stays visible (see doc above).
line.write(imageMark);
case CellRole.continuation:
// The second column of a wide grapheme; its leading cell already
// wrote the whole glyph, so this contributes nothing.
break;
}
}
out.writeln(line.toString());
}
return out.toString();
}