1use ratatui::style::Style;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub(crate) enum Paint {
5 Ink,
6 Faint,
7 Own(Style),
8}
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[non_exhaustive]
12pub struct Part {
13 pub(crate) text: String,
14 pub(crate) paint: Option<Paint>,
15}
16
17impl Part {
18 pub fn new(text: impl Into<String>) -> Self {
19 Part {
20 text: text.into(),
21 paint: None,
22 }
23 }
24
25 pub fn faint(mut self) -> Self {
26 self.paint = Some(Paint::Faint);
27 self
28 }
29
30 pub fn style(mut self, style: Style) -> Self {
31 self.paint = Some(Paint::Own(style));
32 self
33 }
34
35 pub fn text(&self) -> &str {
36 &self.text
37 }
38}
39
40impl From<&str> for Part {
41 fn from(text: &str) -> Self {
42 Part::new(text)
43 }
44}
45
46impl From<String> for Part {
47 fn from(text: String) -> Self {
48 Part::new(text)
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub(crate) struct Run {
54 end: usize,
55 paint: Option<Paint>,
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
59#[non_exhaustive]
60pub struct Cell {
61 pub(crate) text: String,
62 pub(crate) paint: Paint,
63 pub(crate) runs: Vec<Run>,
64 pub(crate) rest: bool,
65}
66
67impl Cell {
68 pub fn new(text: impl Into<String>) -> Self {
69 Cell {
70 text: text.into(),
71 paint: Paint::Ink,
72 runs: Vec::new(),
73 rest: false,
74 }
75 }
76
77 pub fn parts<I, P>(parts: I) -> Self
78 where
79 I: IntoIterator<Item = P>,
80 P: Into<Part>,
81 {
82 let mut cell = Cell::new(String::new());
83 for part in parts {
84 let part = part.into();
85 cell.text.push_str(&part.text);
86 cell.runs.push(Run {
87 end: cell.text.len(),
88 paint: part.paint,
89 });
90 }
91 cell
92 }
93
94 pub fn faint(mut self) -> Self {
95 self.paint = Paint::Faint;
96 self
97 }
98
99 pub fn style(mut self, style: Style) -> Self {
100 self.paint = Paint::Own(style);
101 self
102 }
103
104 pub fn rest(mut self) -> Self {
105 self.rest = true;
106 self
107 }
108
109 pub fn text(&self) -> &str {
110 &self.text
111 }
112
113 pub fn set_text(&mut self, text: &str) {
114 self.text.clear();
115 self.text.push_str(text);
116 self.runs.clear();
117 }
118
119 pub fn set_part(&mut self, index: usize, text: &str) -> bool {
120 if self.runs.is_empty() {
121 if index == 0 {
122 self.text.clear();
123 self.text.push_str(text);
124 }
125 return index == 0;
126 }
127 let Some(end) = self.runs.get(index).map(|run| run.end) else {
128 return false;
129 };
130 let start = index
131 .checked_sub(1)
132 .map_or(0, |before| self.runs[before].end);
133 self.text.replace_range(start..end, text);
134 for run in &mut self.runs[index..] {
135 run.end = run.end - (end - start) + text.len();
136 }
137 true
138 }
139
140 pub fn set_style(&mut self, style: Style) {
141 self.paint = Paint::Own(style);
142 }
143
144 pub fn set_part_style(&mut self, index: usize, style: Style) -> bool {
145 if self.runs.is_empty() {
146 if index == 0 {
147 self.paint = Paint::Own(style);
148 }
149 return index == 0;
150 }
151 let Some(run) = self.runs.get_mut(index) else {
152 return false;
153 };
154 run.paint = Some(Paint::Own(style));
155 true
156 }
157
158 pub(crate) fn pieces(&self) -> Pieces<'_> {
159 Pieces {
160 cell: self,
161 at: 0,
162 start: 0,
163 }
164 }
165}
166
167#[derive(Debug, Clone)]
168pub(crate) struct Pieces<'c> {
169 cell: &'c Cell,
170 at: usize,
171 start: usize,
172}
173
174impl<'c> Iterator for Pieces<'c> {
175 type Item = (&'c str, Paint);
176
177 fn next(&mut self) -> Option<Self::Item> {
178 let cell = self.cell;
179 if cell.runs.is_empty() {
180 if self.at > 0 {
181 return None;
182 }
183 self.at = 1;
184 return Some((cell.text.as_str(), cell.paint));
185 }
186 let run = cell.runs.get(self.at)?;
187 let text = cell.text.get(self.start..run.end).unwrap_or("");
188 self.start = run.end;
189 self.at += 1;
190 Some((text, run.paint.unwrap_or(cell.paint)))
191 }
192}
193
194impl From<&str> for Cell {
195 fn from(text: &str) -> Self {
196 Cell::new(text)
197 }
198}
199
200impl From<String> for Cell {
201 fn from(text: String) -> Self {
202 Cell::new(text)
203 }
204}
205
206#[derive(Debug, Clone, PartialEq, Eq)]
207#[non_exhaustive]
208pub struct Mark {
209 pub(crate) text: String,
210 pub(crate) paint: Paint,
211}
212
213impl Mark {
214 pub fn new(text: impl Into<String>) -> Self {
215 Mark {
216 text: text.into(),
217 paint: Paint::Ink,
218 }
219 }
220
221 pub fn faint(mut self) -> Self {
222 self.paint = Paint::Faint;
223 self
224 }
225
226 pub fn style(mut self, style: Style) -> Self {
227 self.paint = Paint::Own(style);
228 self
229 }
230
231 pub fn text(&self) -> &str {
232 &self.text
233 }
234
235 pub fn set_text(&mut self, text: &str) {
236 self.text.clear();
237 self.text.push_str(text);
238 }
239
240 pub fn set_style(&mut self, style: Style) {
241 self.paint = Paint::Own(style);
242 }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
246pub(crate) enum Kind {
247 #[default]
248 Item,
249 Heading,
250 Blank,
251}
252
253#[derive(Debug, Clone, PartialEq, Eq, Default)]
254#[non_exhaustive]
255pub struct Row {
256 pub(crate) mark: Option<Mark>,
257 pub(crate) cells: Vec<Cell>,
258 pub(crate) kind: Kind,
259}
260
261impl Row {
262 pub fn new<I, C>(cells: I) -> Self
263 where
264 I: IntoIterator<Item = C>,
265 C: Into<Cell>,
266 {
267 Row {
268 mark: None,
269 cells: cells.into_iter().map(Into::into).collect(),
270 kind: Kind::Item,
271 }
272 }
273
274 pub fn heading<I, P>(parts: I) -> Self
275 where
276 I: IntoIterator<Item = P>,
277 P: Into<Part>,
278 {
279 Row {
280 mark: None,
281 cells: vec![Cell::parts(parts)],
282 kind: Kind::Heading,
283 }
284 }
285
286 pub fn blank() -> Self {
287 Row {
288 mark: None,
289 cells: Vec::new(),
290 kind: Kind::Blank,
291 }
292 }
293
294 pub fn mark(mut self, mark: Mark) -> Self {
295 self.mark = Some(mark);
296 self
297 }
298
299 pub fn cells(&self) -> &[Cell] {
300 &self.cells
301 }
302
303 pub fn cells_mut(&mut self) -> &mut [Cell] {
304 &mut self.cells
305 }
306
307 pub fn leading(&self) -> Option<&Mark> {
308 self.mark.as_ref()
309 }
310
311 pub fn leading_mut(&mut self) -> Option<&mut Mark> {
312 self.mark.as_mut()
313 }
314
315 pub fn selectable(&self) -> bool {
316 self.kind == Kind::Item
317 }
318
319 pub(crate) fn mark_width(&self) -> u16 {
320 match (&self.mark, self.kind) {
321 (Some(mark), Kind::Item) => crate::text::cells(&mark.text),
322 _ => 0,
323 }
324 }
325}