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