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