rat_theme3/
dark_theme.rs

1//!
2//! Implements a dark theme.
3//!
4
5use 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/// One sample theme which prefers dark colors from the color-palette
38/// and generates styles for widgets.
39///
40/// The widget set fits for the widgets provided by
41/// [rat-widget](https://www.docs.rs/rat-widget), for other needs
42/// take it as an idea for your own implementation.
43///
44#[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    /// Create a style from a background color
59    fn style(&self, bg: Color) -> Style {
60        self.p.style(bg, Contrast::Normal)
61    }
62
63    /// Create a style from a background color
64    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    /// Some display name.
79    fn name(&self) -> &str {
80        &self.name
81    }
82
83    /// The underlying palette.
84    fn palette(&self) -> &Palette {
85        &self.p
86    }
87
88    /// Create a style from the given white shade.
89    /// n is `0..8`
90    fn white(&self, n: usize) -> Style {
91        self.p.white(n, Contrast::Normal)
92    }
93
94    /// Create a style from the given black shade.
95    /// n is `0..8`
96    fn black(&self, n: usize) -> Style {
97        self.p.black(n, Contrast::Normal)
98    }
99
100    /// Create a style from the given gray shade.
101    /// n is `0..8`
102    fn gray(&self, n: usize) -> Style {
103        self.p.gray(n, Contrast::Normal)
104    }
105
106    /// Create a style from the given red shade.
107    /// n is `0..8`
108    fn red(&self, n: usize) -> Style {
109        self.p.red(n, Contrast::Normal)
110    }
111
112    /// Create a style from the given orange shade.
113    /// n is `0..8`
114    fn orange(&self, n: usize) -> Style {
115        self.p.orange(n, Contrast::Normal)
116    }
117
118    /// Create a style from the given yellow shade.
119    /// n is `0..8`
120    fn yellow(&self, n: usize) -> Style {
121        self.p.yellow(n, Contrast::Normal)
122    }
123
124    /// Create a style from the given limegreen shade.
125    /// n is `0..8`
126    fn limegreen(&self, n: usize) -> Style {
127        self.p.limegreen(n, Contrast::Normal)
128    }
129
130    /// Create a style from the given green shade.
131    /// n is `0..8`
132    fn green(&self, n: usize) -> Style {
133        self.p.green(n, Contrast::Normal)
134    }
135
136    /// Create a style from the given bluegreen shade.
137    /// n is `0..8`
138    fn bluegreen(&self, n: usize) -> Style {
139        self.p.bluegreen(n, Contrast::Normal)
140    }
141
142    /// Create a style from the given cyan shade.
143    /// n is `0..8`
144    fn cyan(&self, n: usize) -> Style {
145        self.p.cyan(n, Contrast::Normal)
146    }
147
148    /// Create a style from the given blue shade.
149    /// n is `0..8`
150    fn blue(&self, n: usize) -> Style {
151        self.p.blue(n, Contrast::Normal)
152    }
153
154    /// Create a style from the given deepblue shade.
155    /// n is `0..8`
156    fn deepblue(&self, n: usize) -> Style {
157        self.p.deepblue(n, Contrast::Normal)
158    }
159
160    /// Create a style from the given purple shade.
161    /// n is `0..8`
162    fn purple(&self, n: usize) -> Style {
163        self.p.purple(n, Contrast::Normal)
164    }
165
166    /// Create a style from the given magenta shade.
167    /// n is `0..8`
168    fn magenta(&self, n: usize) -> Style {
169        self.p.magenta(n, Contrast::Normal)
170    }
171
172    /// Create a style from the given redpink shade.
173    /// n is `0..8`
174    fn redpink(&self, n: usize) -> Style {
175        self.p.redpink(n, Contrast::Normal)
176    }
177
178    /// Create a style from the given primary shade.
179    /// n is `0..8`
180    fn primary(&self, n: usize) -> Style {
181        self.p.primary(n, Contrast::Normal)
182    }
183
184    /// Create a style from the given secondary shade.
185    /// n is `0..8`
186    fn secondary(&self, n: usize) -> Style {
187        self.p.secondary(n, Contrast::Normal)
188    }
189
190    /// Focus style
191    fn focus(&self) -> Style {
192        self.high_style(self.p.primary[2])
193    }
194
195    /// Selection style
196    fn select(&self) -> Style {
197        self.high_style(self.p.secondary[1])
198    }
199
200    /// Text field style.
201    fn text_input(&self) -> Style {
202        self.high_style(self.p.gray[3])
203    }
204
205    /// Focused text field style.
206    fn text_focus(&self) -> Style {
207        self.high_style(self.p.primary[1])
208    }
209
210    /// Text selection style.
211    fn text_select(&self) -> Style {
212        self.high_style(self.p.secondary[1])
213    }
214
215    /// Container base
216    fn container_base(&self) -> Style {
217        self.style(self.p.black[1])
218    }
219
220    /// Container border
221    fn container_border(&self) -> Style {
222        self.container_base().fg(self.p.gray[0])
223    }
224
225    /// Container arrows
226    fn container_arrow(&self) -> Style {
227        self.container_base().fg(self.p.gray[0])
228    }
229
230    /// Background for popups.
231    fn popup_base(&self) -> Style {
232        self.style(self.p.white[0])
233    }
234
235    /// Dialog arrows
236    fn popup_border(&self) -> Style {
237        self.popup_base().fg(self.p.gray[0])
238    }
239
240    /// Dialog arrows
241    fn popup_arrow(&self) -> Style {
242        self.popup_base().fg(self.p.gray[0])
243    }
244
245    /// Background for dialogs.
246    fn dialog_base(&self) -> Style {
247        self.style(self.p.gray[1])
248    }
249
250    /// Dialog arrows
251    fn dialog_border(&self) -> Style {
252        self.dialog_base().fg(self.p.white[0])
253    }
254
255    /// Dialog arrows
256    fn dialog_arrow(&self) -> Style {
257        self.dialog_base().fg(self.p.white[0])
258    }
259
260    /// Style for the status line.
261    fn status_base(&self) -> Style {
262        self.style(self.p.black[2])
263    }
264
265    /// Base style for buttons.
266    fn button_base(&self) -> Style {
267        self.style(self.p.gray[2])
268    }
269
270    /// Armed style for buttons.
271    fn button_armed(&self) -> Style {
272        self.style(self.p.secondary[0])
273    }
274
275    /// Complete MonthStyle.
276    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    /// Style for shadows.
290    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    /// Style for LineNumbers.
299    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    /// Complete TextAreaStyle
308    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    /// Complete TextInputStyle
320    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    /// Text-label style.
331    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    /// Complete CheckboxStyle
371    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    /// Slider Style
380    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    /// Complete MenuStyle
392    #[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    /// Complete ButtonStyle
411    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    /// Complete TableStyle
423    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    /// Complete ListStyle
438    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    /// Scroll style
449    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    /// Popup scroll style
461    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    /// Dialog scroll style
473    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    /// Split style
485    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    /// View style
495    fn view_style(&self) -> ViewStyle {
496        ViewStyle {
497            scroll: Some(self.scroll_style()),
498            ..Default::default()
499        }
500    }
501
502    /// Tabbed style
503    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    /// Complete StatusLineStyle for a StatusLine with 3 indicator fields.
515    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    /// StatusLineStyle for a StatusLine with 3 indicator fields.
525    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    /// FileDialog style.
538    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    /// Complete MsgDialogStyle.
554    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    /// Pager style.
563    #[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    /// Clipper style.
591    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.focus()),
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}