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