too/renderer/
cell.rs

1use compact_str::{CompactString, ToCompactString};
2use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
3
4use crate::{renderer::Rgba, Str};
5
6#[derive(Clone, PartialEq, Eq, Debug)]
7pub enum Cell {
8    Grapheme(Grapheme),
9    Pixel(Pixel),
10    Empty,
11    Continuation,
12}
13
14impl Cell {
15    pub fn set_fg(&mut self, fg: impl Into<Color>) {
16        match self {
17            Self::Grapheme(grapheme) => grapheme.fg = fg.into(),
18            Self::Pixel(pixel) => pixel.fg = fg.into(),
19            _ => {}
20        }
21    }
22
23    pub fn set_bg(&mut self, bg: impl Into<Color>) {
24        match self {
25            Self::Grapheme(grapheme) => grapheme.bg = bg.into(),
26            Self::Pixel(pixel) => pixel.bg = bg.into(),
27            _ => {}
28        }
29    }
30
31    pub fn set_attribute(&mut self, attribute: Attribute) {
32        match self {
33            Self::Grapheme(grapheme) => grapheme.attribute |= attribute,
34            Self::Pixel(pixel) => pixel.attribute |= attribute,
35            _ => {}
36        }
37    }
38}
39
40impl Cell {
41    pub(crate) fn is_same(&self, other: &Self) -> bool {
42        fn check(fg: Color, bg: Color) -> bool {
43            matches!(bg, Color::Reuse) || matches!(fg, Color::Reuse)
44        }
45
46        match (self, other) {
47            (Cell::Grapheme(left), Cell::Grapheme(right)) => {
48                (left == right) || (left.cluster == right.cluster && check(right.bg, right.fg))
49            }
50            (Cell::Grapheme(left), Cell::Pixel(right)) => {
51                compare(&left.cluster, right.char) && check(right.bg, right.fg)
52            }
53            (Cell::Pixel(left), Cell::Grapheme(right)) => {
54                compare(&right.cluster, left.char) && check(right.bg, right.fg)
55            }
56            (Cell::Pixel(left), Cell::Pixel(right)) => {
57                (left == right) || ((left.char == right.char) && check(right.bg, right.fg))
58            }
59            (Cell::Empty, Cell::Grapheme(..) | Cell::Pixel(..)) => false,
60            (Cell::Empty, Cell::Continuation | Cell::Empty) => true,
61            _ => false,
62        }
63    }
64
65    pub(crate) fn merge(mut this: &mut Self, other: Self) {
66        fn merge_fg(left_fg: &mut Color, right_fg: Color) {
67            if let (Color::Reset | Color::Set(..), ..) = (right_fg, &left_fg) {
68                *left_fg = right_fg
69            }
70        }
71
72        fn merge_bg(left_bg: &mut Color, right_bg: Color) {
73            match (right_bg, &left_bg) {
74                (Color::Set(a), Color::Set(b)) => *left_bg = Color::Set(a.blend_alpha(*b)),
75                (Color::Reset | Color::Set(..), ..) => *left_bg = right_bg,
76                _ => {}
77            }
78        }
79
80        match (&mut this, other) {
81            (Cell::Grapheme(ref mut left), Cell::Grapheme(mut right)) => {
82                merge_fg(&mut left.fg, right.fg);
83                merge_bg(&mut left.bg, right.bg);
84                left.attribute = right.attribute;
85                left.cluster = std::mem::take(&mut right.cluster);
86            }
87            (Cell::Grapheme(ref mut left), Cell::Pixel(right)) => {
88                merge_fg(&mut left.fg, right.fg);
89                merge_bg(&mut left.bg, right.bg);
90                let pixel = Pixel {
91                    char: right.char,
92                    fg: left.fg,
93                    bg: left.bg,
94                    attribute: right.attribute,
95                };
96                *this = Cell::Pixel(pixel)
97            }
98            (Cell::Pixel(ref mut left), Cell::Grapheme(mut right)) => {
99                merge_fg(&mut left.fg, right.fg);
100                merge_bg(&mut left.bg, right.bg);
101                let grapheme = Grapheme {
102                    cluster: std::mem::take(&mut right.cluster),
103                    fg: left.fg,
104                    bg: left.bg,
105                    attribute: right.attribute,
106                };
107                *this = Cell::Grapheme(grapheme)
108            }
109
110            (Cell::Pixel(ref mut left), Cell::Pixel(right)) => {
111                merge_fg(&mut left.fg, right.fg);
112                merge_bg(&mut left.bg, right.bg);
113                left.attribute = right.attribute;
114                left.char = right.char;
115            }
116
117            (_, right @ (Cell::Grapheme(..) | Cell::Pixel(..))) => *this = right,
118            _ => {}
119        }
120    }
121
122    pub(crate) fn width(&self) -> usize {
123        match self {
124            Self::Grapheme(grapheme) => grapheme.cluster.width(),
125            Self::Pixel(pixel) => pixel.char.width().unwrap_or(0),
126            Self::Empty | Self::Continuation => 0,
127        }
128    }
129
130    pub(crate) const fn is_continuation(&self) -> bool {
131        matches!(self, Self::Continuation)
132    }
133
134    pub(crate) const fn is_empty(&self) -> bool {
135        matches!(self, Self::Empty)
136    }
137
138    pub(crate) const fn fg(&self) -> Color {
139        match self {
140            Self::Grapheme(grapheme) => grapheme.fg,
141            Self::Pixel(pixel) => pixel.fg,
142            _ => unreachable!(),
143        }
144    }
145
146    pub(crate) const fn bg(&self) -> Color {
147        match self {
148            Self::Grapheme(grapheme) => grapheme.bg,
149            Self::Pixel(pixel) => pixel.bg,
150            _ => unreachable!(),
151        }
152    }
153
154    pub(crate) const fn attribute(&self) -> Attribute {
155        match self {
156            Self::Grapheme(grapheme) => grapheme.attribute,
157            Self::Pixel(pixel) => pixel.attribute,
158            _ => unreachable!(),
159        }
160    }
161}
162
163impl Default for Cell {
164    fn default() -> Self {
165        Self::Empty
166    }
167}
168
169// impl std::fmt::Debug for Cell {
170//     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171//         match self {
172//             Self::Grapheme(grapheme) => f.debug_tuple("Grapheme").field(&grapheme.cluster).finish(),
173//             Self::Pixel(pixel) => f.debug_tuple("Pixel").field(&pixel.char).finish(),
174//             Self::Empty => write!(f, "Empty"),
175//             Self::Continuation => write!(f, "Continuation"),
176//         }
177//     }
178// }
179
180impl From<Grapheme> for Cell {
181    fn from(value: Grapheme) -> Self {
182        Self::Grapheme(value)
183    }
184}
185
186impl From<Pixel> for Cell {
187    fn from(value: Pixel) -> Self {
188        Self::Pixel(value)
189    }
190}
191
192impl From<Rgba> for Cell {
193    fn from(bg: Rgba) -> Self {
194        Cell::Pixel(Pixel::from(bg))
195    }
196}
197
198impl<T: ToCompactString> From<T> for Cell {
199    fn from(value: T) -> Self {
200        Self::Grapheme(Grapheme::from(value))
201    }
202}
203
204#[derive(Copy, Clone, Debug, PartialEq, Eq)]
205pub struct Pixel {
206    pub(crate) char: char,
207    pub(crate) fg: Color,
208    pub(crate) bg: Color,
209    attribute: Attribute,
210}
211
212impl Default for Pixel {
213    fn default() -> Self {
214        Self::DEFAULT
215    }
216}
217
218impl From<Rgba> for Pixel {
219    fn from(value: Rgba) -> Self {
220        Self::new(' ').bg(value)
221    }
222}
223
224pub(crate) fn compare(left: &str, right: char) -> bool {
225    let mut b: [u8; 4] = [0; 4];
226    left == right.encode_utf8(&mut b)
227}
228
229impl Pixel {
230    pub(crate) const DEFAULT: Self = Self {
231        char: ' ',
232        fg: Color::Reset,
233        bg: Color::Reset,
234        attribute: Attribute::RESET,
235    };
236
237    pub const fn new(char: char) -> Self {
238        Self {
239            char,
240            fg: Color::Reset,
241            bg: Color::Reuse,
242            attribute: Attribute::RESET,
243        }
244    }
245
246    pub const fn char(mut self, char: char) -> Self {
247        self.char = char;
248        self
249    }
250
251    pub fn fg(mut self, fg: impl Into<Color>) -> Self {
252        self.fg = fg.into();
253        self
254    }
255
256    pub fn bg(mut self, bg: impl Into<Color>) -> Self {
257        self.bg = bg.into();
258        self
259    }
260
261    pub fn attribute(mut self, attribute: Attribute) -> Self {
262        self.attribute = attribute;
263        self
264    }
265}
266
267impl From<char> for Pixel {
268    fn from(char: char) -> Self {
269        Self::new(char)
270    }
271}
272
273#[derive(Clone, Debug, PartialEq, Eq)]
274pub struct Grapheme {
275    pub(crate) cluster: CompactString,
276    pub(crate) fg: Color,
277    pub(crate) bg: Color,
278    attribute: Attribute,
279}
280
281impl Grapheme {
282    pub const fn const_new(str: &'static str) -> Self {
283        Self {
284            cluster: CompactString::const_new(str),
285            fg: Color::Reset,
286            bg: Color::Reuse,
287            attribute: Attribute::RESET,
288        }
289    }
290
291    pub fn new(data: impl Into<Str>) -> Self {
292        Self {
293            cluster: data.into().into_inner(),
294            fg: Color::Reset,
295            bg: Color::Reuse,
296            attribute: Attribute::RESET,
297        }
298    }
299
300    pub fn fg(mut self, fg: impl Into<Color>) -> Self {
301        self.fg = fg.into();
302        self
303    }
304
305    pub fn bg(mut self, bg: impl Into<Color>) -> Self {
306        self.bg = bg.into();
307        self
308    }
309
310    pub fn attribute(mut self, attribute: Attribute) -> Self {
311        self.attribute = attribute;
312        self
313    }
314}
315
316impl<T: ToCompactString> From<T> for Grapheme {
317    fn from(value: T) -> Self {
318        Self::new(value)
319    }
320}
321
322// #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
323// pub struct Underline(pub(crate) u8);
324// impl Underline {
325//     // <ESC>[4:0m  # no underline
326//     // <ESC>[4:1m  # straight underline
327//     // <ESC>[4:2m  # double underline
328//     // <ESC>[4:3m  # curly underline
329//     // <ESC>[4:4m  # dotted underline
330//     // <ESC>[4:5m  # dashed underline
331
332//     pub const NONE: Self = Self(0);
333//     pub const STRAIGHT: Self = Self(1 << 0);
334//     pub const DOUBLE: Self = Self(1 << 1);
335//     pub const CURLY: Self = Self(1 << 2);
336//     pub const DOTTED: Self = Self(1 << 3);
337//     pub const DASHED: Self = Self(1 << 4);
338// }
339
340/// Attributes for a [`Pixel`] like _italic_ or _bold_
341#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Hash)]
342pub struct Attribute(pub u16);
343
344impl Attribute {
345    pub const RESET: Self = Self(0);
346    pub const BOLD: Self = Self(1 << 0);
347    pub const FAINT: Self = Self(1 << 1);
348    pub const ITALIC: Self = Self(1 << 2);
349    pub const UNDERLINE: Self = Self(1 << 3);
350    pub const BLINK: Self = Self(1 << 4);
351    pub const REVERSE: Self = Self(1 << 6);
352    pub const STRIKEOUT: Self = Self(1 << 8);
353}
354
355impl Attribute {
356    pub const fn is_reset(&self) -> bool {
357        self.0 == 0
358    }
359
360    pub const fn is_bold(&self) -> bool {
361        self.0 & (1 << 0) != 0
362    }
363
364    pub const fn is_faint(&self) -> bool {
365        self.0 & (1 << 1) != 0
366    }
367
368    pub const fn is_italic(&self) -> bool {
369        self.0 & (1 << 2) != 0
370    }
371
372    pub const fn is_underline(&self) -> bool {
373        self.0 & (1 << 3) != 0
374    }
375
376    pub const fn is_blink(&self) -> bool {
377        self.0 & (1 << 4) != 0
378    }
379
380    pub const fn is_reverse(&self) -> bool {
381        self.0 & (1 << 6) != 0
382    }
383
384    pub const fn is_strikeout(&self) -> bool {
385        self.0 & (1 << 8) != 0
386    }
387}
388
389impl std::ops::BitAnd for Attribute {
390    type Output = Self;
391    fn bitand(self, rhs: Self) -> Self::Output {
392        Self(self.0 & rhs.0)
393    }
394}
395impl std::ops::BitAndAssign for Attribute {
396    fn bitand_assign(&mut self, rhs: Self) {
397        *self = *self & rhs
398    }
399}
400
401impl std::ops::BitOr for Attribute {
402    type Output = Self;
403    fn bitor(self, rhs: Self) -> Self::Output {
404        Self(self.0 | rhs.0)
405    }
406}
407impl std::ops::BitOrAssign for Attribute {
408    fn bitor_assign(&mut self, rhs: Self) {
409        *self = *self | rhs
410    }
411}
412
413impl std::ops::Not for Attribute {
414    type Output = Self;
415    fn not(self) -> Self::Output {
416        Self(!self.0)
417    }
418}
419
420impl std::fmt::Debug for Attribute {
421    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422        const FIELDS: [&str; 9] = [
423            "Bold",
424            "Faint",
425            "Italic",
426            "Underline",
427            "Blink",
428            "", // rapid blink
429            "Reverse",
430            "", // conceal
431            "Strikeout",
432        ];
433
434        let mut seen = false;
435        for (flag, repr) in (0..).zip(FIELDS) {
436            if repr.is_empty() {
437                continue;
438            }
439
440            if (self.0 >> flag) & 1 == 1 {
441                if seen {
442                    f.write_str(" | ")?;
443                }
444                f.write_str(repr)?;
445                seen |= true
446            }
447        }
448
449        if !seen || self.0 == 0 {
450            f.write_str("Reset")?;
451        }
452
453        Ok(())
454    }
455}
456
457impl std::str::FromStr for Attribute {
458    type Err = String;
459    fn from_str(input: &str) -> Result<Self, Self::Err> {
460        let mut this = Self::RESET;
461        for part in input.split_terminator('+').map(<str>::trim) {
462            this |= match part {
463                s if s.eq_ignore_ascii_case("bold") => Self::BOLD,
464                s if s.eq_ignore_ascii_case("faint") => Self::FAINT,
465                s if s.eq_ignore_ascii_case("italic") => Self::ITALIC,
466                s if s.eq_ignore_ascii_case("underline") => Self::UNDERLINE,
467                s if s.eq_ignore_ascii_case("blink") => Self::BLINK,
468                s if s.eq_ignore_ascii_case("reverse") => Self::REVERSE,
469                s if s.eq_ignore_ascii_case("strikeout") => Self::STRIKEOUT,
470                attr => return Err(format!("unknown attribute: {attr}")),
471            }
472        }
473        Ok(this)
474    }
475}
476
477/// Color mode for a Pixel
478#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
479pub enum Color {
480    /// Use this color
481    Set(Rgba),
482    #[default]
483    /// Reuse the existing color
484    Reuse,
485    /// Reset the default color
486    Reset,
487}
488
489impl From<&'static str> for Color {
490    fn from(value: &'static str) -> Self {
491        Self::Set(Rgba::hex(value))
492    }
493}
494
495impl From<Rgba> for Color {
496    fn from(value: Rgba) -> Self {
497        Self::Set(value)
498    }
499}
500
501impl<T: Into<Rgba>> From<Option<T>> for Color {
502    fn from(value: Option<T>) -> Self {
503        value.map_or(Self::Reset, |val| Self::Set(val.into()))
504    }
505}