1use crate::{Contrast, Palette};
6use rat_widget::button::ButtonStyle;
7use rat_widget::calendar::CalendarStyle;
8use rat_widget::checkbox::CheckboxStyle;
9use rat_widget::choice::ChoiceStyle;
10use rat_widget::clipper::ClipperStyle;
11use rat_widget::file_dialog::FileDialogStyle;
12use rat_widget::line_number::LineNumberStyle;
13use rat_widget::list::ListStyle;
14use rat_widget::menu::MenuStyle;
15use rat_widget::msgdialog::MsgDialogStyle;
16use rat_widget::paragraph::ParagraphStyle;
17use rat_widget::radio::{RadioLayout, RadioStyle};
18use rat_widget::scrolled::ScrollStyle;
19use rat_widget::shadow::{ShadowDirection, ShadowStyle};
20use rat_widget::slider::SliderStyle;
21use rat_widget::splitter::SplitStyle;
22use rat_widget::statusline::StatusLineStyle;
23use rat_widget::tabbed::TabbedStyle;
24use rat_widget::table::TableStyle;
25use rat_widget::text::TextStyle;
26use rat_widget::view::ViewStyle;
27use ratatui_core::layout::Alignment;
28use ratatui_core::style::Color;
29use ratatui_core::style::Style;
30use ratatui_widgets::block::Block;
31use ratatui_widgets::borders::Borders;
32use std::borrow::Cow;
33use std::time::Duration;
34
35#[derive(Debug, Clone)]
43pub struct DarkTheme {
44 p: Palette,
45 name: &'static str,
46}
47
48impl DarkTheme {
49 pub const fn new(name: &'static str, s: Palette) -> Self {
50 Self { p: s, name }
51 }
52}
53
54impl DarkTheme {
55 pub fn name(&self) -> &str {
57 self.name
58 }
59
60 pub fn dark_theme(&self) -> bool {
62 true
63 }
64
65 pub fn palette(&self) -> &Palette {
67 &self.p
68 }
69
70 pub fn style(&self, bg: Color) -> Style {
72 self.p.style(bg, Contrast::Normal)
73 }
74
75 pub fn high_style(&self, bg: Color) -> Style {
77 self.p.style(bg, Contrast::High)
78 }
79
80 pub fn white(&self, n: usize) -> Style {
83 self.p.white(n, Contrast::Normal)
84 }
85
86 pub fn black(&self, n: usize) -> Style {
89 self.p.black(n, Contrast::Normal)
90 }
91
92 pub fn gray(&self, n: usize) -> Style {
95 self.p.gray(n, Contrast::Normal)
96 }
97
98 pub fn red(&self, n: usize) -> Style {
101 self.p.red(n, Contrast::Normal)
102 }
103
104 pub fn orange(&self, n: usize) -> Style {
107 self.p.orange(n, Contrast::Normal)
108 }
109
110 pub fn yellow(&self, n: usize) -> Style {
113 self.p.yellow(n, Contrast::Normal)
114 }
115
116 pub fn limegreen(&self, n: usize) -> Style {
119 self.p.limegreen(n, Contrast::Normal)
120 }
121
122 pub fn green(&self, n: usize) -> Style {
125 self.p.green(n, Contrast::Normal)
126 }
127
128 pub fn bluegreen(&self, n: usize) -> Style {
131 self.p.bluegreen(n, Contrast::Normal)
132 }
133
134 pub fn cyan(&self, n: usize) -> Style {
137 self.p.cyan(n, Contrast::Normal)
138 }
139
140 pub fn blue(&self, n: usize) -> Style {
143 self.p.blue(n, Contrast::Normal)
144 }
145
146 pub fn deepblue(&self, n: usize) -> Style {
149 self.p.deepblue(n, Contrast::Normal)
150 }
151
152 pub fn purple(&self, n: usize) -> Style {
155 self.p.purple(n, Contrast::Normal)
156 }
157
158 pub fn magenta(&self, n: usize) -> Style {
161 self.p.magenta(n, Contrast::Normal)
162 }
163
164 pub fn redpink(&self, n: usize) -> Style {
167 self.p.redpink(n, Contrast::Normal)
168 }
169
170 pub fn primary(&self, n: usize) -> Style {
173 self.p.primary(n, Contrast::Normal)
174 }
175
176 pub fn secondary(&self, n: usize) -> Style {
179 self.p.secondary(n, Contrast::Normal)
180 }
181
182 pub fn focus(&self) -> Style {
184 self.high_style(self.p.primary[2])
185 }
186
187 pub fn select(&self) -> Style {
189 self.high_style(self.p.secondary[1])
190 }
191
192 pub fn text_input(&self) -> Style {
194 self.high_style(self.p.gray[3])
195 }
196
197 pub fn text_focus(&self) -> Style {
199 self.high_style(self.p.primary[1])
200 }
201
202 pub fn text_select(&self) -> Style {
204 self.high_style(self.p.secondary[1])
205 }
206
207 pub fn container_base(&self) -> Style {
209 self.style(self.p.black[1])
210 }
211
212 pub fn container_border(&self) -> Style {
214 self.container_base().fg(self.p.gray[0])
215 }
216
217 pub fn container_arrow(&self) -> Style {
219 self.container_base().fg(self.p.gray[0])
220 }
221
222 pub fn popup_base(&self) -> Style {
224 self.style(self.p.white[0])
225 }
226
227 pub fn popup_label(&self) -> Style {
229 self.style(self.p.white[0])
230 }
231
232 pub fn popup_border(&self) -> Style {
234 self.popup_base().fg(self.p.gray[0])
235 }
236
237 pub fn popup_arrow(&self) -> Style {
239 self.popup_base().fg(self.p.gray[0])
240 }
241
242 pub fn dialog_base(&self) -> Style {
244 self.style(self.p.gray[1])
245 }
246
247 pub fn dialog_label(&self) -> Style {
249 self.dialog_base()
250 }
251
252 pub fn dialog_border(&self) -> Style {
254 self.dialog_base().fg(self.p.white[0])
255 }
256
257 pub fn dialog_arrow(&self) -> Style {
259 self.dialog_base().fg(self.p.white[0])
260 }
261
262 pub fn status_base(&self) -> Style {
264 self.style(self.p.black[2])
265 }
266
267 pub fn button_base(&self) -> Style {
269 self.style(self.p.gray[2])
270 }
271
272 pub fn button_armed(&self) -> Style {
274 self.style(self.p.secondary[0])
275 }
276
277 pub fn month_style(&self) -> CalendarStyle {
279 CalendarStyle {
280 style: self.style(self.p.black[2]),
281 title: None,
282 weeknum: Some(Style::new().fg(self.p.limegreen[2])),
283 weekday: Some(Style::new().fg(self.p.limegreen[2])),
284 day: None,
285 select: Some(self.select()),
286 focus: Some(self.focus()),
287 ..CalendarStyle::default()
288 }
289 }
290
291 pub fn shadow_style(&self) -> ShadowStyle {
293 ShadowStyle {
294 style: Style::new().bg(self.p.black[0]),
295 dir: ShadowDirection::BottomRight,
296 ..ShadowStyle::default()
297 }
298 }
299
300 pub fn line_nr_style(&self) -> LineNumberStyle {
302 LineNumberStyle {
303 style: self.container_base().fg(self.p.gray[1]),
304 cursor: Some(self.text_select()),
305 ..LineNumberStyle::default()
306 }
307 }
308
309 pub fn textarea_style(&self) -> TextStyle {
311 TextStyle {
312 style: self.container_base(),
313 focus: Some(self.focus()),
314 select: Some(self.text_select()),
315 scroll: Some(self.scroll_style()),
316 border_style: Some(self.container_border()),
317 ..TextStyle::default()
318 }
319 }
320
321 pub fn text_style(&self) -> TextStyle {
323 TextStyle {
324 style: self.text_input(),
325 focus: Some(self.text_focus()),
326 select: Some(self.text_select()),
327 invalid: Some(Style::default().bg(self.p.red[3])),
328 ..TextStyle::default()
329 }
330 }
331
332 pub fn label_style(&self) -> Style {
334 self.container_base()
335 }
336
337 pub fn paragraph_style(&self) -> ParagraphStyle {
338 ParagraphStyle {
339 style: self.container_base(),
340 focus: Some(self.focus()),
341 scroll: Some(self.scroll_style()),
342 ..Default::default()
343 }
344 }
345
346 pub fn choice_style(&self) -> ChoiceStyle {
347 ChoiceStyle {
348 style: self.text_input(),
349 select: Some(self.text_select()),
350 focus: Some(self.text_focus()),
351 popup_style: Some(self.popup_base()),
352 popup_border: Some(self.popup_border()),
353 popup_scroll: Some(self.popup_scroll_style()),
354 popup_block: Some(
355 Block::bordered()
356 .borders(Borders::LEFT)
357 .border_style(self.popup_border()),
358 ),
359 ..Default::default()
360 }
361 }
362
363 pub fn radio_style(&self) -> RadioStyle {
364 RadioStyle {
365 layout: Some(RadioLayout::Stacked),
366 style: self.text_input(),
367 focus: Some(self.text_focus()),
368 ..Default::default()
369 }
370 }
371
372 pub fn checkbox_style(&self) -> CheckboxStyle {
374 CheckboxStyle {
375 style: self.text_input(),
376 focus: Some(self.text_focus()),
377 ..Default::default()
378 }
379 }
380
381 pub fn slider_style(&self) -> SliderStyle {
383 SliderStyle {
384 style: self.text_input(),
385 bounds: Some(self.gray(2)),
386 knob: Some(self.select()),
387 focus: Some(self.focus()),
388 text_align: Some(Alignment::Center),
389 ..Default::default()
390 }
391 }
392
393 pub fn menu_style(&self) -> MenuStyle {
395 let menu = Style::default().fg(self.p.white[3]).bg(self.p.black[2]);
396 MenuStyle {
397 style: menu,
398 title: Some(Style::default().fg(self.p.black[0]).bg(self.p.yellow[2])),
399 focus: Some(self.focus()),
400 right: Some(Style::default().fg(self.p.bluegreen[0])),
401 disabled: Some(Style::default().fg(self.p.gray[0])),
402 highlight: Some(Style::default().underlined()),
403 popup_style: Some(menu),
404 popup_block: Some(Block::bordered()),
405 popup_focus: Some(self.focus()),
406 popup_right: Some(Style::default().fg(self.p.bluegreen[0])),
407 popup_disabled: Some(Style::default().fg(self.p.gray[0])),
408 popup_highlight: Some(Style::default().underlined()),
409 popup: Default::default(),
410 ..Default::default()
411 }
412 }
413
414 pub fn button_style(&self) -> ButtonStyle {
416 ButtonStyle {
417 style: self.button_base(),
418 focus: Some(self.focus()),
419 armed: Some(self.select()),
420 hover: Some(self.select()),
421 armed_delay: Some(Duration::from_millis(50)),
422 ..Default::default()
423 }
424 }
425
426 pub fn table_style(&self) -> TableStyle {
428 TableStyle {
429 style: self.container_base(),
430 select_row: Some(self.select()),
431 show_row_focus: true,
432 focus_style: Some(self.focus()),
433 border_style: Some(self.container_border()),
434 scroll: Some(self.scroll_style()),
435 ..Default::default()
436 }
437 }
438
439 pub fn table_header(&self) -> Style {
440 self.style(self.p.blue[2])
441 }
442
443 pub fn table_footer(&self) -> Style {
444 self.style(self.p.blue[2])
445 }
446
447 pub fn list_style(&self) -> ListStyle {
449 ListStyle {
450 style: self.container_base(),
451 select: Some(self.select()),
452 focus: Some(self.focus()),
453 scroll: Some(self.scroll_style()),
454 ..Default::default()
455 }
456 }
457
458 pub fn scroll_style(&self) -> ScrollStyle {
460 ScrollStyle {
461 thumb_style: Some(self.container_border()),
462 track_style: Some(self.container_border()),
463 min_style: Some(self.container_border()),
464 begin_style: Some(self.container_arrow()),
465 end_style: Some(self.container_arrow()),
466 ..Default::default()
467 }
468 }
469
470 pub fn popup_scroll_style(&self) -> ScrollStyle {
472 ScrollStyle {
473 thumb_style: Some(self.popup_border()),
474 track_style: Some(self.popup_border()),
475 min_style: Some(self.popup_border()),
476 begin_style: Some(self.popup_arrow()),
477 end_style: Some(self.popup_arrow()),
478 ..Default::default()
479 }
480 }
481
482 pub fn dialog_scroll_style(&self) -> ScrollStyle {
484 ScrollStyle {
485 thumb_style: Some(self.dialog_border()),
486 track_style: Some(self.dialog_border()),
487 min_style: Some(self.dialog_border()),
488 begin_style: Some(self.dialog_arrow()),
489 end_style: Some(self.dialog_arrow()),
490 ..Default::default()
491 }
492 }
493
494 pub fn split_style(&self) -> SplitStyle {
496 SplitStyle {
497 style: self.container_border(),
498 arrow_style: Some(self.container_arrow()),
499 drag_style: Some(self.focus()),
500 ..Default::default()
501 }
502 }
503
504 pub fn view_style(&self) -> ViewStyle {
506 ViewStyle {
507 scroll: Some(self.scroll_style()),
508 ..Default::default()
509 }
510 }
511
512 pub fn tabbed_style(&self) -> TabbedStyle {
514 let style = self.high_style(self.p.black[1]);
515 TabbedStyle {
516 style,
517 tab: Some(self.gray(1)),
518 select: Some(self.style(self.p.primary[4])),
519 focus: Some(self.focus()),
520 ..Default::default()
521 }
522 }
523
524 pub fn statusline_style(&self) -> Vec<Style> {
529 vec![
530 self.status_base(),
531 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
532 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
533 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
534 ]
535 }
536
537 pub fn statusline_style_ext(&self) -> StatusLineStyle {
539 StatusLineStyle {
540 sep: Some(Cow::Borrowed("|")),
541 styles: vec![
542 self.status_base(),
543 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
544 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
545 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
546 ],
547 ..Default::default()
548 }
549 }
550
551 pub fn file_dialog_style(&self) -> FileDialogStyle {
553 FileDialogStyle {
554 style: self.dialog_base(),
555 list: Some(self.list_style()),
556 roots: Some(ListStyle {
557 style: self.dialog_base(),
558 ..self.list_style()
559 }),
560 text: Some(self.text_style()),
561 button: Some(self.button_style()),
562 block: Some(Block::bordered()),
563 ..Default::default()
564 }
565 }
566
567 pub fn msg_dialog_style(&self) -> MsgDialogStyle {
569 MsgDialogStyle {
570 style: self.dialog_base(),
571 button: Some(self.button_style()),
572 ..Default::default()
573 }
574 }
575
576 pub fn clipper_style(&self) -> ClipperStyle {
578 ClipperStyle {
579 style: self.container_base(),
580 scroll: Some(self.scroll_style()),
581 ..Default::default()
582 }
583 }
584}