1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
use crate::Scheme;
use rat_widget::button::ButtonStyle;
use rat_widget::input::TextInputStyle;
use rat_widget::list::ListStyle;
use rat_widget::masked_input::MaskedInputStyle;
use rat_widget::menuline::MenuStyle;
use rat_widget::msgdialog::MsgDialogStyle;
use rat_widget::scrolled::ScrolledStyle;
use rat_widget::table::FTableStyle;
use ratatui::prelude::Style;

/// One sample theme which prefers dark colors from the color-scheme
/// and generates styles for widgets.
///
/// The widget set fits for the widgets provided by
/// [rat-widget](https://www.docs.rs/rat-widget), for other needs
/// take it as an idea for your own implementation.
///
#[derive(Debug)]
pub struct DarkTheme {
    s: Scheme,
    name: String,
}

impl DarkTheme {
    pub fn new(name: String, s: Scheme) -> Self {
        Self { s, name }
    }
}

impl DarkTheme {
    /// Some display name.
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Hint at dark.
    pub fn dark_theme(&self) -> bool {
        true
    }

    /// The underlying scheme.
    pub fn scheme(&self) -> &Scheme {
        &self.s
    }

    /// Focus style
    pub fn focus(&self) -> Style {
        let bg = self.s.primary[2];
        Style::default().fg(self.s.text_color(bg)).bg(bg)
    }

    /// Selection style
    pub fn select(&self) -> Style {
        let bg = self.s.secondary[1];
        Style::default().fg(self.s.text_color(bg)).bg(bg)
    }

    /// Text field style.
    pub fn text_input(&self) -> Style {
        Style::default().fg(self.s.black[0]).bg(self.s.gray[3])
    }

    /// Focused text field style.
    pub fn text_focus(&self) -> Style {
        let bg = self.s.primary[0];
        Style::default().fg(self.s.text_color(bg)).bg(bg)
    }

    /// Text selection style.
    pub fn text_select(&self) -> Style {
        let bg = self.s.secondary[0];
        Style::default().fg(self.s.text_color(bg)).bg(bg)
    }

    /// Data display style. Used for lists, tables, ...
    pub fn data(&self) -> Style {
        Style::default().fg(self.s.white[0]).bg(self.s.black[1])
    }

    /// Background for dialogs.
    pub fn dialog_style(&self) -> Style {
        Style::default().fg(self.s.white[2]).bg(self.s.gray[1])
    }

    /// Style for the status line.
    pub fn status_style(&self) -> Style {
        Style::default().fg(self.s.white[0]).bg(self.s.black[2])
    }

    /// Complete TextInputStyle
    pub fn input_style(&self) -> TextInputStyle {
        TextInputStyle {
            style: self.text_input(),
            focus: Some(self.text_focus()),
            select: Some(self.text_select()),
            ..TextInputStyle::default()
        }
    }

    /// Complete MaskedInputStyle
    pub fn inputmask_style(&self) -> MaskedInputStyle {
        MaskedInputStyle {
            style: self.text_input(),
            focus: Some(self.text_focus()),
            select: Some(self.text_select()),
            invalid: Some(Style::default().bg(self.s.red[3])),
            ..Default::default()
        }
    }

    /// Complete MenuStyle
    pub fn menu_style(&self) -> MenuStyle {
        let menu = Style::default().fg(self.s.white[3]).bg(self.s.black[2]);
        MenuStyle {
            style: menu,
            title: Some(Style::default().fg(self.s.black[0]).bg(self.s.yellow[2])),
            select: Some(menu),
            focus: Some(self.focus()),
            ..Default::default()
        }
    }

    /// Complete FTableStyle
    pub fn table_style(&self) -> FTableStyle {
        FTableStyle {
            style: self.data(),
            select_row_style: Some(self.select()),
            show_row_focus: true,
            focus_style: Some(self.focus()),
            ..Default::default()
        }
    }

    /// Complete ListStyle
    pub fn list_style(&self) -> ListStyle {
        ListStyle {
            style: self.data(),
            select_style: self.select(),
            focus_style: self.focus(),
            ..Default::default()
        }
    }

    /// Complete ButtonStyle
    pub fn button_style(&self) -> ButtonStyle {
        ButtonStyle {
            style: Style::default().fg(self.s.white[0]).bg(self.s.primary[0]),
            focus: Some(self.focus()),
            armed: Some(Style::default().fg(self.s.black[0]).bg(self.s.secondary[0])),
            ..Default::default()
        }
    }

    /// Complete ScrolledStyle
    pub fn scrolled_style(&self) -> ScrolledStyle {
        let style = Style::default().fg(self.s.gray[0]).bg(self.s.black[1]);
        let arrow_style = Style::default().fg(self.s.secondary[0]).bg(self.s.black[1]);
        ScrolledStyle {
            thumb_style: Some(style),
            track_style: Some(style),
            begin_style: Some(arrow_style),
            end_style: Some(arrow_style),
            ..Default::default()
        }
    }

    /// Complete StatusLineStyle for a StatusLine with 3 indicator fields.
    /// This is what I need for the
    /// [minimal](https://github.com/thscharler/rat-salsa/blob/master/examples/minimal.rs)
    /// example, which shows timings for Render/Event/Action.
    pub fn statusline_style(&self) -> Vec<Style> {
        let s = &self.s;
        vec![
            self.status_style(),
            Style::default().fg(s.text_color(s.white[0])).bg(s.blue[3]),
            Style::default().fg(s.text_color(s.white[0])).bg(s.blue[2]),
            Style::default().fg(s.text_color(s.white[0])).bg(s.blue[1]),
        ]
    }

    /// Complete MsgDialogStyle.
    pub fn msg_dialog_style(&self) -> MsgDialogStyle {
        MsgDialogStyle {
            style: self.status_style(),
            button: self.button_style(),
            ..Default::default()
        }
    }
}