1use crate::buffer::CellStyle;
2use crate::geom::Insets;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum Theme {
7 Dark,
8 Light,
9}
10
11thread_local! {
12 pub static CURRENT_THEME: std::cell::RefCell<Theme> = std::cell::RefCell::new(Theme::Dark);
14}
15
16pub fn set_theme(theme: Theme) {
18 CURRENT_THEME.with(|t| *t.borrow_mut() = theme);
19 crate::runtime::FORCE_FULL_PAINT.with(|f| *f.borrow_mut() = true);
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum Color {
26 Black,
27 Red,
28 Green,
29 Yellow,
30 Blue,
31 Magenta,
32 Cyan,
33 White,
34 Gray,
35}
36
37impl From<Color> for crossterm::style::Color {
38 fn from(c: Color) -> Self {
39 let dark = CURRENT_THEME.with(|t| *t.borrow() == Theme::Dark);
40 match (c, dark) {
41 (Color::Black, _) => crossterm::style::Color::Black,
42 (Color::Red, true) => crossterm::style::Color::DarkRed,
43 (Color::Red, false) => crossterm::style::Color::Red,
44 (Color::Green, true) => crossterm::style::Color::DarkGreen,
45 (Color::Green, false) => crossterm::style::Color::Green,
46 (Color::Yellow, true) => crossterm::style::Color::DarkYellow,
47 (Color::Yellow, false) => crossterm::style::Color::Yellow,
48 (Color::Blue, true) => crossterm::style::Color::DarkBlue,
49 (Color::Blue, false) => crossterm::style::Color::Blue,
50 (Color::Magenta, true) => crossterm::style::Color::DarkMagenta,
51 (Color::Magenta, false) => crossterm::style::Color::Magenta,
52 (Color::Cyan, true) => crossterm::style::Color::DarkCyan,
53 (Color::Cyan, false) => crossterm::style::Color::Cyan,
54 (Color::White, _) => crossterm::style::Color::Grey,
55 (Color::Gray, true) => crossterm::style::Color::DarkGrey,
56 (Color::Gray, false) => crossterm::style::Color::Grey,
57 }
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum Length {
64 Auto,
66 Fixed(u16),
68 Percent(u16),
70 Fraction(u16),
72}
73
74impl Default for Length {
75 fn default() -> Self {
76 Length::Auto
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub enum Layout {
83 None,
84 Vertical,
85 Horizontal,
86}
87
88impl Default for Layout {
89 fn default() -> Self {
90 Layout::None
91 }
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
96pub enum TextWrap {
97 #[default]
99 None,
100 Char,
102 Word,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
108pub enum TextAlign {
109 #[default]
110 Left,
111 Center,
112 Right,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
117pub enum TextTruncate {
118 #[default]
120 None,
121 Ellipsis,
123}
124
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
127pub enum Border {
128 #[default]
129 None,
130 Plain,
131 Rounded,
132 Double,
133}
134
135#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct Style {
138 pub fg: Option<Color>,
140 pub bg: Option<Color>,
141 pub bold: bool,
142 pub italic: bool,
143 pub underline: bool,
144
145 pub width: Length,
147 pub height: Length,
148 pub padding: Insets,
149 pub margin: Insets,
150 pub layout: Layout,
151 pub gap: u16,
152 pub flex_grow: u16,
153 pub flex_shrink: bool,
154 pub border: Border,
155}
156
157impl Default for Style {
158 fn default() -> Self {
159 Self {
160 fg: None,
161 bg: None,
162 bold: false,
163 italic: false,
164 underline: false,
165 width: Length::Auto,
166 height: Length::Auto,
167 padding: Insets::ZERO,
168 margin: Insets::ZERO,
169 layout: Layout::None,
170 gap: 0,
171 flex_grow: 0,
172 flex_shrink: true,
173 border: Border::None,
174 }
175 }
176}
177
178impl Style {
179 pub fn fg(mut self, color: Color) -> Self {
181 self.fg = Some(color);
182 self
183 }
184
185 pub fn bg(mut self, color: Color) -> Self {
187 self.bg = Some(color);
188 self
189 }
190
191 pub fn bold(mut self) -> Self {
193 self.bold = true;
194 self
195 }
196
197 pub fn italic(mut self) -> Self {
199 self.italic = true;
200 self
201 }
202
203 pub fn underline(mut self) -> Self {
205 self.underline = true;
206 self
207 }
208
209 pub fn width(mut self, width: Length) -> Self {
211 self.width = width;
212 self
213 }
214
215 pub fn height(mut self, height: Length) -> Self {
217 self.height = height;
218 self
219 }
220
221 pub fn padding(mut self, value: u16) -> Self {
223 self.padding = Insets::all(value);
224 self
225 }
226
227 pub fn padding_each(mut self, top: u16, right: u16, bottom: u16, left: u16) -> Self {
229 self.padding = Insets { top, right, bottom, left };
230 self
231 }
232
233 pub fn margin(mut self, value: u16) -> Self {
235 self.margin = Insets::all(value);
236 self
237 }
238
239 pub fn layout(mut self, layout: Layout) -> Self {
241 self.layout = layout;
242 self
243 }
244
245 pub fn gap(mut self, gap: u16) -> Self {
247 self.gap = gap;
248 self
249 }
250
251 pub fn flex_grow(mut self, grow: u16) -> Self {
253 self.flex_grow = grow;
254 self
255 }
256
257 pub fn flex_shrink(mut self, shrink: bool) -> Self {
259 self.flex_shrink = shrink;
260 self
261 }
262
263 pub fn border(mut self, border: Border) -> Self {
265 self.border = border;
266 self
267 }
268
269 pub fn into_cell_style(&self) -> CellStyle {
271 CellStyle {
272 fg: self.fg,
273 bg: self.bg,
274 bold: self.bold,
275 italic: self.italic,
276 underline: self.underline,
277 }
278 }
279}