writeString method
Writes styled text to the buffer starting at (x, y).
Newlines (\n) will wrap to y + 1 at the starting column x.
Any characters that fall out of bounds are clipped.
Implementation
void writeString(int x, int y, String text, Style style) {
final startX = x;
var currentX = x;
var currentY = y;
// Fast-path for pure ASCII text: no wide characters, no variation selectors,
// and no multi-code-unit grapheme clusters.
var isAscii = true;
final len = text.length;
for (var i = 0; i < len; i++) {
if (text.codeUnitAt(i) >= 128) {
isAscii = false;
break;
}
}
if (isAscii) {
final fg = style.foreground?.argb ?? 0;
final bg = style.background?.argb ?? 0;
final modifiers = style.modifiers;
final hasFg = style.foreground != null;
final hasBg = style.background != null;
for (var i = 0; i < len; i++) {
final codeUnit = text.codeUnitAt(i);
if (codeUnit == 10) {
// '\n'
currentX = startX;
currentY++;
continue;
}
if (isCellValid(currentX, currentY)) {
final idx = currentY * width + currentX;
final char = String.fromCharCode(codeUnit);
// Clear potential wide char we are about to overwrite
if (characters[idx] == '') {
if (currentX - 1 >= 0) {
final prevIdx = idx - 1;
if (isWideGrapheme(characters[prevIdx])) {
characters[prevIdx] = ' ';
}
}
} else if (isWideGrapheme(characters[idx])) {
if (currentX + 1 < width) {
final nextIdx = idx + 1;
if (characters[nextIdx] == '') {
characters[nextIdx] = ' ';
}
}
}
characters[idx] = char;
final attrIdx = idx * 3;
attributes[attrIdx + 0] = hasFg ? fg : 0;
if (hasBg) {
attributes[attrIdx + 1] = blendColor(bg, attributes[attrIdx + 1]);
}
attributes[attrIdx + 2] = modifiers;
currentX += 1;
} else {
currentX += 1;
}
}
return;
}
final chars = text.characters;
final fg = style.foreground?.argb ?? 0;
final bg = style.background?.argb ?? 0;
final modifiers = style.modifiers;
final hasFg = style.foreground != null;
final hasBg = style.background != null;
for (final char in chars) {
if (char == '\n') {
currentX = startX;
currentY++;
continue;
}
if (isCellValid(currentX, currentY)) {
final idx = currentY * width + currentX;
// Clear potential wide char we are about to overwrite
if (characters[idx] == '') {
if (currentX - 1 >= 0) {
final prevIdx = idx - 1;
if (isWideGrapheme(characters[prevIdx])) {
characters[prevIdx] = ' ';
}
}
} else if (isWideGrapheme(characters[idx])) {
if (currentX + 1 < width) {
final nextIdx = idx + 1;
if (characters[nextIdx] == '') {
characters[nextIdx] = ' ';
}
}
}
final isWide = isWideGrapheme(char);
if (isWide && currentX == width - 1) {
// Can't fit wide character in the last column, write a space instead
characters[idx] = ' ';
final attrIdx = idx * 3;
attributes[attrIdx + 0] = hasFg ? fg : 0;
if (hasBg) {
attributes[attrIdx + 1] = blendColor(bg, attributes[attrIdx + 1]);
}
attributes[attrIdx + 2] = modifiers;
currentX += 1;
} else {
characters[idx] = char;
final attrIdx = idx * 3;
attributes[attrIdx + 0] = hasFg ? fg : 0;
if (hasBg) {
attributes[attrIdx + 1] = blendColor(bg, attributes[attrIdx + 1]);
}
attributes[attrIdx + 2] = modifiers;
if (isWide) {
if (currentX + 1 < width) {
final nextIdx = idx + 1;
// Clear potential wide char we are overwriting in the next cell
if (isWideGrapheme(characters[nextIdx]) && currentX + 2 < width) {
final nextNextIdx = idx + 2;
if (characters[nextNextIdx] == '') {
characters[nextNextIdx] = ' ';
}
}
characters[nextIdx] = '';
final nextAttrIdx = nextIdx * 3;
attributes[nextAttrIdx + 0] = hasFg ? fg : 0;
if (hasBg) attributes[nextAttrIdx + 1] = bg;
attributes[nextAttrIdx + 2] = modifiers;
}
currentX += 2;
} else {
currentX += 1;
}
}
} else {
currentX += 1;
}
}
}