1use tui::style::Modifier;
2
3use crate::term::BufWrite as _;
4
5#[derive(Eq, PartialEq, Debug, Copy, Clone)]
7pub enum Color {
8 Default,
10
11 Idx(u8),
13
14 Rgb(u8, u8, u8),
16}
17
18impl Default for Color {
19 fn default() -> Self {
20 Self::Default
21 }
22}
23
24impl Color {
25 pub fn to_tui(self) -> tui::style::Color {
26 match self {
27 Color::Default => tui::style::Color::Reset,
28 Color::Idx(index) => tui::style::Color::Indexed(index),
29 Color::Rgb(r, g, b) => tui::style::Color::Rgb(r, g, b),
30 }
31 }
32}
33
34impl From<termwiz::color::ColorSpec> for Color {
35 fn from(value: termwiz::color::ColorSpec) -> Self {
36 match value {
37 termwiz::color::ColorSpec::Default => Self::Default,
38 termwiz::color::ColorSpec::PaletteIndex(idx) => Self::Idx(idx),
39 termwiz::color::ColorSpec::TrueColor(srgba) => {
40 let (r, g, b, _) = srgba.to_srgb_u8();
41 Self::Rgb(r, g, b)
42 }
43 }
44 }
45}
46
47const TEXT_MODE_BOLD: u8 = 0b0000_0001;
48const TEXT_MODE_ITALIC: u8 = 0b0000_0010;
49const TEXT_MODE_UNDERLINE: u8 = 0b0000_0100;
50const TEXT_MODE_INVERSE: u8 = 0b0000_1000;
51
52#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
53pub struct Attrs {
54 pub fgcolor: Color,
55 pub bgcolor: Color,
56 pub mode: u8,
57}
58
59impl Attrs {
60 pub fn bold(&self) -> bool {
61 self.mode & TEXT_MODE_BOLD != 0
62 }
63
64 pub fn set_bold(&mut self, bold: bool) {
65 if bold {
66 self.mode |= TEXT_MODE_BOLD;
67 } else {
68 self.mode &= !TEXT_MODE_BOLD;
69 }
70 }
71
72 pub fn italic(&self) -> bool {
73 self.mode & TEXT_MODE_ITALIC != 0
74 }
75
76 pub fn set_italic(&mut self, italic: bool) {
77 if italic {
78 self.mode |= TEXT_MODE_ITALIC;
79 } else {
80 self.mode &= !TEXT_MODE_ITALIC;
81 }
82 }
83
84 pub fn underline(&self) -> bool {
85 self.mode & TEXT_MODE_UNDERLINE != 0
86 }
87
88 pub fn set_underline(&mut self, underline: bool) {
89 if underline {
90 self.mode |= TEXT_MODE_UNDERLINE;
91 } else {
92 self.mode &= !TEXT_MODE_UNDERLINE;
93 }
94 }
95
96 pub fn inverse(&self) -> bool {
97 self.mode & TEXT_MODE_INVERSE != 0
98 }
99
100 pub fn set_inverse(&mut self, inverse: bool) {
101 if inverse {
102 self.mode |= TEXT_MODE_INVERSE;
103 } else {
104 self.mode &= !TEXT_MODE_INVERSE;
105 }
106 }
107
108 pub fn write_escape_code_diff(&self, contents: &mut Vec<u8>, other: &Self) {
109 if self != other && self == &Self::default() {
110 crate::term::ClearAttrs::default().write_buf(contents);
111 return;
112 }
113
114 let attrs = crate::term::Attrs::default();
115
116 let attrs = if self.fgcolor == other.fgcolor {
117 attrs
118 } else {
119 attrs.fgcolor(self.fgcolor)
120 };
121 let attrs = if self.bgcolor == other.bgcolor {
122 attrs
123 } else {
124 attrs.bgcolor(self.bgcolor)
125 };
126 let attrs = if self.bold() == other.bold() {
127 attrs
128 } else {
129 attrs.bold(self.bold())
130 };
131 let attrs = if self.italic() == other.italic() {
132 attrs
133 } else {
134 attrs.italic(self.italic())
135 };
136 let attrs = if self.underline() == other.underline() {
137 attrs
138 } else {
139 attrs.underline(self.underline())
140 };
141 let attrs = if self.inverse() == other.inverse() {
142 attrs
143 } else {
144 attrs.inverse(self.inverse())
145 };
146
147 attrs.write_buf(contents);
148 }
149}
150
151impl Attrs {
152 pub fn mods_to_tui(&self) -> tui::style::Modifier {
153 let mut mods = Modifier::empty();
154 mods.set(Modifier::BOLD, self.bold());
155 mods.set(Modifier::ITALIC, self.italic());
156 mods.set(Modifier::UNDERLINED, self.underline());
157 mods.set(Modifier::REVERSED, self.inverse());
158 mods
159 }
160}