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;
16#[allow(deprecated)]
17use rat_widget::pager::PagerStyle;
18use rat_widget::paragraph::ParagraphStyle;
19use rat_widget::popup::PopupStyle;
20use rat_widget::radio::{RadioLayout, RadioStyle};
21use rat_widget::scrolled::ScrollStyle;
22use rat_widget::shadow::{ShadowDirection, ShadowStyle};
23use rat_widget::slider::SliderStyle;
24use rat_widget::splitter::SplitStyle;
25use rat_widget::statusline::StatusLineStyle;
26use rat_widget::tabbed::TabbedStyle;
27use rat_widget::table::TableStyle;
28use rat_widget::text::TextStyle;
29use rat_widget::view::ViewStyle;
30use ratatui::layout::Alignment;
31use ratatui::style::Color;
32use ratatui::style::{Style, Stylize};
33use ratatui::widgets::{Block, Borders};
34use std::borrow::Cow;
35use std::time::Duration;
36
37#[derive(Debug, Clone)]
45pub struct DarkTheme {
46 p: Palette,
47 name: &'static str,
48}
49
50impl DarkTheme {
51 pub const fn new(name: &'static str, s: Palette) -> Self {
52 Self { p: s, name }
53 }
54}
55
56impl DarkTheme {
57 pub fn name(&self) -> &str {
59 &self.name
60 }
61
62 pub fn dark_theme(&self) -> bool {
64 true
65 }
66
67 pub fn palette(&self) -> &Palette {
69 &self.p
70 }
71
72 pub fn style(&self, bg: Color) -> Style {
74 self.p.style(bg, Contrast::Normal)
75 }
76
77 pub fn high_style(&self, bg: Color) -> Style {
79 self.p.style(bg, Contrast::High)
80 }
81
82 pub fn white(&self, n: usize) -> Style {
85 self.p.white(n, Contrast::Normal)
86 }
87
88 pub fn black(&self, n: usize) -> Style {
91 self.p.black(n, Contrast::Normal)
92 }
93
94 pub fn gray(&self, n: usize) -> Style {
97 self.p.gray(n, Contrast::Normal)
98 }
99
100 pub fn red(&self, n: usize) -> Style {
103 self.p.red(n, Contrast::Normal)
104 }
105
106 pub fn orange(&self, n: usize) -> Style {
109 self.p.orange(n, Contrast::Normal)
110 }
111
112 pub fn yellow(&self, n: usize) -> Style {
115 self.p.yellow(n, Contrast::Normal)
116 }
117
118 pub fn limegreen(&self, n: usize) -> Style {
121 self.p.limegreen(n, Contrast::Normal)
122 }
123
124 pub fn green(&self, n: usize) -> Style {
127 self.p.green(n, Contrast::Normal)
128 }
129
130 pub fn bluegreen(&self, n: usize) -> Style {
133 self.p.bluegreen(n, Contrast::Normal)
134 }
135
136 pub fn cyan(&self, n: usize) -> Style {
139 self.p.cyan(n, Contrast::Normal)
140 }
141
142 pub fn blue(&self, n: usize) -> Style {
145 self.p.blue(n, Contrast::Normal)
146 }
147
148 pub fn deepblue(&self, n: usize) -> Style {
151 self.p.deepblue(n, Contrast::Normal)
152 }
153
154 pub fn purple(&self, n: usize) -> Style {
157 self.p.purple(n, Contrast::Normal)
158 }
159
160 pub fn magenta(&self, n: usize) -> Style {
163 self.p.magenta(n, Contrast::Normal)
164 }
165
166 pub fn redpink(&self, n: usize) -> Style {
169 self.p.redpink(n, Contrast::Normal)
170 }
171
172 pub fn primary(&self, n: usize) -> Style {
175 self.p.primary(n, Contrast::Normal)
176 }
177
178 pub fn secondary(&self, n: usize) -> Style {
181 self.p.secondary(n, Contrast::Normal)
182 }
183
184 pub fn focus(&self) -> Style {
186 self.high_style(self.p.primary[2])
187 }
188
189 pub fn select(&self) -> Style {
191 self.high_style(self.p.secondary[1])
192 }
193
194 pub fn text_input(&self) -> Style {
196 self.high_style(self.p.gray[3])
197 }
198
199 pub fn text_focus(&self) -> Style {
201 self.high_style(self.p.primary[1])
202 }
203
204 pub fn text_select(&self) -> Style {
206 self.high_style(self.p.secondary[1])
207 }
208
209 pub fn container_base(&self) -> Style {
211 self.style(self.p.black[1])
212 }
213
214 pub fn container_border(&self) -> Style {
216 self.container_base().fg(self.p.gray[0])
217 }
218
219 pub fn container_arrow(&self) -> Style {
221 self.container_base().fg(self.p.gray[0])
222 }
223
224 pub fn popup_base(&self) -> Style {
226 self.style(self.p.white[0])
227 }
228
229 pub fn popup_label(&self) -> Style {
231 self.style(self.p.white[0])
232 }
233
234 pub fn popup_border(&self) -> Style {
236 self.popup_base().fg(self.p.gray[0])
237 }
238
239 pub fn popup_arrow(&self) -> Style {
241 self.popup_base().fg(self.p.gray[0])
242 }
243
244 pub fn dialog_base(&self) -> Style {
246 self.style(self.p.gray[1])
247 }
248
249 pub fn dialog_label(&self) -> Style {
251 self.dialog_base()
252 }
253
254 pub fn dialog_border(&self) -> Style {
256 self.dialog_base().fg(self.p.white[0])
257 }
258
259 pub fn dialog_arrow(&self) -> Style {
261 self.dialog_base().fg(self.p.white[0])
262 }
263
264 pub fn status_base(&self) -> Style {
266 self.style(self.p.black[2])
267 }
268
269 pub fn button_base(&self) -> Style {
271 self.style(self.p.gray[2])
272 }
273
274 pub fn button_armed(&self) -> Style {
276 self.style(self.p.secondary[0])
277 }
278
279 pub fn month_style(&self) -> CalendarStyle {
281 CalendarStyle {
282 style: self.style(self.p.black[2]),
283 title: None,
284 weeknum: Some(Style::new().fg(self.p.limegreen[2])),
285 weekday: Some(Style::new().fg(self.p.limegreen[2])),
286 day: None,
287 select: Some(self.select()),
288 focus: Some(self.focus()),
289 ..CalendarStyle::default()
290 }
291 }
292
293 pub fn shadow_style(&self) -> ShadowStyle {
295 ShadowStyle {
296 style: Style::new().bg(self.p.black[0]),
297 dir: ShadowDirection::BottomRight,
298 ..ShadowStyle::default()
299 }
300 }
301
302 pub fn line_nr_style(&self) -> LineNumberStyle {
304 LineNumberStyle {
305 style: self.container_base().fg(self.p.gray[1]),
306 cursor: Some(self.text_select()),
307 ..LineNumberStyle::default()
308 }
309 }
310
311 pub fn textarea_style(&self) -> TextStyle {
313 TextStyle {
314 style: self.container_base(),
315 focus: Some(self.focus()),
316 select: Some(self.text_select()),
317 scroll: Some(self.scroll_style()),
318 border_style: Some(self.container_border()),
319 ..TextStyle::default()
320 }
321 }
322
323 pub fn text_style(&self) -> TextStyle {
325 TextStyle {
326 style: self.text_input(),
327 focus: Some(self.text_focus()),
328 select: Some(self.text_select()),
329 invalid: Some(Style::default().bg(self.p.red[3])),
330 ..TextStyle::default()
331 }
332 }
333
334 pub fn label_style(&self) -> Style {
336 self.container_base()
337 }
338
339 pub fn paragraph_style(&self) -> ParagraphStyle {
340 ParagraphStyle {
341 style: self.container_base(),
342 focus: Some(self.focus()),
343 scroll: Some(self.scroll_style()),
344 ..Default::default()
345 }
346 }
347
348 #[allow(deprecated)]
349 pub fn choice_style(&self) -> ChoiceStyle {
350 ChoiceStyle {
351 style: self.text_input(),
352 select: Some(self.text_select()),
353 focus: Some(self.text_focus()),
354 popup_style: Some(self.popup_base()),
355 popup_border: Some(self.popup_border()),
356 popup_scroll: Some(self.popup_scroll_style()),
357 popup_block: Some(
358 Block::bordered()
359 .borders(Borders::LEFT)
360 .border_style(self.popup_border()),
361 ),
362 ..Default::default()
363 }
364 }
365
366 pub fn radio_style(&self) -> RadioStyle {
367 RadioStyle {
368 layout: Some(RadioLayout::Stacked),
369 style: self.text_input(),
370 focus: Some(self.text_focus()),
371 ..Default::default()
372 }
373 }
374
375 pub fn checkbox_style(&self) -> CheckboxStyle {
377 CheckboxStyle {
378 style: self.text_input(),
379 focus: Some(self.text_focus()),
380 ..Default::default()
381 }
382 }
383
384 pub fn slider_style(&self) -> SliderStyle {
386 SliderStyle {
387 style: self.text_input(),
388 bounds: Some(self.gray(2)),
389 knob: Some(self.select()),
390 focus: Some(self.focus()),
391 text_align: Some(Alignment::Center),
392 ..Default::default()
393 }
394 }
395
396 #[allow(deprecated)]
398 pub fn menu_style(&self) -> MenuStyle {
399 let menu = Style::default().fg(self.p.white[3]).bg(self.p.black[2]);
400 MenuStyle {
401 style: menu,
402 title: Some(Style::default().fg(self.p.black[0]).bg(self.p.yellow[2])),
403 focus: Some(self.focus()),
404 right: Some(Style::default().fg(self.p.bluegreen[0])),
405 disabled: Some(Style::default().fg(self.p.gray[0])),
406 highlight: Some(Style::default().underlined()),
407 popup: PopupStyle {
408 style: menu,
409 block: Some(Block::bordered()),
410 ..Default::default()
411 },
412 ..Default::default()
413 }
414 }
415
416 pub fn button_style(&self) -> ButtonStyle {
418 ButtonStyle {
419 style: self.button_base(),
420 focus: Some(self.focus()),
421 armed: Some(self.select()),
422 hover: Some(self.select()),
423 armed_delay: Some(Duration::from_millis(50)),
424 ..Default::default()
425 }
426 }
427
428 pub fn table_style(&self) -> TableStyle {
430 TableStyle {
431 style: self.container_base(),
432 select_row: Some(self.select()),
433 show_row_focus: true,
434 focus_style: Some(self.focus()),
435 border_style: Some(self.container_border()),
436 scroll: Some(self.scroll_style()),
437 ..Default::default()
438 }
439 }
440
441 pub fn table_header(&self) -> Style {
442 self.style(self.p.blue[2])
443 }
444
445 pub fn table_footer(&self) -> Style {
446 self.style(self.p.blue[2])
447 }
448
449 pub fn list_style(&self) -> ListStyle {
451 ListStyle {
452 style: self.container_base(),
453 select: Some(self.select()),
454 focus: Some(self.focus()),
455 scroll: Some(self.scroll_style()),
456 ..Default::default()
457 }
458 }
459
460 pub fn scroll_style(&self) -> ScrollStyle {
462 ScrollStyle {
463 thumb_style: Some(self.container_border()),
464 track_style: Some(self.container_border()),
465 min_style: Some(self.container_border()),
466 begin_style: Some(self.container_arrow()),
467 end_style: Some(self.container_arrow()),
468 ..Default::default()
469 }
470 }
471
472 pub fn popup_scroll_style(&self) -> ScrollStyle {
474 ScrollStyle {
475 thumb_style: Some(self.popup_border()),
476 track_style: Some(self.popup_border()),
477 min_style: Some(self.popup_border()),
478 begin_style: Some(self.popup_arrow()),
479 end_style: Some(self.popup_arrow()),
480 ..Default::default()
481 }
482 }
483
484 pub fn dialog_scroll_style(&self) -> ScrollStyle {
486 ScrollStyle {
487 thumb_style: Some(self.dialog_border()),
488 track_style: Some(self.dialog_border()),
489 min_style: Some(self.dialog_border()),
490 begin_style: Some(self.dialog_arrow()),
491 end_style: Some(self.dialog_arrow()),
492 ..Default::default()
493 }
494 }
495
496 pub fn split_style(&self) -> SplitStyle {
498 SplitStyle {
499 style: self.container_border(),
500 arrow_style: Some(self.container_arrow()),
501 drag_style: Some(self.focus()),
502 ..Default::default()
503 }
504 }
505
506 pub fn view_style(&self) -> ViewStyle {
508 ViewStyle {
509 scroll: Some(self.scroll_style()),
510 ..Default::default()
511 }
512 }
513
514 pub fn tabbed_style(&self) -> TabbedStyle {
516 let style = self.high_style(self.p.black[1]);
517 TabbedStyle {
518 style,
519 tab: Some(self.gray(1)),
520 select: Some(self.style(self.p.primary[4])),
521 focus: Some(self.focus()),
522 ..Default::default()
523 }
524 }
525
526 pub fn statusline_style(&self) -> Vec<Style> {
531 vec![
532 self.status_base(),
533 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
534 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
535 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
536 ]
537 }
538
539 pub fn statusline_style_ext(&self) -> StatusLineStyle {
541 StatusLineStyle {
542 sep: Some(Cow::Borrowed("|")),
543 styles: vec![
544 self.status_base(),
545 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
546 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
547 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
548 ],
549 ..Default::default()
550 }
551 }
552
553 pub fn file_dialog_style(&self) -> FileDialogStyle {
555 FileDialogStyle {
556 style: self.dialog_base(),
557 list: Some(self.list_style()),
558 roots: Some(ListStyle {
559 style: self.dialog_base(),
560 ..self.list_style()
561 }),
562 text: Some(self.text_style()),
563 button: Some(self.button_style()),
564 block: Some(Block::bordered()),
565 ..Default::default()
566 }
567 }
568
569 pub fn msg_dialog_style(&self) -> MsgDialogStyle {
571 MsgDialogStyle {
572 style: self.dialog_base(),
573 button: Some(self.button_style()),
574 ..Default::default()
575 }
576 }
577
578 #[allow(deprecated)]
580 pub fn pager_style(&self) -> PagerStyle {
581 PagerStyle {
582 style: self.container_base(),
583 navigation: Some(self.container_arrow()),
584 block: Some(
585 Block::bordered()
586 .borders(Borders::TOP | Borders::BOTTOM)
587 .border_style(self.container_border()),
588 ),
589 ..Default::default()
590 }
591 }
592
593 pub fn clipper_style(&self) -> ClipperStyle {
595 ClipperStyle {
596 style: self.container_base(),
597 scroll: Some(self.scroll_style()),
598 ..Default::default()
599 }
600 }
601}