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;
17#[allow(deprecated)]
18use rat_widget::pager::PagerStyle;
19use rat_widget::paragraph::ParagraphStyle;
20use rat_widget::popup::PopupStyle;
21use rat_widget::radio::{RadioLayout, RadioStyle};
22use rat_widget::scrolled::ScrollStyle;
23use rat_widget::shadow::{ShadowDirection, ShadowStyle};
24use rat_widget::slider::SliderStyle;
25use rat_widget::splitter::SplitStyle;
26use rat_widget::statusline::StatusLineStyle;
27use rat_widget::tabbed::TabbedStyle;
28use rat_widget::table::TableStyle;
29use rat_widget::text::TextStyle;
30use rat_widget::view::ViewStyle;
31use ratatui::layout::Alignment;
32use ratatui::style::Color;
33use ratatui::style::{Style, Stylize};
34use ratatui::widgets::{Block, Borders};
35use std::time::Duration;
36
37#[derive(Debug, Clone)]
45pub struct DarkTheme {
46 p: Palette,
47 name: Box<str>,
48}
49
50impl DarkTheme {
51 pub fn new(name: &str, s: Palette) -> Self {
52 Self {
53 p: s,
54 name: Box::from(name),
55 }
56 }
57
58 fn style(&self, bg: Color) -> Style {
60 self.p.style(bg, Contrast::Normal)
61 }
62
63 fn high_style(&self, bg: Color) -> Style {
65 self.p.style(bg, Contrast::High)
66 }
67
68 fn table_header(&self) -> Style {
69 self.style(self.p.blue[2])
70 }
71
72 fn table_footer(&self) -> Style {
73 self.style(self.p.blue[2])
74 }
75}
76
77impl SalsaTheme for DarkTheme {
78 fn name(&self) -> &str {
80 &self.name
81 }
82
83 fn palette(&self) -> &Palette {
85 &self.p
86 }
87
88 fn white(&self, n: usize) -> Style {
91 self.p.white(n, Contrast::Normal)
92 }
93
94 fn black(&self, n: usize) -> Style {
97 self.p.black(n, Contrast::Normal)
98 }
99
100 fn gray(&self, n: usize) -> Style {
103 self.p.gray(n, Contrast::Normal)
104 }
105
106 fn red(&self, n: usize) -> Style {
109 self.p.red(n, Contrast::Normal)
110 }
111
112 fn orange(&self, n: usize) -> Style {
115 self.p.orange(n, Contrast::Normal)
116 }
117
118 fn yellow(&self, n: usize) -> Style {
121 self.p.yellow(n, Contrast::Normal)
122 }
123
124 fn limegreen(&self, n: usize) -> Style {
127 self.p.limegreen(n, Contrast::Normal)
128 }
129
130 fn green(&self, n: usize) -> Style {
133 self.p.green(n, Contrast::Normal)
134 }
135
136 fn bluegreen(&self, n: usize) -> Style {
139 self.p.bluegreen(n, Contrast::Normal)
140 }
141
142 fn cyan(&self, n: usize) -> Style {
145 self.p.cyan(n, Contrast::Normal)
146 }
147
148 fn blue(&self, n: usize) -> Style {
151 self.p.blue(n, Contrast::Normal)
152 }
153
154 fn deepblue(&self, n: usize) -> Style {
157 self.p.deepblue(n, Contrast::Normal)
158 }
159
160 fn purple(&self, n: usize) -> Style {
163 self.p.purple(n, Contrast::Normal)
164 }
165
166 fn magenta(&self, n: usize) -> Style {
169 self.p.magenta(n, Contrast::Normal)
170 }
171
172 fn redpink(&self, n: usize) -> Style {
175 self.p.redpink(n, Contrast::Normal)
176 }
177
178 fn primary(&self, n: usize) -> Style {
181 self.p.primary(n, Contrast::Normal)
182 }
183
184 fn secondary(&self, n: usize) -> Style {
187 self.p.secondary(n, Contrast::Normal)
188 }
189
190 fn focus(&self) -> Style {
192 self.high_style(self.p.primary[2])
193 }
194
195 fn select(&self) -> Style {
197 self.high_style(self.p.secondary[1])
198 }
199
200 fn text_input(&self) -> Style {
202 self.high_style(self.p.gray[3])
203 }
204
205 fn text_focus(&self) -> Style {
207 self.high_style(self.p.primary[1])
208 }
209
210 fn text_select(&self) -> Style {
212 self.high_style(self.p.secondary[1])
213 }
214
215 fn container_base(&self) -> Style {
217 self.style(self.p.black[1])
218 }
219
220 fn container_border(&self) -> Style {
222 self.container_base().fg(self.p.gray[0])
223 }
224
225 fn container_arrow(&self) -> Style {
227 self.container_base().fg(self.p.gray[0])
228 }
229
230 fn popup_base(&self) -> Style {
232 self.style(self.p.white[0])
233 }
234
235 fn popup_border(&self) -> Style {
237 self.popup_base().fg(self.p.gray[0])
238 }
239
240 fn popup_arrow(&self) -> Style {
242 self.popup_base().fg(self.p.gray[0])
243 }
244
245 fn dialog_base(&self) -> Style {
247 self.style(self.p.gray[1])
248 }
249
250 fn dialog_border(&self) -> Style {
252 self.dialog_base().fg(self.p.white[0])
253 }
254
255 fn dialog_arrow(&self) -> Style {
257 self.dialog_base().fg(self.p.white[0])
258 }
259
260 fn status_base(&self) -> Style {
262 self.style(self.p.black[2])
263 }
264
265 fn button_base(&self) -> Style {
267 self.style(self.p.gray[2])
268 }
269
270 fn button_armed(&self) -> Style {
272 self.style(self.p.secondary[0])
273 }
274
275 fn month_style(&self) -> CalendarStyle {
277 CalendarStyle {
278 style: self.style(self.p.black[2]),
279 title: None,
280 weeknum: Some(Style::new().fg(self.p.limegreen[2])),
281 weekday: Some(Style::new().fg(self.p.limegreen[2])),
282 day: None,
283 select: Some(self.select()),
284 focus: Some(self.focus()),
285 ..CalendarStyle::default()
286 }
287 }
288
289 fn shadow_style(&self) -> ShadowStyle {
291 ShadowStyle {
292 style: Style::new().bg(self.p.black[0]),
293 dir: ShadowDirection::BottomRight,
294 ..ShadowStyle::default()
295 }
296 }
297
298 fn line_nr_style(&self) -> LineNumberStyle {
300 LineNumberStyle {
301 style: self.container_base().fg(self.p.gray[1]),
302 cursor: Some(self.text_select()),
303 ..LineNumberStyle::default()
304 }
305 }
306
307 fn textarea_style(&self) -> TextStyle {
309 TextStyle {
310 style: self.text_input(),
311 focus: Some(self.focus()),
312 select: Some(self.text_select()),
313 scroll: Some(self.scroll_style()),
314 border_style: Some(self.container_border()),
315 ..TextStyle::default()
316 }
317 }
318
319 fn text_style(&self) -> TextStyle {
321 TextStyle {
322 style: self.text_input(),
323 focus: Some(self.text_focus()),
324 select: Some(self.text_select()),
325 invalid: Some(Style::default().bg(self.p.red[3])),
326 ..TextStyle::default()
327 }
328 }
329
330 fn label_style(&self) -> Style {
332 self.container_base()
333 }
334
335 fn paragraph_style(&self) -> ParagraphStyle {
336 ParagraphStyle {
337 style: self.container_base(),
338 focus: Some(self.focus()),
339 scroll: Some(self.scroll_style()),
340 ..Default::default()
341 }
342 }
343
344 fn choice_style(&self) -> ChoiceStyle {
345 ChoiceStyle {
346 style: self.text_input(),
347 select: Some(self.text_select()),
348 focus: Some(self.text_focus()),
349 popup_style: Some(self.popup_base()),
350 popup_border: Some(self.popup_border()),
351 popup_scroll: Some(self.popup_scroll_style()),
352 popup_block: Some(
353 Block::bordered()
354 .borders(Borders::LEFT)
355 .border_style(self.popup_border()),
356 ),
357 ..Default::default()
358 }
359 }
360
361 fn radio_style(&self) -> RadioStyle {
362 RadioStyle {
363 layout: Some(RadioLayout::Stacked),
364 style: self.text_input(),
365 focus: Some(self.text_focus()),
366 ..Default::default()
367 }
368 }
369
370 fn checkbox_style(&self) -> CheckboxStyle {
372 CheckboxStyle {
373 style: self.text_input(),
374 focus: Some(self.text_focus()),
375 ..Default::default()
376 }
377 }
378
379 fn slider_style(&self) -> SliderStyle {
381 SliderStyle {
382 style: self.text_input(),
383 bounds: Some(self.gray(2)),
384 knob: Some(self.select()),
385 focus: Some(self.focus()),
386 text_align: Some(Alignment::Center),
387 ..Default::default()
388 }
389 }
390
391 #[allow(deprecated)]
393 fn menu_style(&self) -> MenuStyle {
394 MenuStyle {
395 style: self.status_base(),
396 title: Some(self.style(self.p.yellow[2])),
397 focus: Some(self.focus()),
398 right: Some(Style::default().fg(self.p.bluegreen[0])),
399 disabled: Some(Style::default().fg(self.p.gray[0])),
400 highlight: Some(Style::default().underlined()),
401 popup: PopupStyle {
402 style: self.status_base(),
403 block: Some(Block::bordered()),
404 ..Default::default()
405 },
406 ..Default::default()
407 }
408 }
409
410 fn button_style(&self) -> ButtonStyle {
412 ButtonStyle {
413 style: self.button_base(),
414 focus: Some(self.focus()),
415 armed: Some(self.select()),
416 hover: Some(self.select()),
417 armed_delay: Some(Duration::from_millis(50)),
418 ..Default::default()
419 }
420 }
421
422 fn table_style(&self) -> TableStyle {
424 TableStyle {
425 style: self.container_base(),
426 select_row: Some(self.select()),
427 show_row_focus: true,
428 focus_style: Some(self.focus()),
429 border_style: Some(self.container_border()),
430 scroll: Some(self.scroll_style()),
431 header: Some(self.table_header()),
432 footer: Some(self.table_footer()),
433 ..Default::default()
434 }
435 }
436
437 fn list_style(&self) -> ListStyle {
439 ListStyle {
440 style: self.container_base(),
441 select: Some(self.select()),
442 focus: Some(self.focus()),
443 scroll: Some(self.scroll_style()),
444 ..Default::default()
445 }
446 }
447
448 fn scroll_style(&self) -> ScrollStyle {
450 ScrollStyle {
451 thumb_style: Some(self.container_border()),
452 track_style: Some(self.container_border()),
453 min_style: Some(self.container_border()),
454 begin_style: Some(self.container_arrow()),
455 end_style: Some(self.container_arrow()),
456 ..Default::default()
457 }
458 }
459
460 fn popup_scroll_style(&self) -> ScrollStyle {
462 ScrollStyle {
463 thumb_style: Some(self.popup_border()),
464 track_style: Some(self.popup_border()),
465 min_style: Some(self.popup_border()),
466 begin_style: Some(self.popup_arrow()),
467 end_style: Some(self.popup_arrow()),
468 ..Default::default()
469 }
470 }
471
472 fn dialog_scroll_style(&self) -> ScrollStyle {
474 ScrollStyle {
475 thumb_style: Some(self.dialog_border()),
476 track_style: Some(self.dialog_border()),
477 min_style: Some(self.dialog_border()),
478 begin_style: Some(self.dialog_arrow()),
479 end_style: Some(self.dialog_arrow()),
480 ..Default::default()
481 }
482 }
483
484 fn split_style(&self) -> SplitStyle {
486 SplitStyle {
487 style: self.container_border(),
488 arrow_style: Some(self.container_arrow()),
489 drag_style: Some(self.focus()),
490 ..Default::default()
491 }
492 }
493
494 fn view_style(&self) -> ViewStyle {
496 ViewStyle {
497 scroll: Some(self.scroll_style()),
498 ..Default::default()
499 }
500 }
501
502 fn tabbed_style(&self) -> TabbedStyle {
504 let style = self.high_style(self.p.black[1]);
505 TabbedStyle {
506 style,
507 tab: Some(self.gray(1)),
508 select: Some(self.style(self.p.primary[4])),
509 focus: Some(self.focus()),
510 ..Default::default()
511 }
512 }
513
514 fn statusline_style(&self) -> Vec<Style> {
516 vec![
517 self.status_base(),
518 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
519 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
520 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
521 ]
522 }
523
524 fn statusline_style_ext(&self) -> StatusLineStyle {
526 StatusLineStyle {
527 styles: vec![
528 self.status_base(),
529 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
530 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
531 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
532 ],
533 ..Default::default()
534 }
535 }
536
537 fn file_dialog_style(&self) -> FileDialogStyle {
539 FileDialogStyle {
540 style: self.dialog_base(),
541 list: Some(self.list_style()),
542 roots: Some(ListStyle {
543 style: self.dialog_base(),
544 ..self.list_style()
545 }),
546 text: Some(self.text_style()),
547 button: Some(self.button_style()),
548 block: Some(Block::bordered()),
549 ..Default::default()
550 }
551 }
552
553 fn msg_dialog_style(&self) -> MsgDialogStyle {
555 MsgDialogStyle {
556 style: self.dialog_base(),
557 button: Some(self.button_style()),
558 ..Default::default()
559 }
560 }
561
562 #[allow(deprecated)]
564 fn pager_style(&self) -> PagerStyle {
565 PagerStyle {
566 style: self.container_base(),
567 navigation: Some(self.container_arrow()),
568 block: Some(
569 Block::bordered()
570 .borders(Borders::TOP | Borders::BOTTOM)
571 .border_style(self.container_border()),
572 ),
573 ..Default::default()
574 }
575 }
576
577 fn form_style(&self) -> FormStyle {
578 FormStyle {
579 style: self.container_base(),
580 navigation: Some(self.container_arrow()),
581 block: Some(
582 Block::bordered()
583 .borders(Borders::TOP | Borders::BOTTOM)
584 .border_style(self.container_border()),
585 ),
586 ..Default::default()
587 }
588 }
589
590 fn clipper_style(&self) -> ClipperStyle {
592 ClipperStyle {
593 style: self.container_base(),
594 scroll: Some(self.scroll_style()),
595 ..Default::default()
596 }
597 }
598
599 fn textview_style(&self) -> TextStyle {
600 TextStyle {
601 style: self.container_base(),
602 focus: Some(self.container_base()),
603 select: Some(self.text_select()),
604 scroll: Some(self.scroll_style()),
605 border_style: Some(self.container_border()),
606 ..TextStyle::default()
607 }
608 }
609}