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 block: Some(Block::bordered()),
399 popup: Default::default(),
400 ..Default::default()
401 }
402 }
403
404 fn button_style(&self) -> ButtonStyle {
406 ButtonStyle {
407 style: self.button_base(),
408 focus: Some(self.focus()),
409 armed: Some(self.select()),
410 hover: Some(self.select()),
411 armed_delay: Some(Duration::from_millis(50)),
412 ..Default::default()
413 }
414 }
415
416 fn table_style(&self) -> TableStyle {
418 TableStyle {
419 style: self.container_base(),
420 select_row: Some(self.select()),
421 show_row_focus: true,
422 focus_style: Some(self.focus()),
423 border_style: Some(self.container_border()),
424 scroll: Some(self.scroll_style()),
425 header: Some(self.table_header()),
426 footer: Some(self.table_footer()),
427 ..Default::default()
428 }
429 }
430
431 fn list_style(&self) -> ListStyle {
433 ListStyle {
434 style: self.container_base(),
435 select: Some(self.select()),
436 focus: Some(self.focus()),
437 scroll: Some(self.scroll_style()),
438 ..Default::default()
439 }
440 }
441
442 fn scroll_style(&self) -> ScrollStyle {
444 ScrollStyle {
445 thumb_style: Some(self.container_border()),
446 track_style: Some(self.container_border()),
447 min_style: Some(self.container_border()),
448 begin_style: Some(self.container_arrow()),
449 end_style: Some(self.container_arrow()),
450 ..Default::default()
451 }
452 }
453
454 fn popup_scroll_style(&self) -> ScrollStyle {
456 ScrollStyle {
457 thumb_style: Some(self.popup_border()),
458 track_style: Some(self.popup_border()),
459 min_style: Some(self.popup_border()),
460 begin_style: Some(self.popup_arrow()),
461 end_style: Some(self.popup_arrow()),
462 ..Default::default()
463 }
464 }
465
466 fn dialog_scroll_style(&self) -> ScrollStyle {
468 ScrollStyle {
469 thumb_style: Some(self.dialog_border()),
470 track_style: Some(self.dialog_border()),
471 min_style: Some(self.dialog_border()),
472 begin_style: Some(self.dialog_arrow()),
473 end_style: Some(self.dialog_arrow()),
474 ..Default::default()
475 }
476 }
477
478 fn split_style(&self) -> SplitStyle {
480 SplitStyle {
481 style: self.container_border(),
482 arrow_style: Some(self.container_arrow()),
483 drag_style: Some(self.focus()),
484 ..Default::default()
485 }
486 }
487
488 fn view_style(&self) -> ViewStyle {
490 ViewStyle {
491 scroll: Some(self.scroll_style()),
492 ..Default::default()
493 }
494 }
495
496 fn tabbed_style(&self) -> TabbedStyle {
498 let style = self.high_style(self.p.black[1]);
499 TabbedStyle {
500 style,
501 tab: Some(self.gray(1)),
502 select: Some(self.style(self.p.primary[4])),
503 focus: Some(self.focus()),
504 ..Default::default()
505 }
506 }
507
508 fn statusline_style(&self) -> Vec<Style> {
510 vec![
511 self.status_base(),
512 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
513 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
514 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
515 ]
516 }
517
518 fn statusline_style_ext(&self) -> StatusLineStyle {
520 StatusLineStyle {
521 styles: vec![
522 self.status_base(),
523 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
524 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
525 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
526 ],
527 ..Default::default()
528 }
529 }
530
531 fn file_dialog_style(&self) -> FileDialogStyle {
533 FileDialogStyle {
534 style: self.dialog_base(),
535 list: Some(self.list_style()),
536 roots: Some(ListStyle {
537 style: self.dialog_base(),
538 ..self.list_style()
539 }),
540 text: Some(self.text_style()),
541 button: Some(self.button_style()),
542 block: Some(Block::bordered()),
543 ..Default::default()
544 }
545 }
546
547 fn msg_dialog_style(&self) -> MsgDialogStyle {
549 MsgDialogStyle {
550 style: self.dialog_base(),
551 button: Some(self.button_style()),
552 ..Default::default()
553 }
554 }
555
556 fn form_style(&self) -> FormStyle {
557 FormStyle {
558 style: self.container_base(),
559 navigation: Some(self.container_arrow()),
560 block: Some(
561 Block::bordered()
562 .borders(Borders::TOP | Borders::BOTTOM)
563 .border_style(self.container_border()),
564 ),
565 ..Default::default()
566 }
567 }
568
569 fn clipper_style(&self) -> ClipperStyle {
571 ClipperStyle {
572 style: self.container_base(),
573 scroll: Some(self.scroll_style()),
574 ..Default::default()
575 }
576 }
577
578 fn textview_style(&self) -> TextStyle {
579 TextStyle {
580 style: self.container_base(),
581 focus: Some(self.container_base()),
582 select: Some(self.text_select()),
583 scroll: Some(self.scroll_style()),
584 border_style: Some(self.container_border()),
585 ..TextStyle::default()
586 }
587 }
588}