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_core::layout::Alignment;
29use ratatui_core::style::Color;
30use ratatui_core::style::Style;
31use ratatui_widgets::block::Block;
32use ratatui_widgets::borders::Borders;
33#[cfg(feature = "serde")]
34use serde::de::{Error, MapAccess, SeqAccess, Visitor};
35#[cfg(feature = "serde")]
36use serde::ser::SerializeStruct;
37#[cfg(feature = "serde")]
38use serde::{Deserialize, Deserializer, Serialize, Serializer};
39#[cfg(feature = "serde")]
40use std::fmt::Formatter;
41use std::time::Duration;
42
43#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct DarkTheme {
52 name: Box<str>,
53 p: Palette,
54}
55
56#[cfg(feature = "serde")]
57impl Serialize for DarkTheme {
58 fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
59 where
60 S: Serializer,
61 {
62 let mut theme = ser.serialize_struct("DarkTheme", 2)?;
63 theme.serialize_field("name", &self.name)?;
64 theme.serialize_field("p", &self.p)?;
65 theme.end()
66 }
67}
68
69#[cfg(feature = "serde")]
70struct DarkThemeVisitor;
71
72#[cfg(feature = "serde")]
73impl<'de> Visitor<'de> for DarkThemeVisitor {
74 type Value = DarkTheme;
75
76 fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
77 write!(f, "struct DarkTheme")
78 }
79
80 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
81 where
82 A: SeqAccess<'de>,
83 {
84 let name = seq
85 .next_element::<Box<str>>()?
86 .ok_or(A::Error::invalid_length(0, &"DarkTheme.name"))?;
87 let p = seq
88 .next_element::<Palette>()?
89 .ok_or(A::Error::invalid_length(0, &"DarkTheme.p"))?;
90 Ok(DarkTheme { name, p })
91 }
92
93 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
94 where
95 A: MapAccess<'de>,
96 {
97 let mut name = None;
98 let mut p = None;
99
100 while let Some(key) = map.next_key::<&str>()? {
101 match key {
102 "name" => name = Some(map.next_value::<Box<str>>()?),
103 "p" => p = Some(map.next_value::<Palette>()?),
104 _ => {}
105 }
106 }
107 let name = name.ok_or(A::Error::missing_field("name"))?;
108 let p = p.ok_or(A::Error::missing_field("p"))?;
109
110 Ok(DarkTheme { name, p })
111 }
112}
113
114#[cfg(feature = "serde")]
115impl<'de> Deserialize<'de> for DarkTheme {
116 fn deserialize<D>(des: D) -> Result<Self, D::Error>
117 where
118 D: Deserializer<'de>,
119 {
120 const FIELDS: &'static [&'static str] = &["name", "p"];
121 des.deserialize_struct("DarkTheme", FIELDS, DarkThemeVisitor)
122 }
123}
124
125impl DarkTheme {
126 pub fn new(name: &str, s: Palette) -> Self {
127 Self {
128 name: Box::from(name),
129 p: s,
130 }
131 }
132
133 fn style(&self, bg: Color) -> Style {
135 self.p.style(bg, Contrast::Normal)
136 }
137
138 fn high_style(&self, bg: Color) -> Style {
140 self.p.style(bg, Contrast::High)
141 }
142
143 fn table_header(&self) -> Style {
144 self.style(self.p.blue[2])
145 }
146
147 fn table_footer(&self) -> Style {
148 self.style(self.p.blue[2])
149 }
150}
151
152impl SalsaTheme for DarkTheme {
153 fn name(&self) -> &str {
155 &self.name
156 }
157
158 fn palette(&self) -> &Palette {
160 &self.p
161 }
162
163 fn white(&self, n: usize) -> Style {
166 self.p.white(n, Contrast::Normal)
167 }
168
169 fn black(&self, n: usize) -> Style {
172 self.p.black(n, Contrast::Normal)
173 }
174
175 fn gray(&self, n: usize) -> Style {
178 self.p.gray(n, Contrast::Normal)
179 }
180
181 fn red(&self, n: usize) -> Style {
184 self.p.red(n, Contrast::Normal)
185 }
186
187 fn orange(&self, n: usize) -> Style {
190 self.p.orange(n, Contrast::Normal)
191 }
192
193 fn yellow(&self, n: usize) -> Style {
196 self.p.yellow(n, Contrast::Normal)
197 }
198
199 fn limegreen(&self, n: usize) -> Style {
202 self.p.limegreen(n, Contrast::Normal)
203 }
204
205 fn green(&self, n: usize) -> Style {
208 self.p.green(n, Contrast::Normal)
209 }
210
211 fn bluegreen(&self, n: usize) -> Style {
214 self.p.bluegreen(n, Contrast::Normal)
215 }
216
217 fn cyan(&self, n: usize) -> Style {
220 self.p.cyan(n, Contrast::Normal)
221 }
222
223 fn blue(&self, n: usize) -> Style {
226 self.p.blue(n, Contrast::Normal)
227 }
228
229 fn deepblue(&self, n: usize) -> Style {
232 self.p.deepblue(n, Contrast::Normal)
233 }
234
235 fn purple(&self, n: usize) -> Style {
238 self.p.purple(n, Contrast::Normal)
239 }
240
241 fn magenta(&self, n: usize) -> Style {
244 self.p.magenta(n, Contrast::Normal)
245 }
246
247 fn redpink(&self, n: usize) -> Style {
250 self.p.redpink(n, Contrast::Normal)
251 }
252
253 fn primary(&self, n: usize) -> Style {
256 self.p.primary(n, Contrast::Normal)
257 }
258
259 fn secondary(&self, n: usize) -> Style {
262 self.p.secondary(n, Contrast::Normal)
263 }
264
265 fn focus(&self) -> Style {
267 self.high_style(self.p.primary[2])
268 }
269
270 fn select(&self) -> Style {
272 self.high_style(self.p.secondary[1])
273 }
274
275 fn text_input(&self) -> Style {
277 self.high_style(self.p.gray[3])
278 }
279
280 fn text_focus(&self) -> Style {
282 self.high_style(self.p.primary[1])
283 }
284
285 fn text_select(&self) -> Style {
287 self.high_style(self.p.secondary[1])
288 }
289
290 fn container_base(&self) -> Style {
292 self.style(self.p.black[1])
293 }
294
295 fn container_border(&self) -> Style {
297 self.container_base().fg(self.p.gray[0])
298 }
299
300 fn container_arrow(&self) -> Style {
302 self.container_base().fg(self.p.gray[0])
303 }
304
305 fn popup_base(&self) -> Style {
307 self.style(self.p.white[0])
308 }
309
310 fn popup_border(&self) -> Style {
312 self.popup_base().fg(self.p.gray[0])
313 }
314
315 fn popup_arrow(&self) -> Style {
317 self.popup_base().fg(self.p.gray[0])
318 }
319
320 fn dialog_base(&self) -> Style {
322 self.style(self.p.gray[1])
323 }
324
325 fn dialog_border(&self) -> Style {
327 self.dialog_base().fg(self.p.white[0])
328 }
329
330 fn dialog_arrow(&self) -> Style {
332 self.dialog_base().fg(self.p.white[0])
333 }
334
335 fn status_base(&self) -> Style {
337 self.style(self.p.black[2])
338 }
339
340 fn button_base(&self) -> Style {
342 self.style(self.p.gray[2])
343 }
344
345 fn button_armed(&self) -> Style {
347 self.style(self.p.secondary[0])
348 }
349
350 fn month_style(&self) -> CalendarStyle {
352 CalendarStyle {
353 style: self.style(self.p.black[2]),
354 title: None,
355 weeknum: Some(Style::new().fg(self.p.limegreen[2])),
356 weekday: Some(Style::new().fg(self.p.limegreen[2])),
357 day: None,
358 select: Some(self.select()),
359 focus: Some(self.focus()),
360 ..CalendarStyle::default()
361 }
362 }
363
364 fn shadow_style(&self) -> ShadowStyle {
366 ShadowStyle {
367 style: Style::new().bg(self.p.black[0]),
368 dir: ShadowDirection::BottomRight,
369 ..ShadowStyle::default()
370 }
371 }
372
373 fn line_nr_style(&self) -> LineNumberStyle {
375 LineNumberStyle {
376 style: self.container_base().fg(self.p.gray[1]),
377 cursor: Some(self.text_select()),
378 ..LineNumberStyle::default()
379 }
380 }
381
382 fn textarea_style(&self) -> TextStyle {
384 TextStyle {
385 style: self.text_input(),
386 focus: Some(self.focus()),
387 select: Some(self.text_select()),
388 scroll: Some(self.scroll_style()),
389 border_style: Some(self.container_border()),
390 ..TextStyle::default()
391 }
392 }
393
394 fn text_style(&self) -> TextStyle {
396 TextStyle {
397 style: self.text_input(),
398 focus: Some(self.text_focus()),
399 select: Some(self.text_select()),
400 invalid: Some(Style::default().bg(self.p.red[3])),
401 ..TextStyle::default()
402 }
403 }
404
405 fn label_style(&self) -> Style {
407 self.container_base()
408 }
409
410 fn paragraph_style(&self) -> ParagraphStyle {
411 ParagraphStyle {
412 style: self.container_base(),
413 focus: Some(self.focus()),
414 scroll: Some(self.scroll_style()),
415 ..Default::default()
416 }
417 }
418
419 fn choice_style(&self) -> ChoiceStyle {
420 ChoiceStyle {
421 style: self.text_input(),
422 select: Some(self.text_select()),
423 focus: Some(self.text_focus()),
424 popup_style: Some(self.popup_base()),
425 popup_border: Some(self.popup_border()),
426 popup_scroll: Some(self.popup_scroll_style()),
427 popup_block: Some(
428 Block::bordered()
429 .borders(Borders::LEFT)
430 .border_style(self.popup_border()),
431 ),
432 ..Default::default()
433 }
434 }
435
436 fn radio_style(&self) -> RadioStyle {
437 RadioStyle {
438 layout: Some(RadioLayout::Stacked),
439 style: self.text_input(),
440 focus: Some(self.text_focus()),
441 ..Default::default()
442 }
443 }
444
445 fn checkbox_style(&self) -> CheckboxStyle {
447 CheckboxStyle {
448 style: self.text_input(),
449 focus: Some(self.text_focus()),
450 ..Default::default()
451 }
452 }
453
454 fn slider_style(&self) -> SliderStyle {
456 SliderStyle {
457 style: self.text_input(),
458 bounds: Some(self.gray(2)),
459 knob: Some(self.select()),
460 focus: Some(self.focus()),
461 text_align: Some(Alignment::Center),
462 ..Default::default()
463 }
464 }
465
466 fn menu_style(&self) -> MenuStyle {
468 MenuStyle {
469 style: self.status_base(),
470 title: Some(self.style(self.p.yellow[2])),
471 focus: Some(self.focus()),
472 right: Some(Style::default().fg(self.p.bluegreen[0])),
473 disabled: Some(Style::default().fg(self.p.gray[0])),
474 highlight: Some(Style::default().underlined()),
475 popup_style: Some(self.status_base()),
476 popup_block: Some(Block::bordered()),
477 popup_focus: Some(self.focus()),
478 popup_right: Some(Style::default().fg(self.p.bluegreen[0])),
479 popup_disabled: Some(Style::default().fg(self.p.gray[0])),
480 popup_highlight: Some(Style::default().underlined()),
481 popup: Default::default(),
482 ..Default::default()
483 }
484 }
485
486 fn button_style(&self) -> ButtonStyle {
488 ButtonStyle {
489 style: self.button_base(),
490 focus: Some(self.focus()),
491 armed: Some(self.select()),
492 hover: Some(self.select()),
493 armed_delay: Some(Duration::from_millis(50)),
494 ..Default::default()
495 }
496 }
497
498 fn table_style(&self) -> TableStyle {
500 TableStyle {
501 style: self.container_base(),
502 select_row: Some(self.select()),
503 show_row_focus: true,
504 focus_style: Some(self.focus()),
505 border_style: Some(self.container_border()),
506 scroll: Some(self.scroll_style()),
507 header: Some(self.table_header()),
508 footer: Some(self.table_footer()),
509 ..Default::default()
510 }
511 }
512
513 fn list_style(&self) -> ListStyle {
515 ListStyle {
516 style: self.container_base(),
517 select: Some(self.select()),
518 focus: Some(self.focus()),
519 scroll: Some(self.scroll_style()),
520 ..Default::default()
521 }
522 }
523
524 fn scroll_style(&self) -> ScrollStyle {
526 ScrollStyle {
527 thumb_style: Some(self.container_border()),
528 track_style: Some(self.container_border()),
529 min_style: Some(self.container_border()),
530 begin_style: Some(self.container_arrow()),
531 end_style: Some(self.container_arrow()),
532 ..Default::default()
533 }
534 }
535
536 fn popup_scroll_style(&self) -> ScrollStyle {
538 ScrollStyle {
539 thumb_style: Some(self.popup_border()),
540 track_style: Some(self.popup_border()),
541 min_style: Some(self.popup_border()),
542 begin_style: Some(self.popup_arrow()),
543 end_style: Some(self.popup_arrow()),
544 ..Default::default()
545 }
546 }
547
548 fn dialog_scroll_style(&self) -> ScrollStyle {
550 ScrollStyle {
551 thumb_style: Some(self.dialog_border()),
552 track_style: Some(self.dialog_border()),
553 min_style: Some(self.dialog_border()),
554 begin_style: Some(self.dialog_arrow()),
555 end_style: Some(self.dialog_arrow()),
556 ..Default::default()
557 }
558 }
559
560 fn split_style(&self) -> SplitStyle {
562 SplitStyle {
563 style: self.container_border(),
564 arrow_style: Some(self.container_arrow()),
565 drag_style: Some(self.focus()),
566 ..Default::default()
567 }
568 }
569
570 fn view_style(&self) -> ViewStyle {
572 ViewStyle {
573 scroll: Some(self.scroll_style()),
574 ..Default::default()
575 }
576 }
577
578 fn tabbed_style(&self) -> TabbedStyle {
580 let style = self.high_style(self.p.black[1]);
581 TabbedStyle {
582 style,
583 tab: Some(self.gray(1)),
584 select: Some(self.style(self.p.primary[4])),
585 focus: Some(self.focus()),
586 ..Default::default()
587 }
588 }
589
590 fn statusline_style(&self) -> Vec<Style> {
592 vec![
593 self.status_base(),
594 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
595 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
596 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
597 ]
598 }
599
600 fn statusline_style_ext(&self) -> StatusLineStyle {
602 StatusLineStyle {
603 styles: vec![
604 self.status_base(),
605 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
606 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
607 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
608 ],
609 ..Default::default()
610 }
611 }
612
613 fn file_dialog_style(&self) -> FileDialogStyle {
615 FileDialogStyle {
616 style: self.dialog_base(),
617 list: Some(self.list_style()),
618 roots: Some(ListStyle {
619 style: self.dialog_base(),
620 ..self.list_style()
621 }),
622 text: Some(self.text_style()),
623 button: Some(self.button_style()),
624 block: Some(Block::bordered()),
625 ..Default::default()
626 }
627 }
628
629 fn msg_dialog_style(&self) -> MsgDialogStyle {
631 MsgDialogStyle {
632 style: self.dialog_base(),
633 button: Some(self.button_style()),
634 ..Default::default()
635 }
636 }
637
638 fn form_style(&self) -> FormStyle {
639 FormStyle {
640 style: self.container_base(),
641 navigation: Some(self.container_arrow()),
642 block: Some(
643 Block::bordered()
644 .borders(Borders::TOP | Borders::BOTTOM)
645 .border_style(self.container_border()),
646 ),
647 ..Default::default()
648 }
649 }
650
651 fn clipper_style(&self) -> ClipperStyle {
653 ClipperStyle {
654 style: self.container_base(),
655 scroll: Some(self.scroll_style()),
656 ..Default::default()
657 }
658 }
659
660 fn textview_style(&self) -> TextStyle {
661 TextStyle {
662 style: self.container_base(),
663 focus: Some(self.container_base()),
664 select: Some(self.text_select()),
665 scroll: Some(self.scroll_style()),
666 border_style: Some(self.container_border()),
667 ..TextStyle::default()
668 }
669 }
670}