Skip to main content

vt100/
attrs.rs

1use crate::term::BufWrite as _;
2
3/// Represents a foreground or background color for cells.
4#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]
5pub enum Color {
6    /// The default terminal color.
7    #[default]
8    Default,
9
10    /// An indexed terminal color.
11    Idx(u8),
12
13    /// An RGB terminal color. The parameters are (red, green, blue).
14    Rgb(u8, u8, u8),
15}
16
17const TEXT_MODE_INTENSITY: u8 = 0b0000_0011;
18const TEXT_MODE_BOLD: u8 = 0b0000_0001;
19const TEXT_MODE_DIM: u8 = 0b0000_0010;
20const TEXT_MODE_ITALIC: u8 = 0b0000_0100;
21const TEXT_MODE_UNDERLINE: u8 = 0b0000_1000;
22const TEXT_MODE_INVERSE: u8 = 0b0001_0000;
23
24#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
25pub struct Attrs {
26    pub fgcolor: Color,
27    pub bgcolor: Color,
28    pub mode: u8,
29}
30
31impl Attrs {
32    pub fn bold(&self) -> bool {
33        self.mode & TEXT_MODE_BOLD != 0
34    }
35
36    pub fn dim(&self) -> bool {
37        self.mode & TEXT_MODE_DIM != 0
38    }
39
40    fn intensity(&self) -> u8 {
41        self.mode & TEXT_MODE_INTENSITY
42    }
43
44    pub fn set_bold(&mut self) {
45        self.mode &= !TEXT_MODE_INTENSITY;
46        self.mode |= TEXT_MODE_BOLD;
47    }
48
49    pub fn set_dim(&mut self) {
50        self.mode &= !TEXT_MODE_INTENSITY;
51        self.mode |= TEXT_MODE_DIM;
52    }
53
54    pub fn set_normal_intensity(&mut self) {
55        self.mode &= !TEXT_MODE_INTENSITY;
56    }
57
58    pub fn italic(&self) -> bool {
59        self.mode & TEXT_MODE_ITALIC != 0
60    }
61
62    pub fn set_italic(&mut self, italic: bool) {
63        if italic {
64            self.mode |= TEXT_MODE_ITALIC;
65        } else {
66            self.mode &= !TEXT_MODE_ITALIC;
67        }
68    }
69
70    pub fn underline(&self) -> bool {
71        self.mode & TEXT_MODE_UNDERLINE != 0
72    }
73
74    pub fn set_underline(&mut self, underline: bool) {
75        if underline {
76            self.mode |= TEXT_MODE_UNDERLINE;
77        } else {
78            self.mode &= !TEXT_MODE_UNDERLINE;
79        }
80    }
81
82    pub fn inverse(&self) -> bool {
83        self.mode & TEXT_MODE_INVERSE != 0
84    }
85
86    pub fn set_inverse(&mut self, inverse: bool) {
87        if inverse {
88            self.mode |= TEXT_MODE_INVERSE;
89        } else {
90            self.mode &= !TEXT_MODE_INVERSE;
91        }
92    }
93
94    pub fn write_escape_code_diff(
95        &self,
96        contents: &mut Vec<u8>,
97        other: &Self,
98    ) {
99        if self != other && self == &Self::default() {
100            crate::term::ClearAttrs.write_buf(contents);
101            return;
102        }
103
104        let attrs = crate::term::Attrs::default();
105
106        let attrs = if self.fgcolor == other.fgcolor {
107            attrs
108        } else {
109            attrs.fgcolor(self.fgcolor)
110        };
111        let attrs = if self.bgcolor == other.bgcolor {
112            attrs
113        } else {
114            attrs.bgcolor(self.bgcolor)
115        };
116        let attrs = if self.intensity() == other.intensity() {
117            attrs
118        } else {
119            attrs.intensity(match self.intensity() {
120                0 => crate::term::Intensity::Normal,
121                TEXT_MODE_BOLD => crate::term::Intensity::Bold,
122                TEXT_MODE_DIM => crate::term::Intensity::Dim,
123                _ => unreachable!(),
124            })
125        };
126        let attrs = if self.italic() == other.italic() {
127            attrs
128        } else {
129            attrs.italic(self.italic())
130        };
131        let attrs = if self.underline() == other.underline() {
132            attrs
133        } else {
134            attrs.underline(self.underline())
135        };
136        let attrs = if self.inverse() == other.inverse() {
137            attrs
138        } else {
139            attrs.inverse(self.inverse())
140        };
141
142        attrs.write_buf(contents);
143    }
144}