render method
Diffs backBuffer against _frontBuffer and writes minimal ANSI escape sequences to out.
Implementation
void render(Buffer backBuffer, StringSink out) {
Tracer.record(_traceRenderId, Phase.begin);
try {
bool sizeChanged = _firstFrame;
_firstFrame = false;
if (mode == RenderingMode.inline) {
if (_lastHeight > 0) {
// Move cursor back up to the top-left of the inline block
out.write('\x1b[${_lastHeight}F');
} else {
// First frame: move to the next line to avoid printing TUI inline on the command prompt line
out.write('\n\r');
}
if (backBuffer.width != _frontBuffer.width ||
backBuffer.height != _frontBuffer.height) {
_frontBuffer.resize(backBuffer.width, backBuffer.height);
sizeChanged = true;
}
} else {
if (backBuffer.width != _frontBuffer.width ||
backBuffer.height != _frontBuffer.height ||
sizeChanged) {
_frontBuffer.resize(backBuffer.width, backBuffer.height);
sizeChanged = true;
// Clear screen and reset cursor position to top-left
out.write('\x1b[2J\x1b[H');
}
}
Style activeStyle = Style.empty;
int cursorX = 0;
int cursorY = 0;
final width = backBuffer.width;
final height = backBuffer.height;
final backCells = backBuffer.cells;
final frontCells = _frontBuffer.cells;
for (var y = 0; y < height; y++) {
var x = 0;
final rowOffset = y * width;
while (x < width) {
final idx = rowOffset + x;
final backCell = backCells[idx];
final frontCell = frontCells[idx];
final changed =
sizeChanged ||
(backCell.char != frontCell.char ||
!_styleEquals(backCell.style, frontCell.style));
if (changed) {
final runStart = x;
var runEnd = x;
// Find the end of the contiguous run of changed cells in the current row
while (runEnd < width) {
final cell = backCells[rowOffset + runEnd];
final fCell = frontCells[rowOffset + runEnd];
if (sizeChanged ||
cell.char != fCell.char ||
!_styleEquals(cell.style, fCell.style)) {
runEnd++;
} else {
break;
}
}
// Move cursor to (runStart, y) relative or absolute
if (mode == RenderingMode.inline) {
_moveCursorRelative(out, cursorX, cursorY, runStart, y);
cursorX = runStart;
cursorY = y;
} else {
out.write('\x1b[${y + 1};${runStart + 1}H');
}
if (activeStyle != Style.empty) {
out.write('\x1b[0m');
activeStyle = Style.empty;
}
// Render each cell in the run
for (var rx = runStart; rx < runEnd; rx++) {
final cell = backCells[rowOffset + rx];
activeStyle = _writeStyleTransition(out, activeStyle, cell.style);
out.write(cell.char);
final fCell = frontCells[rowOffset + rx];
fCell.char = cell.char;
fCell.style = cell.style;
if (mode == RenderingMode.inline) {
cursorX++;
}
}
x = runEnd;
} else {
x++;
}
}
}
if (activeStyle != Style.empty) {
out.write('\x1b[0m');
}
if (mode == RenderingMode.inline) {
// Position cursor at the beginning of the line immediately following the inline block
_moveCursorRelative(out, cursorX, cursorY, 0, backBuffer.height);
_lastHeight = backBuffer.height;
}
} finally {
Tracer.record(_traceRenderId, Phase.end);
}
}