Krolyxon
Commit b139f20

rmptuisrcconfigtheme.rs

104 lines · 2.9 KB · Rust

1use std::str::FromStr;
2
3use ratatui::style::{Color, Modifier, Style};
4use serde::{Deserialize, Deserializer, de};
5
6/// Colors used throughout the UI.
7#[derive(Clone, Debug, Deserialize)]
8#[serde(deny_unknown_fields)]
9pub struct Theme {
10 #[serde(deserialize_with = "color")]
11 pub text: Color,
12 #[serde(deserialize_with = "color")]
13 pub dim: Color,
14 #[serde(deserialize_with = "color")]
15 pub border: Color,
16 #[serde(deserialize_with = "color")]
17 pub selection_fg: Color,
18 #[serde(deserialize_with = "color")]
19 pub selection_bg: Color,
20 #[serde(deserialize_with = "color")]
21 pub playing: Color,
22 #[serde(deserialize_with = "color")]
23 pub marked: Color,
24 #[serde(deserialize_with = "color")]
25 pub directory: Color,
26 #[serde(deserialize_with = "color")]
27 pub error: Color,
28 #[serde(deserialize_with = "color")]
29 pub artist: Color,
30 #[serde(deserialize_with = "color")]
31 pub track: Color,
32 #[serde(deserialize_with = "color")]
33 pub album: Color,
34 #[serde(deserialize_with = "color")]
35 pub duration: Color,
36 #[serde(deserialize_with = "color", rename = "progress")]
37 pub progress_filled: Color,
38 #[serde(deserialize_with = "colors")]
39 pub visualizer: Vec<Color>,
40}
41
42impl Theme {
43 pub fn text(&self) -> Style {
44 Style::new().fg(self.text)
45 }
46
47 pub fn dim(&self) -> Style {
48 Style::new().fg(self.dim)
49 }
50
51 pub fn border(&self) -> Style {
52 Style::new().fg(self.border)
53 }
54
55 /// Titles and column headers.
56 pub fn header(&self) -> Style {
57 self.text().add_modifier(Modifier::BOLD)
58 }
59
60 /// The selected row of the focused list.
61 pub fn selection(&self) -> Style {
62 if self.selection_fg == Color::Reset && self.selection_bg == Color::Reset {
63 Style::new().add_modifier(Modifier::REVERSED)
64 } else {
65 Style::new().fg(self.selection_fg).bg(self.selection_bg)
66 }
67 }
68
69 /// The selected row of a list without focus.
70 pub fn selection_inactive(&self) -> Style {
71 self.selection().add_modifier(Modifier::DIM)
72 }
73
74 pub fn playing(&self) -> Style {
75 Style::new().fg(self.playing).add_modifier(Modifier::BOLD)
76 }
77
78 pub fn marked(&self) -> Style {
79 Style::new().fg(self.marked)
80 }
81
82 pub fn directory(&self) -> Style {
83 Style::new().fg(self.directory)
84 }
85
86 pub fn error(&self) -> Style {
87 Style::new().fg(self.error)
88 }
89}
90
91fn parse_color<E: de::Error>(value: &str) -> Result<Color, E> {
92 Color::from_str(value).map_err(|_| E::custom(format!("invalid color `{value}`")))
93}
94
95fn color<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Color, D::Error> {
96 parse_color(&String::deserialize(deserializer)?)
97}
98
99fn colors<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<Color>, D::Error> {
100 Vec::<String>::deserialize(deserializer)?
101 .iter()
102 .map(|value| parse_color(value))
103 .collect()
104}