1use crate::term::BufWrite as _;
2
3#[derive(Eq, PartialEq, Debug, Copy, Clone, Default)]
5pub enum Color {
6 #[default]
8 Default,
9
10 Idx(u8),
12
13 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}