rat_theme4/themes/
shell.rs

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