1use crate::{Contrast, Palette};
6use rat_widget::button::ButtonStyle;
7use rat_widget::calendar::CalendarStyle;
8use rat_widget::checkbox::CheckboxStyle;
9use rat_widget::choice::ChoiceStyle;
10use rat_widget::clipper::ClipperStyle;
11use rat_widget::file_dialog::FileDialogStyle;
12use rat_widget::line_number::LineNumberStyle;
13use rat_widget::list::ListStyle;
14use rat_widget::menu::MenuStyle;
15use rat_widget::msgdialog::MsgDialogStyle;
16use rat_widget::paragraph::ParagraphStyle;
17use rat_widget::radio::{RadioLayout, RadioStyle};
18use rat_widget::scrolled::ScrollStyle;
19use rat_widget::shadow::{ShadowDirection, ShadowStyle};
20use rat_widget::slider::SliderStyle;
21use rat_widget::splitter::SplitStyle;
22use rat_widget::statusline::StatusLineStyle;
23use rat_widget::tabbed::TabbedStyle;
24use rat_widget::table::TableStyle;
25use rat_widget::text::TextStyle;
26use rat_widget::view::ViewStyle;
27use ratatui::layout::Alignment;
28use ratatui::style::Color;
29use ratatui::style::{Style, Stylize};
30use ratatui::widgets::{Block, Borders};
31use std::borrow::Cow;
32use std::time::Duration;
33
34#[derive(Debug, Clone)]
42pub struct DarkTheme {
43 p: Palette,
44 name: &'static str,
45}
46
47impl DarkTheme {
48 pub const fn new(name: &'static str, s: Palette) -> Self {
49 Self { p: s, name }
50 }
51}
52
53impl DarkTheme {
54 pub fn name(&self) -> &str {
56 &self.name
57 }
58
59 pub fn dark_theme(&self) -> bool {
61 true
62 }
63
64 pub fn palette(&self) -> &Palette {
66 &self.p
67 }
68
69 pub fn style(&self, bg: Color) -> Style {
71 self.p.style(bg, Contrast::Normal)
72 }
73
74 pub fn high_style(&self, bg: Color) -> Style {
76 self.p.style(bg, Contrast::High)
77 }
78
79 pub fn white(&self, n: usize) -> Style {
82 self.p.white(n, Contrast::Normal)
83 }
84
85 pub fn black(&self, n: usize) -> Style {
88 self.p.black(n, Contrast::Normal)
89 }
90
91 pub fn gray(&self, n: usize) -> Style {
94 self.p.gray(n, Contrast::Normal)
95 }
96
97 pub fn red(&self, n: usize) -> Style {
100 self.p.red(n, Contrast::Normal)
101 }
102
103 pub fn orange(&self, n: usize) -> Style {
106 self.p.orange(n, Contrast::Normal)
107 }
108
109 pub fn yellow(&self, n: usize) -> Style {
112 self.p.yellow(n, Contrast::Normal)
113 }
114
115 pub fn limegreen(&self, n: usize) -> Style {
118 self.p.limegreen(n, Contrast::Normal)
119 }
120
121 pub fn green(&self, n: usize) -> Style {
124 self.p.green(n, Contrast::Normal)
125 }
126
127 pub fn bluegreen(&self, n: usize) -> Style {
130 self.p.bluegreen(n, Contrast::Normal)
131 }
132
133 pub fn cyan(&self, n: usize) -> Style {
136 self.p.cyan(n, Contrast::Normal)
137 }
138
139 pub fn blue(&self, n: usize) -> Style {
142 self.p.blue(n, Contrast::Normal)
143 }
144
145 pub fn deepblue(&self, n: usize) -> Style {
148 self.p.deepblue(n, Contrast::Normal)
149 }
150
151 pub fn purple(&self, n: usize) -> Style {
154 self.p.purple(n, Contrast::Normal)
155 }
156
157 pub fn magenta(&self, n: usize) -> Style {
160 self.p.magenta(n, Contrast::Normal)
161 }
162
163 pub fn redpink(&self, n: usize) -> Style {
166 self.p.redpink(n, Contrast::Normal)
167 }
168
169 pub fn primary(&self, n: usize) -> Style {
172 self.p.primary(n, Contrast::Normal)
173 }
174
175 pub fn secondary(&self, n: usize) -> Style {
178 self.p.secondary(n, Contrast::Normal)
179 }
180
181 pub fn focus(&self) -> Style {
183 self.high_style(self.p.primary[2])
184 }
185
186 pub fn select(&self) -> Style {
188 self.high_style(self.p.secondary[1])
189 }
190
191 pub fn text_input(&self) -> Style {
193 self.high_style(self.p.gray[3])
194 }
195
196 pub fn text_focus(&self) -> Style {
198 self.high_style(self.p.primary[1])
199 }
200
201 pub fn text_select(&self) -> Style {
203 self.high_style(self.p.secondary[1])
204 }
205
206 pub fn container_base(&self) -> Style {
208 self.style(self.p.black[1])
209 }
210
211 pub fn container_border(&self) -> Style {
213 self.container_base().fg(self.p.gray[0])
214 }
215
216 pub fn container_arrow(&self) -> Style {
218 self.container_base().fg(self.p.gray[0])
219 }
220
221 pub fn popup_base(&self) -> Style {
223 self.style(self.p.white[0])
224 }
225
226 pub fn popup_label(&self) -> Style {
228 self.style(self.p.white[0])
229 }
230
231 pub fn popup_border(&self) -> Style {
233 self.popup_base().fg(self.p.gray[0])
234 }
235
236 pub fn popup_arrow(&self) -> Style {
238 self.popup_base().fg(self.p.gray[0])
239 }
240
241 pub fn dialog_base(&self) -> Style {
243 self.style(self.p.gray[1])
244 }
245
246 pub fn dialog_label(&self) -> Style {
248 self.dialog_base()
249 }
250
251 pub fn dialog_border(&self) -> Style {
253 self.dialog_base().fg(self.p.white[0])
254 }
255
256 pub fn dialog_arrow(&self) -> Style {
258 self.dialog_base().fg(self.p.white[0])
259 }
260
261 pub fn status_base(&self) -> Style {
263 self.style(self.p.black[2])
264 }
265
266 pub fn button_base(&self) -> Style {
268 self.style(self.p.gray[2])
269 }
270
271 pub fn button_armed(&self) -> Style {
273 self.style(self.p.secondary[0])
274 }
275
276 pub fn month_style(&self) -> CalendarStyle {
278 CalendarStyle {
279 style: self.style(self.p.black[2]),
280 title: None,
281 weeknum: Some(Style::new().fg(self.p.limegreen[2])),
282 weekday: Some(Style::new().fg(self.p.limegreen[2])),
283 day: None,
284 select: Some(self.select()),
285 focus: Some(self.focus()),
286 ..CalendarStyle::default()
287 }
288 }
289
290 pub fn shadow_style(&self) -> ShadowStyle {
292 ShadowStyle {
293 style: Style::new().bg(self.p.black[0]),
294 dir: ShadowDirection::BottomRight,
295 ..ShadowStyle::default()
296 }
297 }
298
299 pub fn line_nr_style(&self) -> LineNumberStyle {
301 LineNumberStyle {
302 style: self.container_base().fg(self.p.gray[1]),
303 cursor: Some(self.text_select()),
304 ..LineNumberStyle::default()
305 }
306 }
307
308 pub fn textarea_style(&self) -> TextStyle {
310 TextStyle {
311 style: self.container_base(),
312 focus: Some(self.focus()),
313 select: Some(self.text_select()),
314 scroll: Some(self.scroll_style()),
315 border_style: Some(self.container_border()),
316 ..TextStyle::default()
317 }
318 }
319
320 pub fn text_style(&self) -> TextStyle {
322 TextStyle {
323 style: self.text_input(),
324 focus: Some(self.text_focus()),
325 select: Some(self.text_select()),
326 invalid: Some(Style::default().bg(self.p.red[3])),
327 ..TextStyle::default()
328 }
329 }
330
331 pub fn label_style(&self) -> Style {
333 self.container_base()
334 }
335
336 pub fn paragraph_style(&self) -> ParagraphStyle {
337 ParagraphStyle {
338 style: self.container_base(),
339 focus: Some(self.focus()),
340 scroll: Some(self.scroll_style()),
341 ..Default::default()
342 }
343 }
344
345 pub fn choice_style(&self) -> ChoiceStyle {
346 ChoiceStyle {
347 style: self.text_input(),
348 select: Some(self.text_select()),
349 focus: Some(self.text_focus()),
350 popup_style: Some(self.popup_base()),
351 popup_border: Some(self.popup_border()),
352 popup_scroll: Some(self.popup_scroll_style()),
353 popup_block: Some(
354 Block::bordered()
355 .borders(Borders::LEFT)
356 .border_style(self.popup_border()),
357 ),
358 ..Default::default()
359 }
360 }
361
362 pub fn radio_style(&self) -> RadioStyle {
363 RadioStyle {
364 layout: Some(RadioLayout::Stacked),
365 style: self.text_input(),
366 focus: Some(self.text_focus()),
367 ..Default::default()
368 }
369 }
370
371 pub fn checkbox_style(&self) -> CheckboxStyle {
373 CheckboxStyle {
374 style: self.text_input(),
375 focus: Some(self.text_focus()),
376 ..Default::default()
377 }
378 }
379
380 pub fn slider_style(&self) -> SliderStyle {
382 SliderStyle {
383 style: self.text_input(),
384 bounds: Some(self.gray(2)),
385 knob: Some(self.select()),
386 focus: Some(self.focus()),
387 text_align: Some(Alignment::Center),
388 ..Default::default()
389 }
390 }
391
392 pub fn menu_style(&self) -> MenuStyle {
394 let menu = Style::default().fg(self.p.white[3]).bg(self.p.black[2]);
395 MenuStyle {
396 style: menu,
397 title: Some(Style::default().fg(self.p.black[0]).bg(self.p.yellow[2])),
398 focus: Some(self.focus()),
399 right: Some(Style::default().fg(self.p.bluegreen[0])),
400 disabled: Some(Style::default().fg(self.p.gray[0])),
401 highlight: Some(Style::default().underlined()),
402 popup_style: Some(menu),
403 popup_block: Some(Block::bordered()),
404 popup_focus: Some(self.focus()),
405 popup_right: Some(Style::default().fg(self.p.bluegreen[0])),
406 popup_disabled: Some(Style::default().fg(self.p.gray[0])),
407 popup_highlight: Some(Style::default().underlined()),
408 popup: Default::default(),
409 ..Default::default()
410 }
411 }
412
413 pub fn button_style(&self) -> ButtonStyle {
415 ButtonStyle {
416 style: self.button_base(),
417 focus: Some(self.focus()),
418 armed: Some(self.select()),
419 hover: Some(self.select()),
420 armed_delay: Some(Duration::from_millis(50)),
421 ..Default::default()
422 }
423 }
424
425 pub fn table_style(&self) -> TableStyle {
427 TableStyle {
428 style: self.container_base(),
429 select_row: Some(self.select()),
430 show_row_focus: true,
431 focus_style: Some(self.focus()),
432 border_style: Some(self.container_border()),
433 scroll: Some(self.scroll_style()),
434 ..Default::default()
435 }
436 }
437
438 pub fn table_header(&self) -> Style {
439 self.style(self.p.blue[2])
440 }
441
442 pub fn table_footer(&self) -> Style {
443 self.style(self.p.blue[2])
444 }
445
446 pub fn list_style(&self) -> ListStyle {
448 ListStyle {
449 style: self.container_base(),
450 select: Some(self.select()),
451 focus: Some(self.focus()),
452 scroll: Some(self.scroll_style()),
453 ..Default::default()
454 }
455 }
456
457 pub fn scroll_style(&self) -> ScrollStyle {
459 ScrollStyle {
460 thumb_style: Some(self.container_border()),
461 track_style: Some(self.container_border()),
462 min_style: Some(self.container_border()),
463 begin_style: Some(self.container_arrow()),
464 end_style: Some(self.container_arrow()),
465 ..Default::default()
466 }
467 }
468
469 pub fn popup_scroll_style(&self) -> ScrollStyle {
471 ScrollStyle {
472 thumb_style: Some(self.popup_border()),
473 track_style: Some(self.popup_border()),
474 min_style: Some(self.popup_border()),
475 begin_style: Some(self.popup_arrow()),
476 end_style: Some(self.popup_arrow()),
477 ..Default::default()
478 }
479 }
480
481 pub fn dialog_scroll_style(&self) -> ScrollStyle {
483 ScrollStyle {
484 thumb_style: Some(self.dialog_border()),
485 track_style: Some(self.dialog_border()),
486 min_style: Some(self.dialog_border()),
487 begin_style: Some(self.dialog_arrow()),
488 end_style: Some(self.dialog_arrow()),
489 ..Default::default()
490 }
491 }
492
493 pub fn split_style(&self) -> SplitStyle {
495 SplitStyle {
496 style: self.container_border(),
497 arrow_style: Some(self.container_arrow()),
498 drag_style: Some(self.focus()),
499 ..Default::default()
500 }
501 }
502
503 pub fn view_style(&self) -> ViewStyle {
505 ViewStyle {
506 scroll: Some(self.scroll_style()),
507 ..Default::default()
508 }
509 }
510
511 pub fn tabbed_style(&self) -> TabbedStyle {
513 let style = self.high_style(self.p.black[1]);
514 TabbedStyle {
515 style,
516 tab: Some(self.gray(1)),
517 select: Some(self.style(self.p.primary[4])),
518 focus: Some(self.focus()),
519 ..Default::default()
520 }
521 }
522
523 pub fn statusline_style(&self) -> Vec<Style> {
528 vec![
529 self.status_base(),
530 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
531 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
532 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
533 ]
534 }
535
536 pub fn statusline_style_ext(&self) -> StatusLineStyle {
538 StatusLineStyle {
539 sep: Some(Cow::Borrowed("|")),
540 styles: vec![
541 self.status_base(),
542 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
543 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
544 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
545 ],
546 ..Default::default()
547 }
548 }
549
550 pub fn file_dialog_style(&self) -> FileDialogStyle {
552 FileDialogStyle {
553 style: self.dialog_base(),
554 list: Some(self.list_style()),
555 roots: Some(ListStyle {
556 style: self.dialog_base(),
557 ..self.list_style()
558 }),
559 text: Some(self.text_style()),
560 button: Some(self.button_style()),
561 block: Some(Block::bordered()),
562 ..Default::default()
563 }
564 }
565
566 pub fn msg_dialog_style(&self) -> MsgDialogStyle {
568 MsgDialogStyle {
569 style: self.dialog_base(),
570 button: Some(self.button_style()),
571 ..Default::default()
572 }
573 }
574
575 pub fn clipper_style(&self) -> ClipperStyle {
577 ClipperStyle {
578 style: self.container_base(),
579 scroll: Some(self.scroll_style()),
580 ..Default::default()
581 }
582 }
583}