1use std::fmt::{self, Display};
4
5use le::layout::{Rect, Vec2};
6use unicode_segmentation::UnicodeSegmentation;
7use unicode_width::UnicodeWidthStr;
8
9use super::{Style, StyledText};
10
11#[derive(Default, Debug, Clone, PartialEq, Eq)]
13pub enum CellText {
14 String(String),
16 Char(char),
18 #[default]
20 None,
21}
22
23impl CellText {
24 pub fn is_empty(&self) -> bool {
26 match self {
27 CellText::String(x) => x.is_empty(),
28 CellText::Char(_) => false,
29 CellText::None => true,
30 }
31 }
32}
33
34impl Display for CellText {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 match self {
37 CellText::String(x) => x.fmt(f),
38 CellText::Char(x) => x.fmt(f),
39 CellText::None => ' '.fmt(f),
40 }
41 }
42}
43
44#[derive(Debug, Clone, Default, PartialEq, Eq)]
46pub struct Cell {
47 pub text: CellText,
52 pub style: Style,
54}
55
56#[derive(Debug, Clone)]
58pub struct Buffer {
59 size: Vec2,
61 text: Vec<Vec<Cell>>,
64}
65
66impl Buffer {
67 pub fn new(size: Vec2) -> Self {
71 Self {
72 size,
73 text: vec![vec![Cell::default(); size.y]; size.x],
74 }
75 }
76
77 pub fn clear(&mut self) {
79 for column in &mut self.text {
80 for cell in column {
81 *cell = Cell::default();
82 }
83 }
84 }
85
86 pub fn size(&self) -> Vec2 {
88 self.size
89 }
90
91 pub fn text(&self) -> &[Vec<Cell>] {
93 &self.text
94 }
95
96 pub fn get(&self, pos: Vec2) -> Option<&Cell> {
98 self.text.get(pos.x).and_then(|x| x.get(pos.y))
99 }
100
101 pub fn get_mut(&mut self, pos: Vec2) -> Option<&mut Cell> {
103 self.text.get_mut(pos.x).and_then(|x| x.get_mut(pos.y))
104 }
105
106 pub fn write<'a>(&mut self, pos: Vec2, text: impl Into<StyledText<'a>>) {
109 self.write_span(pos, &text.into());
110 }
111
112 pub fn write_span(&mut self, mut pos: Vec2, text: &StyledText) {
115 let graphemes = text.text.graphemes(true);
116 for grapheme in graphemes {
117 if let Some(previous) = self.get_mut(pos) {
118 let mut grapheme_chars = grapheme.chars();
119 let char1 = grapheme_chars.next();
120 let char2 = grapheme_chars.next();
121 match (char1, char2) {
122 (Some(_char1), Some(_char2)) => {
123 previous.text = CellText::String(grapheme.to_owned());
124 }
125 (Some(char1), None) => {
126 previous.text = CellText::Char(char1);
127 }
128 _ => (),
129 }
130 previous.style = previous.style.overlay(text.style);
131 }
132 pos.x += grapheme.width();
133 }
134 }
135
136 pub fn write_buffer(&mut self, them: Buffer, pos: Vec2) {
138 for (x, column) in them.text.into_iter().enumerate() {
139 for (y, cell) in column.into_iter().enumerate() {
140 let cell_pos = pos + Vec2::new(x, y);
141
142 if let Some(previous) = self.get_mut(cell_pos) {
143 previous.text = cell.text;
144 previous.style = previous.style.overlay(cell.style);
145 };
146 }
147 }
148 }
149
150 pub fn write_buffer_ref(&mut self, them: &Buffer, pos: Vec2) {
152 for (x, column) in them.text.iter().enumerate() {
153 for (y, cell) in column.iter().enumerate() {
154 let cell_pos = pos + Vec2::new(x, y);
155
156 if let Some(previous) = self.get_mut(cell_pos) {
157 previous.text = cell.text.clone();
158 previous.style = previous.style.overlay(cell.style);
159 };
160 }
161 }
162 }
163
164 pub fn write_char(&mut self, c: char, pos: Vec2) {
166 if let Some(cell) = self.get_mut(pos) {
167 cell.text = CellText::Char(c);
168 }
169 }
170
171 pub fn style_cell(&mut self, style: Style, pos: Vec2) {
173 if let Some(cell) = self.get_mut(pos) {
174 cell.style = style;
175 }
176 }
177
178 pub fn subtract_pos(&mut self, pos: Vec2) {
182 for _x in 0..(pos.x.min(self.text.len())) {
183 self.text.remove(0);
184 }
185 for column in &mut self.text {
186 for _y in 0..(pos.y.min(column.len())) {
187 column.remove(0);
188 }
189 }
190 }
191
192 pub fn clip(&mut self, clip: Vec2) {
195 let mut column = 0;
196 self.text.retain(|_| {
197 column += 1;
198 (column - 1) < clip.x
199 });
200
201 for column in &mut self.text {
202 let mut row = 0;
203 column.retain(|_| {
204 row += 1;
205 (row - 1) < clip.y
206 });
207 }
208 }
209
210 pub fn crop(&mut self, crop: Rect) {
212 let mut column = 0;
213 self.text.retain(|_| {
214 let res = crop.start.x <= column && column < crop.end().x;
215 column += 1;
216 res
217 });
218
219 for column in &mut self.text {
220 let mut row = 0;
221 column.retain(|_| {
222 let res = crop.start.y <= row && row < crop.end().y;
223 row += 1;
224 res
225 });
226 }
227 }
228}