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 block: Some(Block::bordered()),
404 popup: Default::default(),
405 ..Default::default()
406 }
407 }
408
409 pub fn button_style(&self) -> ButtonStyle {
411 ButtonStyle {
412 style: self.button_base(),
413 focus: Some(self.focus()),
414 armed: Some(self.select()),
415 hover: Some(self.select()),
416 armed_delay: Some(Duration::from_millis(50)),
417 ..Default::default()
418 }
419 }
420
421 pub fn table_style(&self) -> TableStyle {
423 TableStyle {
424 style: self.container_base(),
425 select_row: Some(self.select()),
426 show_row_focus: true,
427 focus_style: Some(self.focus()),
428 border_style: Some(self.container_border()),
429 scroll: Some(self.scroll_style()),
430 ..Default::default()
431 }
432 }
433
434 pub fn table_header(&self) -> Style {
435 self.style(self.p.blue[2])
436 }
437
438 pub fn table_footer(&self) -> Style {
439 self.style(self.p.blue[2])
440 }
441
442 pub fn list_style(&self) -> ListStyle {
444 ListStyle {
445 style: self.container_base(),
446 select: Some(self.select()),
447 focus: Some(self.focus()),
448 scroll: Some(self.scroll_style()),
449 ..Default::default()
450 }
451 }
452
453 pub fn scroll_style(&self) -> ScrollStyle {
455 ScrollStyle {
456 thumb_style: Some(self.container_border()),
457 track_style: Some(self.container_border()),
458 min_style: Some(self.container_border()),
459 begin_style: Some(self.container_arrow()),
460 end_style: Some(self.container_arrow()),
461 ..Default::default()
462 }
463 }
464
465 pub fn popup_scroll_style(&self) -> ScrollStyle {
467 ScrollStyle {
468 thumb_style: Some(self.popup_border()),
469 track_style: Some(self.popup_border()),
470 min_style: Some(self.popup_border()),
471 begin_style: Some(self.popup_arrow()),
472 end_style: Some(self.popup_arrow()),
473 ..Default::default()
474 }
475 }
476
477 pub fn dialog_scroll_style(&self) -> ScrollStyle {
479 ScrollStyle {
480 thumb_style: Some(self.dialog_border()),
481 track_style: Some(self.dialog_border()),
482 min_style: Some(self.dialog_border()),
483 begin_style: Some(self.dialog_arrow()),
484 end_style: Some(self.dialog_arrow()),
485 ..Default::default()
486 }
487 }
488
489 pub fn split_style(&self) -> SplitStyle {
491 SplitStyle {
492 style: self.container_border(),
493 arrow_style: Some(self.container_arrow()),
494 drag_style: Some(self.focus()),
495 ..Default::default()
496 }
497 }
498
499 pub fn view_style(&self) -> ViewStyle {
501 ViewStyle {
502 scroll: Some(self.scroll_style()),
503 ..Default::default()
504 }
505 }
506
507 pub fn tabbed_style(&self) -> TabbedStyle {
509 let style = self.high_style(self.p.black[1]);
510 TabbedStyle {
511 style,
512 tab: Some(self.gray(1)),
513 select: Some(self.style(self.p.primary[4])),
514 focus: Some(self.focus()),
515 ..Default::default()
516 }
517 }
518
519 pub fn statusline_style(&self) -> Vec<Style> {
524 vec![
525 self.status_base(),
526 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
527 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
528 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
529 ]
530 }
531
532 pub fn statusline_style_ext(&self) -> StatusLineStyle {
534 StatusLineStyle {
535 sep: Some(Cow::Borrowed("|")),
536 styles: vec![
537 self.status_base(),
538 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
539 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
540 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
541 ],
542 ..Default::default()
543 }
544 }
545
546 pub fn file_dialog_style(&self) -> FileDialogStyle {
548 FileDialogStyle {
549 style: self.dialog_base(),
550 list: Some(self.list_style()),
551 roots: Some(ListStyle {
552 style: self.dialog_base(),
553 ..self.list_style()
554 }),
555 text: Some(self.text_style()),
556 button: Some(self.button_style()),
557 block: Some(Block::bordered()),
558 ..Default::default()
559 }
560 }
561
562 pub fn msg_dialog_style(&self) -> MsgDialogStyle {
564 MsgDialogStyle {
565 style: self.dialog_base(),
566 button: Some(self.button_style()),
567 ..Default::default()
568 }
569 }
570
571 pub fn clipper_style(&self) -> ClipperStyle {
573 ClipperStyle {
574 style: self.container_base(),
575 scroll: Some(self.scroll_style()),
576 ..Default::default()
577 }
578 }
579}