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