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