rat_theme4/
shell_theme.rs

1use crate::palette::Palette;
2use crate::{Category, SalsaTheme};
3use crate::{StyleName, WidgetStyle};
4use rat_widget::button::ButtonStyle;
5use rat_widget::calendar::CalendarStyle;
6use rat_widget::checkbox::CheckboxStyle;
7use rat_widget::choice::ChoiceStyle;
8use rat_widget::clipper::ClipperStyle;
9use rat_widget::color_input::ColorInputStyle;
10use rat_widget::combobox::ComboboxStyle;
11use rat_widget::dialog_frame::DialogFrameStyle;
12use rat_widget::file_dialog::FileDialogStyle;
13use rat_widget::form::FormStyle;
14use rat_widget::line_number::LineNumberStyle;
15use rat_widget::list::ListStyle;
16use rat_widget::menu::MenuStyle;
17use rat_widget::msgdialog::MsgDialogStyle;
18use rat_widget::paragraph::ParagraphStyle;
19use rat_widget::radio::{RadioLayout, RadioStyle};
20use rat_widget::scrolled::ScrollStyle;
21use rat_widget::shadow::{ShadowDirection, ShadowStyle};
22use rat_widget::slider::SliderStyle;
23use rat_widget::splitter::SplitStyle;
24use rat_widget::statusline::StatusLineStyle;
25use rat_widget::tabbed::TabbedStyle;
26use rat_widget::table::TableStyle;
27use rat_widget::text::TextStyle;
28use rat_widget::view::ViewStyle;
29use ratatui::layout::Alignment;
30use ratatui::style::{Style, Stylize};
31use ratatui::widgets::{Block, Borders};
32use std::time::Duration;
33
34/// A 'shell'-theme.
35///
36/// It uses almost no background colors and lets your shell
37/// bleed through.
38pub fn shell_theme(name: &str, p: Palette) -> SalsaTheme {
39    let mut th = SalsaTheme::new(name, Category::Shell, p);
40
41    th.define(Style::INPUT, th.p.gray(0));
42    th.define(Style::FOCUS, th.p.high_contrast(th.p.primary[2]));
43    th.define(Style::SELECT, th.p.high_contrast(th.p.secondary[1]));
44    th.define(Style::TEXT_FOCUS, th.p.high_contrast(th.p.gray[3]));
45    th.define(Style::TEXT_SELECT, th.p.high_contrast(th.p.secondary[0]));
46    th.define(Style::TEXT_SELECT, th.p.gray(2));
47    th.define(Style::BUTTON_BASE, th.p.gray(2));
48
49    th.define(Style::CONTAINER_BASE, Style::default());
50    th.define(Style::CONTAINER_BORDER, Style::default());
51    th.define(Style::CONTAINER_ARROWS, Style::default());
52
53    th.define(Style::POPUP_BASE, th.p.bg_gray(0));
54    th.define(Style::POPUP_BORDER, th.p.bg_gray(0));
55    th.define(Style::POPUP_ARROW, th.p.bg_gray(0));
56
57    th.define(Style::DIALOG_BASE, th.p.bg_gray(1));
58    th.define(Style::DIALOG_BORDER, th.p.bg_gray(1));
59    th.define(Style::DIALOG_ARROW, th.p.bg_gray(1));
60
61    th.define(Style::STATUS_BASE, Style::default());
62
63    th.define_fn(WidgetStyle::BUTTON, button);
64    th.define_fn(WidgetStyle::CHECKBOX, checkbox);
65    th.define_fn(WidgetStyle::CHOICE, choice);
66    th.define_fn(WidgetStyle::CLIPPER, clipper);
67    th.define_fn(WidgetStyle::COMBOBOX, combobox);
68    th.define_fn(WidgetStyle::COLOR_INPUT, color_input);
69    th.define_fn(WidgetStyle::DIALOG_FRAME, dialog_frame);
70    th.define_fn(WidgetStyle::FILE_DIALOG, file_dialog);
71    th.define_fn(WidgetStyle::FORM, form);
72    th.define_fn(WidgetStyle::LINE_NR, line_nr);
73    th.define_fn(WidgetStyle::LIST, list);
74    th.define_fn(WidgetStyle::MENU, menu);
75    th.define_fn(WidgetStyle::MONTH, month);
76    th.define_fn(WidgetStyle::MSG_DIALOG, msg_dialog);
77    th.define_fn(WidgetStyle::PARAGRAPH, paragraph);
78    th.define_fn(WidgetStyle::RADIO, radio);
79    th.define_fn(WidgetStyle::SCROLL, scroll);
80    th.define_fn(WidgetStyle::SCROLL_DIALOG, dialog_scroll);
81    th.define_fn(WidgetStyle::SCROLL_POPUP, popup_scroll);
82    th.define_fn(WidgetStyle::SHADOW, shadow);
83    th.define_fn(WidgetStyle::SLIDER, slider);
84    th.define_fn(WidgetStyle::SPLIT, split);
85    th.define_fn(WidgetStyle::STATUSLINE, statusline);
86    th.define_fn(WidgetStyle::TABBED, tabbed);
87    th.define_fn(WidgetStyle::TABLE, table);
88    th.define_fn(WidgetStyle::TEXT, text);
89    th.define_fn(WidgetStyle::TEXTAREA, textarea);
90    th.define_fn(WidgetStyle::TEXTVIEW, textview);
91    th.define_fn(WidgetStyle::VIEW, view);
92
93    th
94}
95
96fn button(th: &SalsaTheme) -> ButtonStyle {
97    ButtonStyle {
98        style: th.style(Style::BUTTON_BASE),
99        focus: Some(th.style(Style::FOCUS)),
100        armed: Some(th.style(Style::SELECT)),
101        hover: Some(th.style(Style::SELECT)),
102        armed_delay: Some(Duration::from_millis(50)),
103        ..Default::default()
104    }
105}
106
107fn checkbox(th: &SalsaTheme) -> CheckboxStyle {
108    CheckboxStyle {
109        style: th.style(Style::INPUT),
110        focus: Some(th.style(Style::TEXT_FOCUS)),
111        ..Default::default()
112    }
113}
114
115fn choice(th: &SalsaTheme) -> ChoiceStyle {
116    ChoiceStyle {
117        style: th.style(Style::INPUT),
118        select: Some(th.style(Style::TEXT_SELECT)),
119        focus: Some(th.style(Style::TEXT_FOCUS)),
120        popup_style: Some(th.style(Style::POPUP_BASE)),
121        popup_border: Some(th.style(Style::POPUP_BORDER)),
122        popup_scroll: Some(popup_scroll(th)),
123        popup_block: Some(
124            Block::bordered()
125                .borders(Borders::LEFT)
126                .border_style(th.style::<Style>(Style::POPUP_BORDER)),
127        ),
128        ..Default::default()
129    }
130}
131
132fn clipper(th: &SalsaTheme) -> ClipperStyle {
133    ClipperStyle {
134        style: th.style(Style::CONTAINER_BASE),
135        scroll: Some(scroll(th)),
136        ..Default::default()
137    }
138}
139
140fn combobox(th: &SalsaTheme) -> ComboboxStyle {
141    ComboboxStyle {
142        choice: choice(th),
143        text: text(th),
144        ..Default::default()
145    }
146}
147
148fn dialog_frame(th: &SalsaTheme) -> DialogFrameStyle {
149    DialogFrameStyle {
150        style: th.style(Style::DIALOG_BASE),
151        block: Some(Block::bordered().style(th.style::<Style>(Style::DIALOG_BORDER))),
152        button_style: Some(button(th)),
153        ..DialogFrameStyle::default()
154    }
155}
156
157fn file_dialog(th: &SalsaTheme) -> FileDialogStyle {
158    FileDialogStyle {
159        style: th.style(Style::DIALOG_BASE),
160        list: Some(list(th)),
161        roots: Some(ListStyle {
162            style: th.style(Style::DIALOG_BASE),
163            ..list(th)
164        }),
165        text: Some(text(th)),
166        button: Some(button(th)),
167        block: Some(Block::bordered()),
168        ..Default::default()
169    }
170}
171
172fn form(th: &SalsaTheme) -> FormStyle {
173    FormStyle {
174        style: th.style(Style::CONTAINER_BASE),
175        navigation: Some(th.style(Style::CONTAINER_ARROWS)),
176        block: Some(
177            Block::bordered()
178                .borders(Borders::TOP | Borders::BOTTOM)
179                .border_style(th.style::<Style>(Style::CONTAINER_BORDER)),
180        ),
181        ..Default::default()
182    }
183}
184
185fn line_nr(th: &SalsaTheme) -> LineNumberStyle {
186    LineNumberStyle {
187        style: th.style(Style::CONTAINER_BASE),
188        cursor: Some(th.style(Style::TEXT_SELECT)),
189        ..LineNumberStyle::default()
190    }
191}
192
193fn list(th: &SalsaTheme) -> ListStyle {
194    ListStyle {
195        style: th.style(Style::CONTAINER_BASE),
196        select: Some(th.style(Style::SELECT)),
197        focus: Some(th.style(Style::FOCUS)),
198        scroll: Some(scroll(th)),
199        ..Default::default()
200    }
201}
202
203fn menu(th: &SalsaTheme) -> MenuStyle {
204    MenuStyle {
205        style: th.style(Style::STATUS_BASE),
206        title: Some(th.p.bg_yellow(2)),
207        focus: Some(th.style(Style::FOCUS)),
208        right: Some(th.p.fg_green(3)),
209        disabled: Some(th.p.fg_gray(0)),
210        highlight: Some(Style::default().underlined()),
211        block: Some(Block::bordered()),
212        popup: Default::default(),
213        popup_border: Some(th.style(Style::STATUS_BASE)),
214        popup_style: Some(th.style(Style::STATUS_BASE)),
215        ..Default::default()
216    }
217}
218
219fn month(th: &SalsaTheme) -> CalendarStyle {
220    CalendarStyle {
221        style: Default::default(),
222        title: None,
223        weeknum: Some(th.p.fg_limegreen(0)),
224        weekday: Some(th.p.fg_limegreen(0)),
225        day: None,
226        select: Some(th.style(Style::SELECT)),
227        focus: Some(th.style(Style::FOCUS)),
228        ..CalendarStyle::default()
229    }
230}
231
232fn msg_dialog(th: &SalsaTheme) -> MsgDialogStyle {
233    MsgDialogStyle {
234        style: th.style(Style::DIALOG_BASE),
235        button: Some(button(th)),
236        ..Default::default()
237    }
238}
239
240fn paragraph(th: &SalsaTheme) -> ParagraphStyle {
241    ParagraphStyle {
242        style: th.style(Style::CONTAINER_BASE),
243        focus: Some(th.style(Style::FOCUS)),
244        scroll: Some(scroll(th)),
245        ..Default::default()
246    }
247}
248
249fn radio(th: &SalsaTheme) -> RadioStyle {
250    RadioStyle {
251        layout: Some(RadioLayout::Stacked),
252        style: th.style(Style::INPUT),
253        focus: Some(th.style(Style::TEXT_FOCUS)),
254        ..Default::default()
255    }
256}
257
258/// Scroll style
259fn scroll(th: &SalsaTheme) -> ScrollStyle {
260    ScrollStyle {
261        thumb_style: Some(th.style(Style::CONTAINER_BORDER)),
262        track_style: Some(th.style(Style::CONTAINER_BORDER)),
263        min_style: Some(th.style(Style::CONTAINER_BORDER)),
264        begin_style: Some(th.style(Style::CONTAINER_ARROWS)),
265        end_style: Some(th.style(Style::CONTAINER_ARROWS)),
266        ..Default::default()
267    }
268}
269
270fn dialog_scroll(th: &SalsaTheme) -> ScrollStyle {
271    ScrollStyle {
272        thumb_style: Some(th.style(Style::DIALOG_BORDER)),
273        track_style: Some(th.style(Style::DIALOG_BORDER)),
274        min_style: Some(th.style(Style::DIALOG_BORDER)),
275        begin_style: Some(th.style(Style::DIALOG_ARROW)),
276        end_style: Some(th.style(Style::DIALOG_ARROW)),
277        ..Default::default()
278    }
279}
280
281fn popup_scroll(th: &SalsaTheme) -> ScrollStyle {
282    ScrollStyle {
283        thumb_style: Some(th.style(Style::POPUP_BORDER)),
284        track_style: Some(th.style(Style::POPUP_BORDER)),
285        min_style: Some(th.style(Style::POPUP_BORDER)),
286        begin_style: Some(th.style(Style::POPUP_ARROW)),
287        end_style: Some(th.style(Style::POPUP_ARROW)),
288        ..Default::default()
289    }
290}
291
292fn shadow(th: &SalsaTheme) -> ShadowStyle {
293    ShadowStyle {
294        style: th.p.normal_contrast(th.p.black[0]),
295        dir: ShadowDirection::BottomRight,
296        ..ShadowStyle::default()
297    }
298}
299
300fn slider(th: &SalsaTheme) -> SliderStyle {
301    SliderStyle {
302        style: th.style(Style::INPUT),
303        bounds: Some(th.p.gray(2)),
304        knob: Some(th.style(Style::TEXT_SELECT)),
305        focus: Some(th.style(Style::TEXT_FOCUS)),
306        text_align: Some(Alignment::Center),
307        ..Default::default()
308    }
309}
310
311fn split(th: &SalsaTheme) -> SplitStyle {
312    SplitStyle {
313        style: th.style(Style::CONTAINER_BORDER),
314        arrow_style: Some(th.style(Style::CONTAINER_ARROWS)),
315        drag_style: Some(th.style(Style::FOCUS)),
316        ..Default::default()
317    }
318}
319
320fn statusline(th: &SalsaTheme) -> StatusLineStyle {
321    StatusLineStyle {
322        styles: vec![
323            th.style(Style::STATUS_BASE),
324            th.p.normal_contrast(th.p.blue[2]),
325            th.p.normal_contrast(th.p.blue[2]),
326            th.p.normal_contrast(th.p.blue[2]),
327        ],
328        ..Default::default()
329    }
330}
331
332fn tabbed(th: &SalsaTheme) -> TabbedStyle {
333    TabbedStyle {
334        style: th.style(Style::CONTAINER_BASE),
335        tab: Some(th.p.gray(2)),
336        select: Some(th.p.secondary(0)),
337        focus: Some(th.style(Style::FOCUS)),
338        ..Default::default()
339    }
340}
341
342fn table(th: &SalsaTheme) -> TableStyle {
343    TableStyle {
344        style: th.style(Style::CONTAINER_BASE),
345        select_row: Some(th.style(Style::SELECT)),
346        show_row_focus: true,
347        focus_style: Some(th.style(Style::FOCUS)),
348        border_style: Some(th.style(Style::CONTAINER_BORDER)),
349        scroll: Some(scroll(th)),
350        header: Some(th.p.green(2)),
351        footer: Some(th.p.green(2)),
352        ..Default::default()
353    }
354}
355
356fn color_input(th: &SalsaTheme) -> ColorInputStyle {
357    ColorInputStyle {
358        text: TextStyle {
359            style: th.style(Style::INPUT),
360            focus: Some(th.style(Style::TEXT_FOCUS)),
361            select: Some(th.style(Style::TEXT_SELECT)),
362            invalid: Some(th.p.fg_red(3)),
363            ..TextStyle::default()
364        },
365        ..Default::default()
366    }
367}
368
369fn text(th: &SalsaTheme) -> TextStyle {
370    TextStyle {
371        style: th.style(Style::INPUT),
372        focus: Some(th.style(Style::TEXT_FOCUS)),
373        select: Some(th.style(Style::TEXT_SELECT)),
374        invalid: Some(th.p.fg_red(3)),
375        ..TextStyle::default()
376    }
377}
378
379fn textarea(th: &SalsaTheme) -> TextStyle {
380    TextStyle {
381        style: th.style(Style::INPUT),
382        focus: Some(th.style(Style::TEXT_FOCUS)),
383        select: Some(th.style(Style::TEXT_SELECT)),
384        scroll: Some(scroll(th)),
385        border_style: Some(th.style(Style::CONTAINER_BORDER)),
386        ..TextStyle::default()
387    }
388}
389
390fn textview(th: &SalsaTheme) -> TextStyle {
391    TextStyle {
392        style: th.style(Style::CONTAINER_BASE),
393        focus: Some(th.style(Style::CONTAINER_BASE)),
394        select: Some(th.style(Style::TEXT_SELECT)),
395        scroll: Some(scroll(th)),
396        border_style: Some(th.style(Style::CONTAINER_BORDER)),
397        ..TextStyle::default()
398    }
399}
400
401fn view(th: &SalsaTheme) -> ViewStyle {
402    ViewStyle {
403        scroll: Some(scroll(th)),
404        ..Default::default()
405    }
406}