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