Skip to main content

pito_list/
row.rs

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}