rat_theme4/themes/
shell.rs

1use crate::RatWidgetColor;
2use crate::palette::{Colors, Palette};
3use crate::theme::{Category, SalsaTheme};
4use crate::{StyleName, WidgetStyle};
5use rat_widget::button::ButtonStyle;
6use rat_widget::calendar::CalendarStyle;
7use rat_widget::checkbox::CheckboxStyle;
8use rat_widget::choice::ChoiceStyle;
9use rat_widget::clipper::ClipperStyle;
10use rat_widget::color_input::ColorInputStyle;
11use rat_widget::combobox::ComboboxStyle;
12use rat_widget::dialog_frame::DialogFrameStyle;
13use rat_widget::file_dialog::FileDialogStyle;
14use rat_widget::form::FormStyle;
15use rat_widget::line_number::LineNumberStyle;
16use rat_widget::list::ListStyle;
17use rat_widget::menu::MenuStyle;
18use rat_widget::msgdialog::MsgDialogStyle;
19use rat_widget::paragraph::ParagraphStyle;
20use rat_widget::radio::{RadioLayout, RadioStyle};
21use rat_widget::scrolled::{ScrollStyle, ScrollSymbols};
22use rat_widget::shadow::{ShadowDirection, ShadowStyle};
23use rat_widget::slider::SliderStyle;
24use rat_widget::splitter::SplitStyle;
25use rat_widget::statusline::StatusLineStyle;
26use rat_widget::tabbed::TabbedStyle;
27use rat_widget::table::TableStyle;
28use rat_widget::text::TextStyle;
29use rat_widget::view::ViewStyle;
30use ratatui::layout::Alignment;
31use ratatui::style::{Color, Style, Stylize};
32use ratatui::symbols;
33use ratatui::symbols::border;
34use ratatui::widgets::{Block, Borders};
35use std::time::Duration;
36
37/// A 'shell'-theme.
38///
39/// It uses almost no background colors and lets your shell
40/// bleed through.
41pub fn create_shell(name: &str, p: Palette) -> SalsaTheme {
42    let mut th = SalsaTheme::new(name, Category::Shell, p);
43
44    th.define_style(Style::LABEL_FG, th.p.fg_style_alias(Color::LABEL_FG));
45    th.define_style(Style::INPUT, th.p.style_alias(Color::INPUT_BG));
46    th.define_style(Style::FOCUS, th.p.style_alias(Color::FOCUS_BG));
47    th.define_style(Style::SELECT, th.p.style_alias(Color::SELECT_BG));
48    th.define_style(Style::DISABLED, th.p.style_alias(Color::DISABLED_BG));
49    th.define_style(Style::INVALID, th.p.style_alias(Color::INVALID_BG));
50    th.define_style(Style::HOVER, th.p.fg_style_alias(Color::HOVER_BG));
51    th.define_style(Style::TITLE, th.p.fg_style_alias(Color::TITLE_FG));
52    th.define_style(Style::HEADER, th.p.fg_style_alias(Color::HEADER_FG));
53    th.define_style(Style::FOOTER, th.p.fg_style_alias(Color::FOOTER_FG));
54    th.define_style(Style::SHADOWS, th.p.style_alias(Color::SHADOW_BG));
55    th.define_style(
56        Style::WEEK_HEADER_FG,
57        th.p.fg_style_alias(Color::WEEK_HEADER_FG),
58    );
59    th.define_style(
60        Style::MONTH_HEADER_FG,
61        th.p.fg_style_alias(Color::MONTH_HEADER_FG),
62    );
63    th.define_style(Style::TEXT_FOCUS, th.p.style_alias(Color::TEXT_FOCUS_BG));
64    th.define_style(Style::TEXT_SELECT, th.p.style_alias(Color::SELECT_BG));
65    th.define_style(Style::KEY_BINDING, th.p.style_alias(Color::KEY_BINDING_BG));
66
67    th.define_style(Style::BUTTON_BASE, th.p.style_alias(Color::BUTTON_BASE_BG));
68    th.define_style(Style::MENU_BASE, th.p.fg_style(Colors::TextLight, 0));
69    th.define_style(Style::STATUS_BASE, th.p.fg_style(Colors::TextLight, 0));
70
71    th.define_style(Style::CONTAINER_BASE, th.p.fg_style(Colors::TextLight, 0));
72    th.define_style(
73        Style::CONTAINER_BORDER_FG,
74        th.p.fg_style_alias(Color::CONTAINER_BORDER_FG),
75    );
76    th.define_style(
77        Style::CONTAINER_ARROW_FG,
78        th.p.fg_style_alias(Color::CONTAINER_ARROW_FG),
79    );
80
81    th.define_style(Style::POPUP_BASE, th.p.fg_style(Colors::TextLight, 0));
82    th.define_style(
83        Style::POPUP_BORDER_FG,
84        th.p.fg_style_alias(Color::POPUP_BORDER_FG),
85    );
86    th.define_style(
87        Style::POPUP_ARROW_FG,
88        th.p.fg_style_alias(Color::POPUP_ARROW_FG),
89    );
90
91    th.define_style(Style::DIALOG_BASE, th.p.fg_style(Colors::TextLight, 0));
92    th.define_style(
93        Style::DIALOG_BORDER_FG,
94        th.p.fg_style_alias(Color::DIALOG_BORDER_FG),
95    );
96    th.define_style(
97        Style::DIALOG_ARROW_FG,
98        th.p.fg_style_alias(Color::DIALOG_ARROW_FG),
99    );
100
101    th.define_fn(WidgetStyle::BUTTON, button);
102    th.define_fn(WidgetStyle::CALENDAR, month);
103    th.define_fn(WidgetStyle::CHECKBOX, checkbox);
104    th.define_fn(WidgetStyle::CHOICE, choice);
105    th.define_fn(WidgetStyle::CLIPPER, clipper);
106    th.define_fn(WidgetStyle::COMBOBOX, combobox);
107    th.define_fn(WidgetStyle::COLOR_INPUT, color_input);
108    th.define_fn(WidgetStyle::DIALOG_FRAME, dialog_frame);
109    th.define_fn(WidgetStyle::FILE_DIALOG, file_dialog);
110    th.define_fn(WidgetStyle::FORM, form);
111    th.define_fn(WidgetStyle::LINE_NR, line_nr);
112    th.define_fn(WidgetStyle::LIST, list);
113    th.define_fn(WidgetStyle::MENU, menu);
114    th.define_fn(WidgetStyle::MONTH, month);
115    th.define_fn(WidgetStyle::MSG_DIALOG, msg_dialog);
116    th.define_fn(WidgetStyle::PARAGRAPH, paragraph);
117    th.define_fn(WidgetStyle::RADIO, radio);
118    th.define_fn(WidgetStyle::SCROLL, scroll);
119    th.define_fn(WidgetStyle::SCROLL_DIALOG, dialog_scroll);
120    th.define_fn(WidgetStyle::SCROLL_POPUP, popup_scroll);
121    th.define_fn(WidgetStyle::SHADOW, shadow);
122    th.define_fn(WidgetStyle::SLIDER, slider);
123    th.define_fn(WidgetStyle::SPLIT, split);
124    th.define_fn(WidgetStyle::STATUSLINE, statusline);
125    th.define_fn(WidgetStyle::TABBED, tabbed);
126    th.define_fn(WidgetStyle::TABLE, table);
127    th.define_fn(WidgetStyle::TEXT, text);
128    th.define_fn(WidgetStyle::TEXTAREA, textarea);
129    th.define_fn(WidgetStyle::TEXTVIEW, textview);
130    th.define_fn(WidgetStyle::VIEW, view);
131
132    th
133}
134
135fn button(th: &SalsaTheme) -> ButtonStyle {
136    ButtonStyle {
137        style: th.style(Style::BUTTON_BASE),
138        focus: Some(th.style(Style::FOCUS)),
139        armed: Some(th.style(Style::SELECT)),
140        hover: Some(th.p.style_alias(Color::HOVER_BG)),
141        armed_delay: Some(Duration::from_millis(50)),
142        ..Default::default()
143    }
144}
145
146fn checkbox(th: &SalsaTheme) -> CheckboxStyle {
147    CheckboxStyle {
148        style: th.style(Style::INPUT),
149        focus: Some(th.style(Style::TEXT_FOCUS)),
150        ..Default::default()
151    }
152}
153
154fn combobox(th: &SalsaTheme) -> ComboboxStyle {
155    ComboboxStyle {
156        choice: choice(th),
157        text: text(th),
158        ..Default::default()
159    }
160}
161
162fn choice(th: &SalsaTheme) -> ChoiceStyle {
163    ChoiceStyle {
164        style: th.style(Style::INPUT),
165        select: Some(th.style(Style::TEXT_SELECT)),
166        focus: Some(th.style(Style::TEXT_FOCUS)),
167        popup_style: Some(th.style(Style::POPUP_BASE)),
168        popup_border: Some(th.style(Style::POPUP_BORDER_FG)),
169        popup_scroll: Some(popup_scroll(th)),
170        popup_block: Some(
171            Block::bordered()
172                .borders(Borders::LEFT | Borders::BOTTOM | Borders::RIGHT)
173                .border_set(border::Set {
174                    top_left: "X",
175                    top_right: "X",
176                    bottom_left: "▀",
177                    bottom_right: "▀",
178                    vertical_left: "▌",
179                    vertical_right: "▐",
180                    horizontal_top: "X",
181                    horizontal_bottom: "▀",
182                })
183                .border_style(th.style::<Style>(Style::POPUP_BORDER_FG)),
184        ),
185        ..Default::default()
186    }
187}
188
189fn clipper(th: &SalsaTheme) -> ClipperStyle {
190    ClipperStyle {
191        style: th.style(Style::CONTAINER_BASE),
192        label_style: Some(th.style(Style::LABEL_FG)),
193        scroll: Some(scroll(th)),
194        ..Default::default()
195    }
196}
197
198fn dialog_frame(th: &SalsaTheme) -> DialogFrameStyle {
199    DialogFrameStyle {
200        style: th.style(Style::DIALOG_BASE),
201        border_style: Some(th.style::<Style>(Style::DIALOG_BORDER_FG)),
202        button_style: Some(button(th)),
203        ..DialogFrameStyle::default()
204    }
205}
206
207fn file_dialog(th: &SalsaTheme) -> FileDialogStyle {
208    FileDialogStyle {
209        style: th.style(Style::DIALOG_BASE),
210        list: Some(list(th)),
211        roots: Some(ListStyle {
212            style: th.style(Style::DIALOG_BASE),
213            ..list(th)
214        }),
215        text: Some(text(th)),
216        button: Some(button(th)),
217        block: Some(Block::bordered()),
218        ..Default::default()
219    }
220}
221
222fn form(th: &SalsaTheme) -> FormStyle {
223    FormStyle {
224        style: th.style(Style::CONTAINER_BASE),
225        label_style: Some(th.style(Style::LABEL_FG)),
226        navigation: Some(th.style(Style::CONTAINER_ARROW_FG)),
227        navigation_hover: Some(th.style(Style::HOVER)),
228        block: Some(
229            Block::bordered()
230                .borders(Borders::TOP | Borders::BOTTOM)
231                .border_set(border::EMPTY)
232                .border_style(th.style::<Style>(Style::CONTAINER_BORDER_FG)),
233        ),
234        border_style: Some(th.style::<Style>(Style::CONTAINER_BORDER_FG)),
235        ..Default::default()
236    }
237}
238
239fn line_nr(th: &SalsaTheme) -> LineNumberStyle {
240    LineNumberStyle {
241        style: th.style(Style::CONTAINER_BASE),
242        cursor: Some(th.style(Style::TEXT_SELECT)),
243        ..LineNumberStyle::default()
244    }
245}
246
247fn list(th: &SalsaTheme) -> ListStyle {
248    ListStyle {
249        style: th.style(Style::CONTAINER_BASE),
250        select: Some(th.style(Style::SELECT)),
251        focus: Some(th.style(Style::FOCUS)),
252        scroll: Some(scroll(th)),
253        ..Default::default()
254    }
255}
256
257fn menu(th: &SalsaTheme) -> MenuStyle {
258    MenuStyle {
259        style: th.style(Style::MENU_BASE),
260        title: Some(th.style(Style::TITLE)),
261        focus: Some(th.style(Style::FOCUS)),
262        right: Some(th.style(Style::KEY_BINDING)),
263        disabled: Some(th.style(Style::DISABLED)),
264        highlight: Some(Style::default().underlined()),
265        popup_block: Some(Block::bordered()),
266        popup: Default::default(),
267        popup_border: Some(th.style(Style::MENU_BASE)),
268        popup_style: Some(th.style(Style::MENU_BASE)),
269        popup_focus: Some(th.style(Style::FOCUS)),
270        popup_right: Some(th.style(Style::KEY_BINDING)),
271        popup_disabled: Some(th.style(Style::DISABLED)),
272        popup_highlight: Some(Style::default().underlined()),
273        ..Default::default()
274    }
275}
276
277fn month(th: &SalsaTheme) -> CalendarStyle {
278    CalendarStyle {
279        style: th.style(Style::CONTAINER_BASE),
280        title: Some(th.style(Style::MONTH_HEADER_FG)),
281        weeknum: Some(th.style(Style::WEEK_HEADER_FG)),
282        weekday: Some(th.style(Style::WEEK_HEADER_FG)),
283        day: None,
284        select: Some(th.style(Style::SELECT)),
285        focus: Some(th.style(Style::FOCUS)),
286        ..CalendarStyle::default()
287    }
288}
289
290fn msg_dialog(th: &SalsaTheme) -> MsgDialogStyle {
291    MsgDialogStyle {
292        style: th.style(Style::DIALOG_BASE),
293        button: Some(button(th)),
294        ..Default::default()
295    }
296}
297
298fn paragraph(th: &SalsaTheme) -> ParagraphStyle {
299    ParagraphStyle {
300        style: th.style(Style::CONTAINER_BASE),
301        focus: Some(th.style(Style::FOCUS)),
302        scroll: Some(scroll(th)),
303        ..Default::default()
304    }
305}
306
307fn radio(th: &SalsaTheme) -> RadioStyle {
308    RadioStyle {
309        layout: Some(RadioLayout::Stacked),
310        style: th.style(Style::INPUT),
311        focus: Some(th.style(Style::TEXT_FOCUS)),
312        ..Default::default()
313    }
314}
315
316/// Scroll style
317fn scroll(th: &SalsaTheme) -> ScrollStyle {
318    ScrollStyle {
319        thumb_style: Some(th.style(Style::CONTAINER_BORDER_FG)),
320        track_style: Some(th.style(Style::CONTAINER_BORDER_FG)),
321        min_style: Some(th.style(Style::CONTAINER_BORDER_FG)),
322        begin_style: Some(th.style(Style::CONTAINER_ARROW_FG)),
323        end_style: Some(th.style(Style::CONTAINER_ARROW_FG)),
324        horizontal: Some(ScrollSymbols {
325            track: "▒",
326            thumb: symbols::block::FULL,
327            begin: "←",
328            end: "→",
329            min: "░",
330        }),
331        vertical: Some(ScrollSymbols {
332            track: "▒",
333            thumb: symbols::block::FULL,
334            begin: "↑",
335            end: "↓",
336            min: "░",
337        }),
338        ..Default::default()
339    }
340}
341
342fn popup_scroll(th: &SalsaTheme) -> ScrollStyle {
343    ScrollStyle {
344        thumb_style: Some(th.style(Style::POPUP_BORDER_FG)),
345        track_style: Some(th.style(Style::POPUP_BORDER_FG)),
346        min_style: Some(th.style(Style::POPUP_BORDER_FG)),
347        begin_style: Some(th.style(Style::POPUP_ARROW_FG)),
348        end_style: Some(th.style(Style::POPUP_ARROW_FG)),
349        horizontal: Some(ScrollSymbols {
350            track: "▒",
351            thumb: symbols::block::FULL,
352            begin: "←",
353            end: "→",
354            min: "░",
355        }),
356        vertical: Some(ScrollSymbols {
357            track: "▒",
358            thumb: symbols::block::FULL,
359            begin: "↑",
360            end: "↓",
361            min: "░",
362        }),
363        ..Default::default()
364    }
365}
366
367fn dialog_scroll(th: &SalsaTheme) -> ScrollStyle {
368    ScrollStyle {
369        thumb_style: Some(th.style(Style::DIALOG_BORDER_FG)),
370        track_style: Some(th.style(Style::DIALOG_BORDER_FG)),
371        min_style: Some(th.style(Style::DIALOG_BORDER_FG)),
372        begin_style: Some(th.style(Style::POPUP_ARROW_FG)),
373        end_style: Some(th.style(Style::POPUP_ARROW_FG)),
374        horizontal: Some(ScrollSymbols {
375            track: "▒",
376            thumb: symbols::block::FULL,
377            begin: "←",
378            end: "→",
379            min: "░",
380        }),
381        vertical: Some(ScrollSymbols {
382            track: "▒",
383            thumb: symbols::block::FULL,
384            begin: "↑",
385            end: "↓",
386            min: "░",
387        }),
388        ..Default::default()
389    }
390}
391
392fn shadow(th: &SalsaTheme) -> ShadowStyle {
393    ShadowStyle {
394        style: th.style(Style::SHADOWS),
395        dir: ShadowDirection::BottomRight,
396        ..ShadowStyle::default()
397    }
398}
399
400fn slider(th: &SalsaTheme) -> SliderStyle {
401    SliderStyle {
402        style: th.style(Style::INPUT),
403        bounds: Some(th.style(Style::INPUT)),
404        knob: Some(th.style(Style::TEXT_SELECT)),
405        focus: Some(th.style(Style::TEXT_FOCUS)),
406        text_align: Some(Alignment::Center),
407        ..Default::default()
408    }
409}
410
411fn split(th: &SalsaTheme) -> SplitStyle {
412    SplitStyle {
413        style: th.style(Style::CONTAINER_BORDER_FG),
414        arrow_style: Some(th.style(Style::CONTAINER_ARROW_FG)),
415        drag_style: Some(th.style(Style::HOVER)),
416        ..Default::default()
417    }
418}
419
420fn statusline(th: &SalsaTheme) -> StatusLineStyle {
421    StatusLineStyle {
422        styles: vec![
423            th.style(Style::STATUS_BASE),
424            th.p.style(Colors::Blue, 3),
425            th.p.style(Colors::Blue, 2),
426            th.p.style(Colors::Blue, 1),
427        ],
428        ..Default::default()
429    }
430}
431
432fn tabbed(th: &SalsaTheme) -> TabbedStyle {
433    TabbedStyle {
434        style: th.style(Style::CONTAINER_BASE),
435        border_style: Some(th.style(Style::CONTAINER_BORDER_FG)),
436        tab: Some(th.style(Style::INPUT)),
437        hover: Some(th.style(Style::HOVER)),
438        select: Some(th.style(Style::SELECT)),
439        focus: Some(th.style(Style::FOCUS)),
440        ..Default::default()
441    }
442}
443
444fn table(th: &SalsaTheme) -> TableStyle {
445    TableStyle {
446        style: th.style(Style::CONTAINER_BASE),
447        select_row: Some(th.style(Style::SELECT)),
448        show_row_focus: true,
449        focus_style: Some(th.style(Style::FOCUS)),
450        border_style: Some(th.style(Style::CONTAINER_BORDER_FG)),
451        scroll: Some(scroll(th)),
452        header: Some(th.style(Style::HEADER)),
453        footer: Some(th.style(Style::FOOTER)),
454        ..Default::default()
455    }
456}
457
458fn color_input(th: &SalsaTheme) -> ColorInputStyle {
459    ColorInputStyle {
460        text: TextStyle {
461            style: th.style(Style::INPUT),
462            focus: Some(th.style(Style::TEXT_FOCUS)),
463            select: Some(th.style(Style::TEXT_SELECT)),
464            invalid: Some(th.style(Style::INVALID)),
465            ..TextStyle::default()
466        },
467        ..Default::default()
468    }
469}
470
471fn text(th: &SalsaTheme) -> TextStyle {
472    TextStyle {
473        style: th.style(Style::INPUT),
474        focus: Some(th.style(Style::TEXT_FOCUS)),
475        select: Some(th.style(Style::TEXT_SELECT)),
476        invalid: Some(th.style(Style::INVALID)),
477        ..TextStyle::default()
478    }
479}
480
481fn textarea(th: &SalsaTheme) -> TextStyle {
482    TextStyle {
483        style: th.style(Style::INPUT),
484        focus: Some(th.style(Style::INPUT)),
485        select: Some(th.style(Style::TEXT_SELECT)),
486        scroll: Some(scroll(th)),
487        border_style: Some(th.style(Style::CONTAINER_BORDER_FG)),
488        ..TextStyle::default()
489    }
490}
491
492fn textview(th: &SalsaTheme) -> TextStyle {
493    TextStyle {
494        style: th.style(Style::CONTAINER_BASE),
495        focus: Some(th.style(Style::CONTAINER_BASE)),
496        select: Some(th.style(Style::TEXT_SELECT)),
497        scroll: Some(scroll(th)),
498        border_style: Some(th.style(Style::CONTAINER_BORDER_FG)),
499        ..TextStyle::default()
500    }
501}
502
503fn view(th: &SalsaTheme) -> ViewStyle {
504    ViewStyle {
505        scroll: Some(scroll(th)),
506        ..Default::default()
507    }
508}