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
35pub 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
259fn 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}