1use crate::{
2 app::App,
3 constants::SAMPLE_TEXT,
4 ui::inbuilt_themes::{
5 cyberpunk_theme, default_theme, dracula_theme, light_theme, matrix_theme, metro_theme,
6 midnight_blue_theme, slate_theme,
7 },
8};
9use ratatui::{
10 style::{Color, Modifier, Style},
11 text::Span,
12 widgets::{Cell, Row},
13};
14use serde::{Deserialize, Serialize};
15use strum::{Display, EnumIter, IntoEnumIterator};
16
17#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
18pub struct Theme {
19 pub card_due_default_style: Style,
20 pub card_due_overdue_style: Style,
21 pub card_due_warning_style: Style,
22 pub card_priority_high_style: Style,
23 pub card_priority_low_style: Style,
24 pub card_priority_medium_style: Style,
25 pub card_status_active_style: Style,
26 pub card_status_completed_style: Style,
27 pub card_status_stale_style: Style,
28 pub error_text_style: Style,
29 pub general_style: Style,
30 pub help_key_style: Style,
31 pub help_text_style: Style,
32 pub inactive_text_style: Style,
33 pub keyboard_focus_style: Style,
34 pub list_select_style: Style,
35 pub log_debug_style: Style,
36 pub log_error_style: Style,
37 pub log_info_style: Style,
38 pub log_trace_style: Style,
39 pub log_warn_style: Style,
40 pub mouse_focus_style: Style,
41 pub name: String,
42 pub progress_bar_style: Style,
43}
44
45impl Default for Theme {
46 fn default() -> Self {
47 default_theme()
48 }
49}
50
51impl Theme {
52 pub fn all_default_themes() -> Vec<Theme> {
53 vec![
54 cyberpunk_theme(),
55 default_theme(),
56 dracula_theme(),
57 light_theme(),
58 matrix_theme(),
59 metro_theme(),
60 midnight_blue_theme(),
61 slate_theme(),
62 ]
63 }
64
65 pub fn get_style(&self, theme_enum: ThemeEnum) -> Style {
66 match theme_enum {
67 ThemeEnum::Name => self.general_style,
68 ThemeEnum::General => self.general_style,
69 ThemeEnum::ListSelect => self.list_select_style,
70 ThemeEnum::MouseFocus => self.mouse_focus_style,
71 ThemeEnum::CardDueDefault => self.card_due_default_style,
72 ThemeEnum::CardDueOverdue => self.card_due_overdue_style,
73 ThemeEnum::CardDueWarning => self.card_due_warning_style,
74 ThemeEnum::CardPriorityHigh => self.card_priority_high_style,
75 ThemeEnum::CardPriorityLow => self.card_priority_low_style,
76 ThemeEnum::CardPriorityMedium => self.card_priority_medium_style,
77 ThemeEnum::CardStatusActive => self.card_status_active_style,
78 ThemeEnum::CardStatusCompleted => self.card_status_completed_style,
79 ThemeEnum::CardStatusStale => self.card_status_stale_style,
80 ThemeEnum::ProgressBar => self.progress_bar_style,
81 ThemeEnum::ErrorText => self.error_text_style,
82 ThemeEnum::HelpKey => self.help_key_style,
83 ThemeEnum::HelpText => self.help_text_style,
84 ThemeEnum::InactiveText => self.inactive_text_style,
85 ThemeEnum::KeyboardFocus => self.keyboard_focus_style,
86 ThemeEnum::LogDebug => self.log_debug_style,
87 ThemeEnum::LogError => self.log_error_style,
88 ThemeEnum::LogInfo => self.log_info_style,
89 ThemeEnum::LogTrace => self.log_trace_style,
90 ThemeEnum::LogWarn => self.log_warn_style,
91 }
92 }
93
94 pub fn get_mut_style(&mut self, theme_enum: ThemeEnum) -> &mut Style {
95 match theme_enum {
96 ThemeEnum::Name => &mut self.general_style,
97 ThemeEnum::General => &mut self.general_style,
98 ThemeEnum::ListSelect => &mut self.list_select_style,
99 ThemeEnum::MouseFocus => &mut self.mouse_focus_style,
100 ThemeEnum::CardDueDefault => &mut self.card_due_default_style,
101 ThemeEnum::CardDueOverdue => &mut self.card_due_overdue_style,
102 ThemeEnum::CardDueWarning => &mut self.card_due_warning_style,
103 ThemeEnum::CardPriorityHigh => &mut self.card_priority_high_style,
104 ThemeEnum::CardPriorityLow => &mut self.card_priority_low_style,
105 ThemeEnum::CardPriorityMedium => &mut self.card_priority_medium_style,
106 ThemeEnum::CardStatusActive => &mut self.card_status_active_style,
107 ThemeEnum::CardStatusCompleted => &mut self.card_status_completed_style,
108 ThemeEnum::CardStatusStale => &mut self.card_status_stale_style,
109 ThemeEnum::ProgressBar => &mut self.progress_bar_style,
110 ThemeEnum::ErrorText => &mut self.error_text_style,
111 ThemeEnum::HelpKey => &mut self.help_key_style,
112 ThemeEnum::HelpText => &mut self.help_text_style,
113 ThemeEnum::InactiveText => &mut self.inactive_text_style,
114 ThemeEnum::KeyboardFocus => &mut self.keyboard_focus_style,
115 ThemeEnum::LogDebug => &mut self.log_debug_style,
116 ThemeEnum::LogError => &mut self.log_error_style,
117 ThemeEnum::LogInfo => &mut self.log_info_style,
118 ThemeEnum::LogTrace => &mut self.log_trace_style,
119 ThemeEnum::LogWarn => &mut self.log_warn_style,
120 }
121 }
122
123 pub fn name(&self) -> &str {
124 &self.name
125 }
126
127 fn create_styled_spans(&self, app: &App, is_active: bool, sample_mode: bool) -> Vec<Span> {
128 ThemeEnum::iter()
129 .map(|theme_enum| {
130 let text = if sample_mode {
131 if theme_enum == ThemeEnum::Name {
132 self.name.clone()
133 } else {
134 SAMPLE_TEXT.to_string()
135 }
136 } else {
137 theme_enum.to_string()
138 };
139 let style = if !is_active {
140 app.current_theme.inactive_text_style
141 } else if sample_mode {
142 self.get_style(theme_enum)
143 } else {
144 app.current_theme.general_style
145 };
146 Span::styled(text, style)
147 })
148 .collect::<Vec<Span>>()
149 }
150
151 pub fn to_rows(&self, app: &App, is_active: bool) -> (Vec<Row>, Vec<Row>) {
152 let theme_title_list = self.create_styled_spans(app, is_active, false);
153 let theme_style_list = self.create_styled_spans(app, is_active, true);
154
155 let rows_title = theme_title_list
156 .iter()
157 .map(|span| Row::new(vec![Cell::from(span.clone())]))
158 .collect();
159 let rows_elements = theme_style_list
160 .iter()
161 .map(|span| Row::new(vec![Cell::from(span.clone())]))
162 .collect();
163
164 (rows_title, rows_elements)
165 }
166
167 pub fn update_style(
168 style: &mut Style,
169 fg_color: Option<Color>,
170 bg_color: Option<Color>,
171 modifier: Option<Modifier>,
172 ) {
173 if let Some(fg) = fg_color {
174 style.fg = Some(fg);
175 }
176
177 if let Some(bg) = bg_color {
178 style.bg = Some(bg);
179 }
180
181 if let Some(modifier) = modifier {
182 Self::add_modifier_to_style(style, modifier);
183 }
184 }
185
186 pub fn add_modifier_to_style(style: &mut Style, modifier: Modifier) {
187 style.sub_modifier = style.sub_modifier.difference(modifier);
188 style.add_modifier = style.add_modifier.union(modifier);
189 }
190}
191
192#[derive(EnumIter, Display, PartialEq, Clone, Copy)]
193#[strum(serialize_all = "title_case")]
194pub enum ThemeEnum {
195 Name,
196 General,
197 ListSelect,
198 MouseFocus,
199 CardDueDefault,
200 CardDueOverdue,
201 CardDueWarning,
202 CardPriorityHigh,
203 CardPriorityLow,
204 CardPriorityMedium,
205 CardStatusActive,
206 CardStatusCompleted,
207 CardStatusStale,
208 ProgressBar,
209 ErrorText,
210 HelpKey,
211 HelpText,
212 InactiveText,
213 KeyboardFocus,
214 LogDebug,
215 LogError,
216 LogInfo,
217 LogTrace,
218 LogWarn,
219}