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