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