1pub mod cursor;
4pub mod damage;
5pub mod text;
6
7use std::io::{self, Write};
8
9use crossterm::{
10 cursor as ct_cursor, style, QueueableCommand,
11};
12use emux_term::Screen;
13
14use crate::cursor::cursor_style;
15use crate::damage::DamageTracker;
16use crate::text::render_row;
17
18pub struct Renderer {
20 damage: DamageTracker,
21 last_cols: usize,
22 last_rows: usize,
23}
24
25impl Renderer {
26 pub fn new(cols: usize, rows: usize) -> Self {
28 Self {
29 damage: DamageTracker::new(rows),
30 last_cols: cols,
31 last_rows: rows,
32 }
33 }
34
35 pub fn render<W: Write>(&mut self, writer: &mut W, screen: &Screen) -> io::Result<()> {
37 let cols = screen.cols();
38 let rows = screen.rows();
39
40 if cols != self.last_cols || rows != self.last_rows {
42 self.resize(cols, rows);
43 }
44
45 if !self.damage.needs_redraw() {
46 return Ok(());
47 }
48
49 writer.queue(ct_cursor::Hide)?;
51
52 let dirty = self.damage.dirty_rows();
53 for row in dirty {
54 if row >= rows {
55 continue;
56 }
57
58 writer.queue(ct_cursor::MoveTo(0, row as u16))?;
60
61 let grid_row = screen.grid.row(row);
63 let spans = render_row(&grid_row.cells, cols);
64
65 for (content_style, text) in spans {
66 writer.queue(style::ResetColor)?;
67 writer.queue(style::SetStyle(content_style))?;
68 writer.queue(style::Print(&text))?;
69 }
70 }
71
72 writer.queue(style::ResetColor)?;
74 writer.queue(style::SetAttribute(style::Attribute::Reset))?;
75
76 let cursor = &screen.cursor;
78 writer.queue(ct_cursor::MoveTo(cursor.col as u16, cursor.row as u16))?;
79
80 if cursor.visible {
82 writer.queue(ct_cursor::Show)?;
83 writer.queue(cursor_style(cursor.shape))?;
84 } else {
85 writer.queue(ct_cursor::Hide)?;
86 }
87
88 writer.flush()?;
89 self.damage.clear();
90
91 Ok(())
92 }
93
94 pub fn force_redraw(&mut self) {
96 self.damage.mark_all();
97 }
98
99 pub fn resize(&mut self, cols: usize, rows: usize) {
101 self.last_cols = cols;
102 self.last_rows = rows;
103 self.damage.resize(rows);
104 }
105}