use crate::render::screenshot::Screenshot;
use crate::render::Attrs as RenderAttrs;
use super::grid::{Attrs, CellContent, Paint};
use super::vt::VtScreen;
impl VtScreen {
pub fn to_text(&self) -> String {
let grid = self.grid_ref();
let mut out = String::new();
for y in 0..grid.h {
out.push_str(grid.row_text(y).trim_end());
out.push('\n');
}
out
}
pub fn to_styled_dump(&self) -> String {
let grid = self.grid_ref();
let cursor = self.cursor();
let mut out = String::new();
out.push_str(&format!(
"size={}x{} cursor={},{}{} modes=[{}]\n",
grid.w,
grid.h,
cursor.x,
cursor.y,
if self.wrap_is_pending() { "+wrap" } else { "" },
self.modes()
.all_set()
.iter()
.map(u32::to_string)
.collect::<Vec<_>>()
.join(","),
));
for y in 0..grid.h {
out.push_str(&format!("{y:>3}|{}|\n", grid.row_text(y)));
}
let mut styles = String::new();
for y in 0..grid.h {
let mut x = 0;
while x < grid.w {
let paint = grid.cell(x, y).map(|c| c.paint).unwrap_or_default();
let mut end = x + 1;
while end < grid.w && grid.cell(end, y).map(|c| c.paint) == Some(paint) {
end += 1;
}
if !paint.is_default() {
styles.push_str(&format!(" {y}:{x}..{end} {}\n", paint_label(&paint)));
}
x = end;
}
}
if !styles.is_empty() {
out.push_str("styles:\n");
out.push_str(&styles);
}
out
}
pub fn screenshot(&self) -> Screenshot {
let grid = self.grid_ref();
let mut cells = Vec::with_capacity((grid.w * grid.h).max(0) as usize);
for y in 0..grid.h {
for x in 0..grid.w {
let Some(cell) = grid.cell(x, y) else {
continue;
};
let (text, width): (&str, u8) = match &cell.content {
CellContent::Blank => (" ", 1),
CellContent::Continuation => ("", 0),
CellContent::Text(s) => (s, if cell.is_wide_leader() { 2 } else { 1 }),
};
let p = cell.paint;
cells.push(Screenshot::make_cell(
text,
width,
p.fg.map(opaque),
p.bg.map(opaque),
p.ul.map(opaque),
map_attrs(p.attrs),
));
}
}
Screenshot::from_cells(self.size(), cells)
}
}
fn opaque(c: crate::base::Rgba) -> crate::base::Rgba {
crate::base::Rgba::rgb(c.r, c.g, c.b)
}
fn map_attrs(a: Attrs) -> RenderAttrs {
const MAP: [(u16, RenderAttrs); 9] = [
(Attrs::BOLD, RenderAttrs::BOLD),
(Attrs::DIM, RenderAttrs::DIM),
(Attrs::ITALIC, RenderAttrs::ITALIC),
(Attrs::UNDERLINE, RenderAttrs::UNDERLINE),
(Attrs::UNDERCURL, RenderAttrs::UNDERCURL),
(Attrs::BLINK, RenderAttrs::BLINK),
(Attrs::REVERSE, RenderAttrs::REVERSE),
(Attrs::HIDDEN, RenderAttrs::HIDDEN),
(Attrs::STRIKE, RenderAttrs::STRIKE),
];
let mut out = RenderAttrs::NONE;
for (bit, mapped) in MAP {
if a.contains(bit) {
out = out.with(mapped);
}
}
out
}
fn paint_label(p: &Paint) -> String {
let mut parts = Vec::new();
if let Some(fg) = p.fg {
parts.push(format!("fg={}", fg.to_hex()));
}
if let Some(bg) = p.bg {
parts.push(format!("bg={}", bg.to_hex()));
}
if let Some(ul) = p.ul {
parts.push(format!("ul={}", ul.to_hex()));
}
if !p.attrs.is_empty() {
parts.push(format!("attrs={}", p.attrs.label()));
}
if let Some(l) = p.link {
parts.push(format!("link={l}"));
}
parts.join(" ")
}