spec_ai/spec_ai_tui/buffer/
buffer.rs1use super::Cell;
4use crate::spec_ai_tui::geometry::Rect;
5use crate::spec_ai_tui::style::{Line, Span, Style};
6
7#[derive(Debug, Clone)]
9pub struct Buffer {
10 area: Rect,
12 cells: Vec<Cell>,
14}
15
16impl Buffer {
17 pub fn new(area: Rect) -> Self {
19 let size = area.area() as usize;
20 Self {
21 area,
22 cells: vec![Cell::empty(); size],
23 }
24 }
25
26 pub fn filled(area: Rect, cell: Cell) -> Self {
28 let size = area.area() as usize;
29 Self {
30 area,
31 cells: vec![cell; size],
32 }
33 }
34
35 pub fn area(&self) -> Rect {
37 self.area
38 }
39
40 fn index(&self, x: u16, y: u16) -> Option<usize> {
42 if x >= self.area.x && x < self.area.right() && y >= self.area.y && y < self.area.bottom() {
43 let local_x = (x - self.area.x) as usize;
44 let local_y = (y - self.area.y) as usize;
45 Some(local_y * self.area.width as usize + local_x)
46 } else {
47 None
48 }
49 }
50
51 pub fn get(&self, x: u16, y: u16) -> Option<&Cell> {
53 self.index(x, y).map(|i| &self.cells[i])
54 }
55
56 pub fn get_mut(&mut self, x: u16, y: u16) -> Option<&mut Cell> {
58 self.index(x, y).map(|i| &mut self.cells[i])
59 }
60
61 pub fn set(&mut self, x: u16, y: u16, cell: Cell) {
63 if let Some(idx) = self.index(x, y) {
64 self.cells[idx] = cell;
65 }
66 }
67
68 pub fn set_symbol(&mut self, x: u16, y: u16, symbol: &str) {
70 if let Some(cell) = self.get_mut(x, y) {
71 cell.symbol = symbol.to_string();
72 }
73 }
74
75 pub fn set_string(&mut self, x: u16, y: u16, s: &str, style: Style) {
77 let mut current_x = x;
78 for c in s.chars() {
79 if current_x >= self.area.right() {
80 break;
81 }
82 if let Some(cell) = self.get_mut(current_x, y) {
83 cell.symbol = c.to_string();
84 cell.fg = style.fg;
85 cell.bg = style.bg;
86 cell.modifier = style.modifier;
87 }
88 let width = unicode_width::UnicodeWidthChar::width(c).unwrap_or(1);
90 current_x = current_x.saturating_add(width as u16);
91 }
92 }
93
94 pub fn set_string_raw(&mut self, x: u16, y: u16, s: &str) {
96 self.set_string(x, y, s, Style::default());
97 }
98
99 pub fn set_span(&mut self, x: u16, y: u16, span: &Span) {
101 self.set_string(x, y, &span.content, span.style);
102 }
103
104 pub fn set_line(&mut self, x: u16, y: u16, line: &Line) {
106 let mut current_x = x;
107 for span in &line.spans {
108 self.set_span(current_x, y, span);
109 current_x = current_x.saturating_add(span.width() as u16);
110 if current_x >= self.area.right() {
111 break;
112 }
113 }
114 }
115
116 pub fn fill(&mut self, area: Rect, cell: Cell) {
118 let clipped = self.area.intersect(&area);
119 for y in clipped.y..clipped.bottom() {
120 for x in clipped.x..clipped.right() {
121 self.set(x, y, cell.clone());
122 }
123 }
124 }
125
126 pub fn fill_style(&mut self, area: Rect, style: Style) {
128 let clipped = self.area.intersect(&area);
129 for y in clipped.y..clipped.bottom() {
130 for x in clipped.x..clipped.right() {
131 if let Some(cell) = self.get_mut(x, y) {
132 cell.fg = style.fg;
133 cell.bg = style.bg;
134 cell.modifier = style.modifier;
135 }
136 }
137 }
138 }
139
140 pub fn clear(&mut self) {
142 for cell in &mut self.cells {
143 cell.reset();
144 }
145 }
146
147 pub fn clear_area(&mut self, area: Rect) {
149 self.fill(area, Cell::empty());
150 }
151
152 pub fn iter(&self) -> impl Iterator<Item = (u16, u16, &Cell)> {
154 self.cells.iter().enumerate().map(move |(i, cell)| {
155 let x = self.area.x + (i % self.area.width as usize) as u16;
156 let y = self.area.y + (i / self.area.width as usize) as u16;
157 (x, y, cell)
158 })
159 }
160
161 pub fn diff<'a>(&'a self, other: &'a Buffer) -> impl Iterator<Item = (u16, u16, &'a Cell)> {
163 self.iter()
164 .filter(move |(x, y, cell)| other.get(*x, *y).map(|c| c != *cell).unwrap_or(true))
165 }
166
167 pub fn merge(&mut self, other: &Buffer) {
169 for (x, y, cell) in other.iter() {
170 self.set(x, y, cell.clone());
171 }
172 }
173
174 pub fn resize(&mut self, area: Rect) {
176 let new_size = area.area() as usize;
177 let mut new_cells = vec![Cell::empty(); new_size];
178
179 let copy_area = self.area.intersect(&area);
181 for y in copy_area.y..copy_area.bottom() {
182 for x in copy_area.x..copy_area.right() {
183 if let Some(old_cell) = self.get(x, y) {
184 let local_x = (x - area.x) as usize;
185 let local_y = (y - area.y) as usize;
186 let idx = local_y * area.width as usize + local_x;
187 if idx < new_cells.len() {
188 new_cells[idx] = old_cell.clone();
189 }
190 }
191 }
192 }
193
194 self.area = area;
195 self.cells = new_cells;
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use crate::spec_ai_tui::style::Color;
203
204 #[test]
205 fn test_buffer_creation() {
206 let area = Rect::new(0, 0, 10, 5);
207 let buf = Buffer::new(area);
208 assert_eq!(buf.area(), area);
209 assert_eq!(buf.cells.len(), 50);
210 }
211
212 #[test]
213 fn test_buffer_get_set() {
214 let area = Rect::new(0, 0, 10, 5);
215 let mut buf = Buffer::new(area);
216
217 buf.set(5, 2, Cell::new("X").fg(Color::Red));
218
219 let cell = buf.get(5, 2).unwrap();
220 assert_eq!(cell.symbol, "X");
221 assert_eq!(cell.fg, Color::Red);
222 }
223
224 #[test]
225 fn test_buffer_bounds() {
226 let area = Rect::new(5, 5, 10, 10);
227 let buf = Buffer::new(area);
228
229 assert!(buf.get(5, 5).is_some());
231 assert!(buf.get(14, 14).is_some());
232
233 assert!(buf.get(4, 5).is_none());
235 assert!(buf.get(15, 5).is_none());
236 }
237
238 #[test]
239 fn test_buffer_set_string() {
240 let area = Rect::new(0, 0, 20, 1);
241 let mut buf = Buffer::new(area);
242
243 buf.set_string(0, 0, "Hello", Style::new().fg(Color::Green));
244
245 assert_eq!(buf.get(0, 0).unwrap().symbol, "H");
246 assert_eq!(buf.get(4, 0).unwrap().symbol, "o");
247 assert_eq!(buf.get(5, 0).unwrap().symbol, " "); }
249
250 #[test]
251 fn test_buffer_fill() {
252 let area = Rect::new(0, 0, 5, 5);
253 let mut buf = Buffer::new(area);
254
255 let fill_area = Rect::new(1, 1, 3, 3);
256 buf.fill(fill_area, Cell::new("#").fg(Color::Blue));
257
258 assert_eq!(buf.get(0, 0).unwrap().symbol, " "); assert_eq!(buf.get(1, 1).unwrap().symbol, "#"); assert_eq!(buf.get(3, 3).unwrap().symbol, "#"); assert_eq!(buf.get(4, 4).unwrap().symbol, " "); }
263
264 #[test]
265 fn test_buffer_diff() {
266 let area = Rect::new(0, 0, 5, 5);
267 let mut buf1 = Buffer::new(area);
268 let mut buf2 = Buffer::new(area);
269
270 buf1.set(1, 1, Cell::new("A"));
271 buf2.set(1, 1, Cell::new("B"));
272 buf2.set(2, 2, Cell::new("C"));
273
274 let diffs: Vec<_> = buf2.diff(&buf1).collect();
275 assert_eq!(diffs.len(), 2);
276 }
277
278 #[test]
279 fn test_buffer_clear() {
280 let area = Rect::new(0, 0, 5, 5);
281 let mut buf = Buffer::new(area);
282
283 buf.set(2, 2, Cell::new("X"));
284 buf.clear();
285
286 assert_eq!(buf.get(2, 2).unwrap().symbol, " ");
287 }
288}