1use super::cell::Cell;
2use super::rect::{Position, Rect};
3use super::style::Style;
4use super::text::{Line, Span};
5use unicode_width::UnicodeWidthStr;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Buffer {
14 pub area: Rect,
15 pub content: Vec<Cell>,
16}
17
18impl Default for Buffer {
19 fn default() -> Self {
20 Self {
21 area: Rect::ZERO,
22 content: Vec::new(),
23 }
24 }
25}
26
27impl Buffer {
28 pub fn empty(area: Rect) -> Self {
30 let size = area.area() as usize;
31 Self {
32 area,
33 content: vec![Cell::default(); size],
34 }
35 }
36
37 pub fn with_lines<'a>(lines: impl IntoIterator<Item = &'a str>) -> Self {
39 let lines: Vec<&str> = lines.into_iter().collect();
40 let height = lines.len() as u16;
41 let width = lines.iter().map(|l| l.width() as u16).max().unwrap_or(0);
42 let area = Rect::new(0, 0, width, height);
43 let mut buf = Self::empty(area);
44 for (y, line) in lines.iter().enumerate() {
45 buf.set_string(0, y as u16, line, Style::default());
46 }
47 buf
48 }
49
50 pub fn reset(&mut self) {
52 for cell in &mut self.content {
53 cell.reset();
54 }
55 }
56
57 pub fn resize(&mut self, area: Rect) {
59 let size = area.area() as usize;
60 self.area = area;
61 self.content.clear();
62 self.content.resize(size, Cell::default());
63 }
64
65 pub fn cell(&self, pos: Position) -> Option<&Cell> {
67 self.index_of(pos.x, pos.y).map(|i| &self.content[i])
68 }
69
70 pub fn cell_mut(&mut self, pos: Position) -> Option<&mut Cell> {
72 self.index_of(pos.x, pos.y).map(|i| &mut self.content[i])
73 }
74
75 fn index_of(&self, x: u16, y: u16) -> Option<usize> {
76 if x >= self.area.x && x < self.area.right() && y >= self.area.y && y < self.area.bottom() {
77 Some(
78 ((y - self.area.y) as usize) * (self.area.width as usize)
79 + ((x - self.area.x) as usize),
80 )
81 } else {
82 None
83 }
84 }
85
86 pub fn set_string(&mut self, x: u16, y: u16, string: &str, style: Style) -> u16 {
89 self.set_string_truncated(x, y, string, self.area.right().saturating_sub(x), style)
90 }
91
92 pub fn set_string_truncated(
94 &mut self,
95 x: u16,
96 y: u16,
97 string: &str,
98 max_width: u16,
99 style: Style,
100 ) -> u16 {
101 let mut col = 0u16;
102 for grapheme in unicode_segmentation::UnicodeSegmentation::graphemes(string, true) {
103 let w = grapheme.width() as u16;
104 if col + w > max_width {
105 break;
106 }
107 if let Some(idx) = self.index_of(x + col, y) {
108 self.content[idx].set_symbol(grapheme).set_style(style);
109 for i in 1..w {
111 if let Some(idx2) = self.index_of(x + col + i, y) {
112 self.content[idx2].set_symbol("").set_style(style);
113 }
114 }
115 }
116 col += w;
117 }
118 col
119 }
120
121 pub fn set_line(&mut self, x: u16, y: u16, line: &Line, max_width: u16) -> u16 {
123 let mut col = 0u16;
124 for span in &line.spans {
125 if col >= max_width {
126 break;
127 }
128 let remaining = max_width - col;
129 let written =
130 self.set_string_truncated(x + col, y, &span.content, remaining, span.style);
131 col += written;
132 }
133 col
134 }
135
136 pub fn set_span(&mut self, x: u16, y: u16, span: &Span, max_width: u16) -> u16 {
138 self.set_string_truncated(x, y, &span.content, max_width, span.style)
139 }
140
141 pub fn set_style(&mut self, area: Rect, style: Style) {
143 let area = self.area.intersection(area);
144 for y in area.y..area.bottom() {
145 for x in area.x..area.right() {
146 if let Some(idx) = self.index_of(x, y) {
147 self.content[idx].set_style(style);
148 }
149 }
150 }
151 }
152
153 pub fn fill(&mut self, area: Rect, symbol: &str, style: Style) {
155 let area = self.area.intersection(area);
156 for y in area.y..area.bottom() {
157 for x in area.x..area.right() {
158 if let Some(idx) = self.index_of(x, y) {
159 self.content[idx].set_symbol(symbol).set_style(style);
160 }
161 }
162 }
163 }
164
165 pub fn diff<'a>(&'a self, other: &'a Buffer) -> Vec<(u16, u16, &'a Cell)> {
168 let mut changes = Vec::new();
169 let area = self.area.intersection(other.area);
170 for y in area.y..area.bottom() {
171 for x in area.x..area.right() {
172 if let (Some(a), Some(b)) = (self.index_of(x, y), other.index_of(x, y)) {
173 if self.content[a] != other.content[b] {
174 changes.push((x, y, &other.content[b]));
175 }
176 }
177 }
178 }
179 changes
180 }
181
182 pub fn merge(&mut self, other: &Buffer) {
184 let area = self.area.intersection(other.area);
185 for y in area.y..area.bottom() {
186 for x in area.x..area.right() {
187 if let (Some(dst), Some(src)) = (self.index_of(x, y), other.index_of(x, y)) {
188 self.content[dst] = other.content[src].clone();
189 }
190 }
191 }
192 }
193}
194
195impl std::ops::Index<(u16, u16)> for Buffer {
196 type Output = Cell;
197 fn index(&self, (x, y): (u16, u16)) -> &Self::Output {
198 static OOB_CELL: std::sync::LazyLock<Cell> = std::sync::LazyLock::new(Cell::default);
200 match self.index_of(x, y) {
201 Some(i) => &self.content[i],
202 None => &OOB_CELL,
203 }
204 }
205}
206
207impl std::ops::IndexMut<(u16, u16)> for Buffer {
208 fn index_mut(&mut self, (x, y): (u16, u16)) -> &mut Self::Output {
209 match self.index_of(x, y) {
213 Some(i) => &mut self.content[i],
214 None => {
215 self.content.push(Cell::default());
216 let last = self.content.len() - 1;
217 &mut self.content[last]
218 }
219 }
220 }
221}