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;
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::widgets::{Block, Borders};
32use std::time::Duration;
33
34pub fn dark_theme(name: &str, p: Palette) -> SalsaTheme {
36 let mut th = SalsaTheme::new(name, Category::Dark, p);
37
38 th.define(Style::INPUT, th.p.high_contrast(p.gray[3]));
39 th.define(Style::FOCUS, th.p.high_contrast(p.primary[2]));
40 th.define(Style::SELECT, th.p.high_contrast(p.secondary[1]));
41 th.define(Style::TEXT_FOCUS, th.p.high_contrast(p.primary[1]));
42 th.define(Style::TEXT_SELECT, th.p.high_contrast(p.secondary[1]));
43 th.define(Style::BUTTON_BASE, th.p.gray(2));
44
45 th.define(Style::CONTAINER_BASE, th.p.normal_contrast(p.black[1]));
46 th.define(
47 Style::CONTAINER_BORDER,
48 th.p.normal_contrast_color(p.black[1], &p.gray),
49 );
50 th.define(
51 Style::CONTAINER_ARROWS,
52 th.p.normal_contrast_color(p.black[1], &p.gray),
53 );
54
55 th.define(Style::POPUP_BASE, th.p.high_contrast(p.white[0]));
56 th.define(Style::POPUP_BORDER, th.p.normal_contrast(p.white[0]));
57 th.define(Style::POPUP_ARROW, th.p.normal_contrast(p.white[0]));
58
59 th.define(Style::DIALOG_BASE, th.p.high_contrast(p.gray[1]));
60 th.define(Style::DIALOG_BORDER, th.p.normal_contrast(p.gray[1]));
61 th.define(Style::DIALOG_ARROW, th.p.normal_contrast(p.gray[1]));
62
63 th.define(Style::STATUS_BASE, th.p.normal_contrast(p.black[2]));
64
65 th.define_fn(WidgetStyle::BUTTON, button);
66 th.define_fn(WidgetStyle::CHECKBOX, checkbox);
67 th.define_fn(WidgetStyle::CHOICE, choice);
68 th.define_fn(WidgetStyle::CLIPPER, clipper);
69 th.define_fn(WidgetStyle::COMBOBOX, combobox);
70 th.define_fn(WidgetStyle::COLOR_INPUT, color_input);
71 th.define_fn(WidgetStyle::DIALOG_FRAME, dialog_frame);
72 th.define_fn(WidgetStyle::FILE_DIALOG, file_dialog);
73 th.define_fn(WidgetStyle::FORM, form);
74 th.define_fn(WidgetStyle::LINE_NR, line_nr);
75 th.define_fn(WidgetStyle::LIST, list);
76 th.define_fn(WidgetStyle::MENU, menu);
77 th.define_fn(WidgetStyle::MONTH, month);
78 th.define_fn(WidgetStyle::MSG_DIALOG, msg_dialog);
79 th.define_fn(WidgetStyle::PARAGRAPH, paragraph);
80 th.define_fn(WidgetStyle::RADIO, radio);
81 th.define_fn(WidgetStyle::SCROLL, scroll);
82 th.define_fn(WidgetStyle::SCROLL_DIALOG, dialog_scroll);
83 th.define_fn(WidgetStyle::SCROLL_POPUP, popup_scroll);
84 th.define_fn(WidgetStyle::SHADOW, shadow);
85 th.define_fn(WidgetStyle::SLIDER, slider);
86 th.define_fn(WidgetStyle::SPLIT, split);
87 th.define_fn(WidgetStyle::STATUSLINE, statusline);
88 th.define_fn(WidgetStyle::TABBED, tabbed);
89 th.define_fn(WidgetStyle::TABLE, table);
90 th.define_fn(WidgetStyle::TEXT, text);
91 th.define_fn(WidgetStyle::TEXTAREA, textarea);
92 th.define_fn(WidgetStyle::TEXTVIEW, textview);
93 th.define_fn(WidgetStyle::VIEW, view);
94
95 th
96}
97
98fn button(th: &SalsaTheme) -> ButtonStyle {
99 ButtonStyle {
100 style: th.style(Style::BUTTON_BASE),
101 focus: Some(th.style(Style::FOCUS)),
102 armed: Some(th.style(Style::SELECT)),
103 hover: Some(th.style(Style::SELECT)),
104 armed_delay: Some(Duration::from_millis(50)),
105 ..Default::default()
106 }
107}
108
109fn checkbox(th: &SalsaTheme) -> CheckboxStyle {
110 CheckboxStyle {
111 style: th.style(Style::INPUT),
112 focus: Some(th.style(Style::TEXT_FOCUS)),
113 ..Default::default()
114 }
115}
116
117fn combobox(th: &SalsaTheme) -> ComboboxStyle {
118 ComboboxStyle {
119 choice: choice(th),
120 text: text(th),
121 ..Default::default()
122 }
123}
124
125fn choice(th: &SalsaTheme) -> ChoiceStyle {
126 ChoiceStyle {
127 style: th.style(Style::INPUT),
128 select: Some(th.style(Style::TEXT_SELECT)),
129 focus: Some(th.style(Style::TEXT_FOCUS)),
130 popup_style: Some(th.style(Style::POPUP_BASE)),
131 popup_border: Some(th.style(Style::POPUP_BORDER)),
132 popup_scroll: Some(popup_scroll(th)),
133 popup_block: Some(
134 Block::bordered()
135 .borders(Borders::LEFT)
136 .border_style(th.style::<Style>(Style::POPUP_BORDER)),
137 ),
138 ..Default::default()
139 }
140}
141
142fn clipper(th: &SalsaTheme) -> ClipperStyle {
143 ClipperStyle {
144 style: th.style(Style::CONTAINER_BASE),
145 scroll: Some(scroll(th)),
146 ..Default::default()
147 }
148}
149
150fn dialog_frame(th: &SalsaTheme) -> DialogFrameStyle {
151 DialogFrameStyle {
152 style: th.style(Style::DIALOG_BASE),
153 block: Some(Block::bordered().style(th.style::<Style>(Style::DIALOG_BORDER))),
154 button_style: Some(button(th)),
155 ..DialogFrameStyle::default()
156 }
157}
158
159fn file_dialog(th: &SalsaTheme) -> FileDialogStyle {
160 FileDialogStyle {
161 style: th.style(Style::DIALOG_BASE),
162 list: Some(list(th)),
163 roots: Some(ListStyle {
164 style: th.style(Style::DIALOG_BASE),
165 ..list(th)
166 }),
167 text: Some(text(th)),
168 button: Some(button(th)),
169 block: Some(Block::bordered()),
170 ..Default::default()
171 }
172}
173
174fn form(th: &SalsaTheme) -> FormStyle {
175 FormStyle {
176 style: th.style(Style::CONTAINER_BASE),
177 navigation: Some(th.style(Style::CONTAINER_ARROWS)),
178 block: Some(
179 Block::bordered()
180 .borders(Borders::TOP | Borders::BOTTOM)
181 .border_style(th.style::<Style>(Style::CONTAINER_BORDER)),
182 ),
183 ..Default::default()
184 }
185}
186
187fn line_nr(th: &SalsaTheme) -> LineNumberStyle {
188 LineNumberStyle {
189 style: th.style(Style::CONTAINER_BASE),
190 cursor: Some(th.style(Style::TEXT_SELECT)),
191 ..LineNumberStyle::default()
192 }
193}
194
195fn list(th: &SalsaTheme) -> ListStyle {
196 ListStyle {
197 style: th.style(Style::CONTAINER_BASE),
198 select: Some(th.style(Style::SELECT)),
199 focus: Some(th.style(Style::FOCUS)),
200 scroll: Some(scroll(th)),
201 ..Default::default()
202 }
203}
204
205fn menu(th: &SalsaTheme) -> MenuStyle {
206 MenuStyle {
207 style: th.style(Style::STATUS_BASE),
208 title: Some(th.p.yellow(2)),
209 focus: Some(th.style(Style::FOCUS)),
210 right: Some(th.p.fg_bluegreen(0)),
211 disabled: Some(th.p.fg_gray(0)),
212 highlight: Some(Style::default().underlined()),
213 block: Some(Block::bordered()),
214 popup: Default::default(),
215 popup_border: Some(th.style(Style::STATUS_BASE)),
216 popup_style: Some(th.style(Style::STATUS_BASE)),
217 ..Default::default()
218 }
219}
220
221fn month(th: &SalsaTheme) -> CalendarStyle {
222 CalendarStyle {
223 style: th.p.normal_contrast(th.p.black[2]),
224 title: None,
225 weeknum: Some(th.p.fg_limegreen(2)),
226 weekday: Some(th.p.fg_limegreen(2)),
227 day: None,
228 select: Some(th.style(Style::SELECT)),
229 focus: Some(th.style(Style::FOCUS)),
230 ..CalendarStyle::default()
231 }
232}
233
234fn msg_dialog(th: &SalsaTheme) -> MsgDialogStyle {
235 MsgDialogStyle {
236 style: th.style(Style::DIALOG_BASE),
237 button: Some(button(th)),
238 ..Default::default()
239 }
240}
241
242fn paragraph(th: &SalsaTheme) -> ParagraphStyle {
243 ParagraphStyle {
244 style: th.style(Style::CONTAINER_BASE),
245 focus: Some(th.style(Style::FOCUS)),
246 scroll: Some(scroll(th)),
247 ..Default::default()
248 }
249}
250
251fn radio(th: &SalsaTheme) -> RadioStyle {
252 RadioStyle {
253 layout: Some(RadioLayout::Stacked),
254 style: th.style(Style::INPUT),
255 focus: Some(th.style(Style::TEXT_FOCUS)),
256 ..Default::default()
257 }
258}
259
260fn scroll(th: &SalsaTheme) -> ScrollStyle {
262 ScrollStyle {
263 thumb_style: Some(th.style(Style::CONTAINER_BORDER)),
264 track_style: Some(th.style(Style::CONTAINER_BORDER)),
265 min_style: Some(th.style(Style::CONTAINER_BORDER)),
266 begin_style: Some(th.style(Style::CONTAINER_ARROWS)),
267 end_style: Some(th.style(Style::CONTAINER_ARROWS)),
268 ..Default::default()
269 }
270}
271
272fn popup_scroll(th: &SalsaTheme) -> ScrollStyle {
273 ScrollStyle {
274 thumb_style: Some(th.style(Style::POPUP_BORDER)),
275 track_style: Some(th.style(Style::POPUP_BORDER)),
276 min_style: Some(th.style(Style::POPUP_BORDER)),
277 begin_style: Some(th.style(Style::POPUP_ARROW)),
278 end_style: Some(th.style(Style::POPUP_ARROW)),
279 ..Default::default()
280 }
281}
282
283fn dialog_scroll(th: &SalsaTheme) -> ScrollStyle {
284 ScrollStyle {
285 thumb_style: Some(th.style(Style::DIALOG_BORDER)),
286 track_style: Some(th.style(Style::DIALOG_BORDER)),
287 min_style: Some(th.style(Style::DIALOG_BORDER)),
288 begin_style: Some(th.style(Style::POPUP_ARROW)),
289 end_style: Some(th.style(Style::POPUP_ARROW)),
290 ..Default::default()
291 }
292}
293
294fn shadow(th: &SalsaTheme) -> ShadowStyle {
295 ShadowStyle {
296 style: th.p.normal_contrast(th.p.black[0]),
297 dir: ShadowDirection::BottomRight,
298 ..ShadowStyle::default()
299 }
300}
301
302fn slider(th: &SalsaTheme) -> SliderStyle {
303 SliderStyle {
304 style: th.style(Style::INPUT),
305 bounds: Some(th.p.gray(2)),
306 knob: Some(th.style(Style::TEXT_SELECT)),
307 focus: Some(th.style(Style::TEXT_FOCUS)),
308 text_align: Some(Alignment::Center),
309 ..Default::default()
310 }
311}
312
313fn split(th: &SalsaTheme) -> SplitStyle {
314 SplitStyle {
315 style: th.style(Style::CONTAINER_BORDER),
316 arrow_style: Some(th.style(Style::CONTAINER_ARROWS)),
317 drag_style: Some(th.style(Style::FOCUS)),
318 ..Default::default()
319 }
320}
321
322fn statusline(th: &SalsaTheme) -> StatusLineStyle {
323 StatusLineStyle {
324 styles: vec![
325 th.style(Style::STATUS_BASE),
326 th.p.normal_contrast(th.p.blue[3]),
327 th.p.normal_contrast(th.p.blue[2]),
328 th.p.normal_contrast(th.p.blue[1]),
329 ],
330 ..Default::default()
331 }
332}
333
334fn tabbed(th: &SalsaTheme) -> TabbedStyle {
335 TabbedStyle {
336 style: th.style(Style::CONTAINER_BASE),
337 tab: Some(th.p.gray(2)),
338 select: Some(th.p.secondary(0)),
339 focus: Some(th.style(Style::FOCUS)),
340 ..Default::default()
341 }
342}
343
344fn table(th: &SalsaTheme) -> TableStyle {
345 TableStyle {
346 style: th.style(Style::CONTAINER_BASE),
347 select_row: Some(th.style(Style::SELECT)),
348 show_row_focus: true,
349 focus_style: Some(th.style(Style::FOCUS)),
350 border_style: Some(th.style(Style::CONTAINER_BORDER)),
351 scroll: Some(scroll(th)),
352 header: Some(th.p.blue(2)),
353 footer: Some(th.p.blue(2)),
354 ..Default::default()
355 }
356}
357
358fn color_input(th: &SalsaTheme) -> ColorInputStyle {
359 ColorInputStyle {
360 text: TextStyle {
361 style: th.style(Style::INPUT),
362 focus: Some(th.style(Style::TEXT_FOCUS)),
363 select: Some(th.style(Style::TEXT_SELECT)),
364 invalid: Some(th.p.fg_red(3)),
365 ..TextStyle::default()
366 },
367 ..Default::default()
368 }
369}
370
371fn text(th: &SalsaTheme) -> TextStyle {
372 TextStyle {
373 style: th.style(Style::INPUT),
374 focus: Some(th.style(Style::TEXT_FOCUS)),
375 select: Some(th.style(Style::TEXT_SELECT)),
376 invalid: Some(th.p.fg_red(3)),
377 ..TextStyle::default()
378 }
379}
380
381fn textarea(th: &SalsaTheme) -> TextStyle {
382 TextStyle {
383 style: th.style(Style::INPUT),
384 focus: Some(th.style(Style::TEXT_FOCUS)),
385 select: Some(th.style(Style::TEXT_SELECT)),
386 scroll: Some(scroll(th)),
387 border_style: Some(th.style(Style::CONTAINER_BORDER)),
388 ..TextStyle::default()
389 }
390}
391
392fn textview(th: &SalsaTheme) -> TextStyle {
393 TextStyle {
394 style: th.style(Style::CONTAINER_BASE),
395 focus: Some(th.style(Style::CONTAINER_BASE)),
396 select: Some(th.style(Style::TEXT_SELECT)),
397 scroll: Some(scroll(th)),
398 border_style: Some(th.style(Style::CONTAINER_BORDER)),
399 ..TextStyle::default()
400 }
401}
402
403fn view(th: &SalsaTheme) -> ViewStyle {
404 ViewStyle {
405 scroll: Some(scroll(th)),
406 ..Default::default()
407 }
408}