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