1use crate::{Contrast, Palette};
6use rat_widget::button::ButtonStyle;
7use rat_widget::calendar::CalendarStyle;
8use rat_widget::caption::{CaptionStyle, HotkeyAlignment, HotkeyPolicy};
9use rat_widget::checkbox::CheckboxStyle;
10use rat_widget::choice::ChoiceStyle;
11use rat_widget::clipper::ClipperStyle;
12use rat_widget::file_dialog::FileDialogStyle;
13use rat_widget::line_number::LineNumberStyle;
14use rat_widget::list::ListStyle;
15use rat_widget::menu::MenuStyle;
16use rat_widget::msgdialog::MsgDialogStyle;
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::splitter::SplitStyle;
24use rat_widget::tabbed::TabbedStyle;
25use rat_widget::table::TableStyle;
26use rat_widget::text::TextStyle;
27use rat_widget::view::ViewStyle;
28use ratatui::layout::Alignment;
29use ratatui::style::Color;
30use ratatui::style::{Style, Stylize};
31use ratatui::widgets::{Block, Borders};
32use std::time::Duration;
33
34#[derive(Debug, Clone)]
42pub struct DarkTheme {
43 p: Palette,
44 name: String,
45}
46
47impl DarkTheme {
48 pub fn new(name: String, 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.style(self.p.white[n], Contrast::Normal)
83 }
84
85 pub fn black(&self, n: usize) -> Style {
88 self.p.style(self.p.black[n], Contrast::Normal)
89 }
90
91 pub fn gray(&self, n: usize) -> Style {
94 self.p.style(self.p.gray[n], Contrast::Normal)
95 }
96
97 pub fn red(&self, n: usize) -> Style {
100 self.p.style(self.p.red[n], Contrast::Normal)
101 }
102
103 pub fn orange(&self, n: usize) -> Style {
106 self.p.style(self.p.orange[n], Contrast::Normal)
107 }
108
109 pub fn yellow(&self, n: usize) -> Style {
112 self.p.style(self.p.yellow[n], Contrast::Normal)
113 }
114
115 pub fn limegreen(&self, n: usize) -> Style {
118 self.p.style(self.p.limegreen[n], Contrast::Normal)
119 }
120
121 pub fn green(&self, n: usize) -> Style {
124 self.p.style(self.p.green[n], Contrast::Normal)
125 }
126
127 pub fn bluegreen(&self, n: usize) -> Style {
130 self.p.style(self.p.bluegreen[n], Contrast::Normal)
131 }
132
133 pub fn cyan(&self, n: usize) -> Style {
136 self.p.style(self.p.cyan[n], Contrast::Normal)
137 }
138
139 pub fn blue(&self, n: usize) -> Style {
142 self.p.style(self.p.blue[n], Contrast::Normal)
143 }
144
145 pub fn deepblue(&self, n: usize) -> Style {
148 self.p.style(self.p.deepblue[n], Contrast::Normal)
149 }
150
151 pub fn purple(&self, n: usize) -> Style {
154 self.p.style(self.p.purple[n], Contrast::Normal)
155 }
156
157 pub fn magenta(&self, n: usize) -> Style {
160 self.p.style(self.p.magenta[n], Contrast::Normal)
161 }
162
163 pub fn redpink(&self, n: usize) -> Style {
166 self.p.style(self.p.redpink[n], Contrast::Normal)
167 }
168
169 pub fn primary(&self, n: usize) -> Style {
172 self.p.style(self.p.primary[n], Contrast::Normal)
173 }
174
175 pub fn secondary(&self, n: usize) -> Style {
178 self.p.style(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 caption_style(&self) -> CaptionStyle {
338 CaptionStyle {
339 style: self.container_base(),
340 hover: Some(self.secondary(0)),
341 hotkey: Some(self.secondary(3)),
342 focus: Some(Style::new().fg(self.palette().secondary[2])),
343 hotkey_align: Some(HotkeyAlignment::HotkeyLabel),
344 align: Some(Alignment::Right),
345 hotkey_policy: Some(HotkeyPolicy::OnHover),
346 ..Default::default()
347 }
348 }
349
350 pub fn paragraph_style(&self) -> ParagraphStyle {
351 ParagraphStyle {
352 style: self.container_base(),
353 focus: Some(self.focus()),
354 scroll: Some(self.scroll_style()),
355 ..Default::default()
356 }
357 }
358
359 pub fn choice_style(&self) -> ChoiceStyle {
360 ChoiceStyle {
361 style: self.text_input(),
362 select: Some(self.text_select()),
363 focus: Some(self.text_focus()),
364 popup: PopupStyle {
365 style: self.popup_base(),
366 scroll: Some(self.popup_scroll_style()),
367 block: Some(
368 Block::bordered()
369 .borders(Borders::LEFT)
370 .border_style(self.popup_arrow()),
371 ),
372 ..Default::default()
373 },
374 ..Default::default()
375 }
376 }
377
378 pub fn radio_style(&self) -> RadioStyle {
379 RadioStyle {
380 layout: Some(RadioLayout::Stacked),
381 style: self.text_input(),
382 focus: Some(self.text_focus()),
383 ..Default::default()
384 }
385 }
386
387 pub fn checkbox_style(&self) -> CheckboxStyle {
389 CheckboxStyle {
390 style: self.text_input(),
391 focus: Some(self.text_focus()),
392 ..Default::default()
393 }
394 }
395
396 pub fn menu_style(&self) -> MenuStyle {
398 let menu = Style::default().fg(self.p.white[3]).bg(self.p.black[2]);
399 MenuStyle {
400 style: menu,
401 title: Some(Style::default().fg(self.p.black[0]).bg(self.p.yellow[2])),
402 select: Some(self.select()),
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 armed_delay: Some(Duration::from_millis(50)),
423 ..Default::default()
424 }
425 }
426
427 pub fn table_style(&self) -> TableStyle {
429 TableStyle {
430 style: self.container_base(),
431 select_row: Some(self.select()),
432 show_row_focus: true,
433 focus_style: Some(self.focus()),
434 border_style: Some(self.container_border()),
435 scroll: Some(self.scroll_style()),
436 ..Default::default()
437 }
438 }
439
440 pub fn table_header(&self) -> Style {
441 self.style(self.p.blue[2])
442 }
443
444 pub fn table_footer(&self) -> Style {
445 self.style(self.p.blue[2])
446 }
447
448 pub fn list_style(&self) -> ListStyle {
450 ListStyle {
451 style: self.container_base(),
452 select: Some(self.select()),
453 focus: Some(self.focus()),
454 scroll: Some(self.scroll_style()),
455 ..Default::default()
456 }
457 }
458
459 pub fn scroll_style(&self) -> ScrollStyle {
461 ScrollStyle {
462 thumb_style: Some(self.container_border()),
463 track_style: Some(self.container_border()),
464 min_style: Some(self.container_border()),
465 begin_style: Some(self.container_arrow()),
466 end_style: Some(self.container_arrow()),
467 ..Default::default()
468 }
469 }
470
471 pub fn popup_scroll_style(&self) -> ScrollStyle {
473 ScrollStyle {
474 thumb_style: Some(self.popup_border()),
475 track_style: Some(self.popup_border()),
476 min_style: Some(self.popup_border()),
477 begin_style: Some(self.popup_arrow()),
478 end_style: Some(self.popup_arrow()),
479 ..Default::default()
480 }
481 }
482
483 pub fn dialog_scroll_style(&self) -> ScrollStyle {
485 ScrollStyle {
486 thumb_style: Some(self.dialog_border()),
487 track_style: Some(self.dialog_border()),
488 min_style: Some(self.dialog_border()),
489 begin_style: Some(self.dialog_arrow()),
490 end_style: Some(self.dialog_arrow()),
491 ..Default::default()
492 }
493 }
494
495 pub fn split_style(&self) -> SplitStyle {
497 SplitStyle {
498 style: self.container_border(),
499 arrow_style: Some(self.container_arrow()),
500 drag_style: Some(self.focus()),
501 ..Default::default()
502 }
503 }
504
505 pub fn view_style(&self) -> ViewStyle {
507 ViewStyle {
508 scroll: Some(self.scroll_style()),
509 ..Default::default()
510 }
511 }
512
513 pub fn tabbed_style(&self) -> TabbedStyle {
515 let style = self.high_style(self.p.black[1]);
516 TabbedStyle {
517 style,
518 tab: Some(self.gray(1)),
519 select: Some(self.gray(3)),
520 focus: Some(self.focus()),
521 ..Default::default()
522 }
523 }
524
525 pub fn statusline_style(&self) -> Vec<Style> {
530 vec![
531 self.status_base(),
532 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[3]),
533 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[2]),
534 self.p.normal_contrast(self.p.white[0]).bg(self.p.blue[1]),
535 ]
536 }
537
538 pub fn file_dialog_style(&self) -> FileDialogStyle {
540 FileDialogStyle {
541 style: self.dialog_base(),
542 list: Some(self.list_style()),
543 roots: Some(ListStyle {
544 style: self.dialog_base(),
545 ..self.list_style()
546 }),
547 text: Some(self.text_style()),
548 button: Some(self.button_style()),
549 block: Some(Block::bordered()),
550 ..Default::default()
551 }
552 }
553
554 pub fn msg_dialog_style(&self) -> MsgDialogStyle {
556 MsgDialogStyle {
557 style: self.dialog_base(),
558 button: Some(self.button_style()),
559 ..Default::default()
560 }
561 }
562
563 pub fn pager_style(&self) -> PagerStyle {
565 PagerStyle {
566 style: self.container_base(),
567 navigation: Some(self.container_arrow()),
568 block: Some(
569 Block::bordered()
570 .borders(Borders::TOP | Borders::BOTTOM)
571 .border_style(self.container_border()),
572 ),
573 ..Default::default()
574 }
575 }
576
577 pub fn clipper_style(&self) -> ClipperStyle {
579 ClipperStyle {
580 style: self.container_base(),
581 scroll: Some(self.scroll_style()),
582 ..Default::default()
583 }
584 }
585}