1use crate::{
2 channels::prototypes::Template,
3 config::themes::DEFAULT_THEME,
4 screen::layout::{InputPosition, Orientation},
5};
6use serde::{Deserialize, Serialize};
7
8pub const DEFAULT_UI_SCALE: u16 = 100;
9pub const DEFAULT_PREVIEW_SIZE: u16 = 50;
10pub const DEFAULT_PROMPT: &str = ">";
11
12#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash)]
13#[serde(default)]
14pub struct InputBarConfig {
15 pub position: InputPosition,
16 pub header: Option<String>,
17 #[serde(default = "default_prompt")]
18 pub prompt: Option<String>,
19 pub border_type: BorderType,
20 pub padding: Padding,
21}
22
23impl Default for InputBarConfig {
24 fn default() -> Self {
25 Self {
26 position: InputPosition::default(),
27 header: None,
28 prompt: Some(String::from(DEFAULT_PROMPT)),
29 border_type: BorderType::default(),
30 padding: Padding::uniform(0),
31 }
32 }
33}
34
35#[allow(clippy::unnecessary_wraps)]
36fn default_prompt() -> Option<String> {
37 Some(String::from(DEFAULT_PROMPT))
38}
39
40#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash, Default)]
41#[serde(default)]
42pub struct StatusBarConfig {
43 pub separator_open: String,
44 pub separator_close: String,
45 pub hidden: bool,
46}
47
48#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash, Default)]
49#[serde(default)]
50pub struct ResultsPanelConfig {
51 pub border_type: BorderType,
52 pub padding: Padding,
53}
54
55#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash)]
56#[serde(default)]
57pub struct PreviewPanelConfig {
58 pub size: u16,
59 pub header: Option<Template>,
60 pub footer: Option<Template>,
61 pub scrollbar: bool,
62 pub border_type: BorderType,
63 pub padding: Padding,
64 pub word_wrap: bool,
65 pub hidden: bool,
66}
67
68impl Default for PreviewPanelConfig {
69 fn default() -> Self {
70 Self {
71 size: DEFAULT_PREVIEW_SIZE,
72 header: None,
73 footer: None,
74 scrollbar: true,
75 border_type: BorderType::default(),
76 padding: Padding::uniform(0),
77 hidden: false,
78 word_wrap: false,
79 }
80 }
81}
82
83#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash)]
84#[serde(default)]
85pub struct HelpPanelConfig {
86 pub show_categories: bool,
87 #[serde(default = "default_help_hidden")]
88 pub hidden: bool,
89 pub disabled: bool,
90}
91
92fn default_help_hidden() -> bool {
93 true
94}
95
96impl Default for HelpPanelConfig {
97 fn default() -> Self {
98 Self {
99 show_categories: true,
100 hidden: true,
101 disabled: false,
102 }
103 }
104}
105
106#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash)]
107#[serde(default)]
108pub struct RemoteControlConfig {
109 pub show_channel_descriptions: bool,
110 pub sort_alphabetically: bool,
111 pub disabled: bool,
112}
113
114impl Default for RemoteControlConfig {
115 fn default() -> Self {
116 Self {
117 show_channel_descriptions: true,
118 sort_alphabetically: true,
119 disabled: false,
120 }
121 }
122}
123
124#[derive(
127 Clone, Debug, Deserialize, Serialize, PartialEq, Eq, Hash, Default,
128)]
129#[serde(default)]
130pub struct ThemeOverrides {
131 pub background: Option<String>,
133 pub border_fg: Option<String>,
134 pub text_fg: Option<String>,
135 pub dimmed_text_fg: Option<String>,
136
137 pub input_text_fg: Option<String>,
139 pub result_count_fg: Option<String>,
140
141 pub result_name_fg: Option<String>,
143 pub result_line_number_fg: Option<String>,
144 pub result_value_fg: Option<String>,
145 pub selection_bg: Option<String>,
146 pub selection_fg: Option<String>,
147 pub match_fg: Option<String>,
148
149 pub preview_title_fg: Option<String>,
151
152 pub channel_mode_fg: Option<String>,
154 pub channel_mode_bg: Option<String>,
155 pub remote_control_mode_fg: Option<String>,
156 pub remote_control_mode_bg: Option<String>,
157 pub action_picker_mode_fg: Option<String>,
158 pub action_picker_mode_bg: Option<String>,
159}
160
161impl ThemeOverrides {
162 pub fn merge(self, other: Self) -> Self {
163 Self {
164 background: self.background.or(other.background),
165 border_fg: self.border_fg.or(other.border_fg),
166 text_fg: self.text_fg.or(other.text_fg),
167 dimmed_text_fg: self.dimmed_text_fg.or(other.dimmed_text_fg),
168 input_text_fg: self.input_text_fg.or(other.input_text_fg),
169 result_count_fg: self.result_count_fg.or(other.result_count_fg),
170 result_name_fg: self.result_name_fg.or(other.result_name_fg),
171 result_line_number_fg: self
172 .result_line_number_fg
173 .or(other.result_line_number_fg),
174 result_value_fg: self.result_value_fg.or(other.result_value_fg),
175 selection_bg: self.selection_bg.or(other.selection_bg),
176 selection_fg: self.selection_fg.or(other.selection_fg),
177 match_fg: self.match_fg.or(other.match_fg),
178 preview_title_fg: self.preview_title_fg.or(other.preview_title_fg),
179 channel_mode_fg: self.channel_mode_fg.or(other.channel_mode_fg),
180 channel_mode_bg: self.channel_mode_bg.or(other.channel_mode_bg),
181 remote_control_mode_fg: self
182 .remote_control_mode_fg
183 .or(other.remote_control_mode_fg),
184 remote_control_mode_bg: self
185 .remote_control_mode_bg
186 .or(other.remote_control_mode_bg),
187 action_picker_mode_fg: self
188 .action_picker_mode_fg
189 .or(other.action_picker_mode_fg),
190 action_picker_mode_bg: self
191 .action_picker_mode_bg
192 .or(other.action_picker_mode_bg),
193 }
194 }
195}
196
197#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash)]
198#[serde(default)]
199pub struct UiConfig {
200 pub ui_scale: u16,
201 pub orientation: Orientation,
202 pub theme: String,
203
204 pub input_bar: InputBarConfig,
206 pub status_bar: StatusBarConfig,
207 pub preview_panel: PreviewPanelConfig,
208 pub results_panel: ResultsPanelConfig,
209 pub help_panel: HelpPanelConfig,
210 pub remote_control: RemoteControlConfig,
211
212 #[serde(default)]
214 pub theme_overrides: ThemeOverrides,
215}
216
217impl Default for UiConfig {
218 fn default() -> Self {
219 Self {
220 ui_scale: DEFAULT_UI_SCALE,
221 orientation: Orientation::Landscape,
222 theme: String::from(DEFAULT_THEME),
223 input_bar: InputBarConfig::default(),
224 status_bar: StatusBarConfig::default(),
225 preview_panel: PreviewPanelConfig::default(),
226 results_panel: ResultsPanelConfig::default(),
227 help_panel: HelpPanelConfig::default(),
228 remote_control: RemoteControlConfig::default(),
229 theme_overrides: ThemeOverrides::default(),
230 }
231 }
232}
233
234#[derive(
235 Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Hash, Default, Eq,
236)]
237#[serde(rename_all = "snake_case")]
238pub enum BorderType {
239 None,
240 Plain,
241 #[default]
242 Rounded,
243 Thick,
244}
245
246impl BorderType {
247 pub fn to_ratatui_border_type(
248 &self,
249 ) -> Option<ratatui::widgets::BorderType> {
250 match self {
251 BorderType::None => None,
252 BorderType::Plain => Some(ratatui::widgets::BorderType::Plain),
253 BorderType::Rounded => Some(ratatui::widgets::BorderType::Rounded),
254 BorderType::Thick => Some(ratatui::widgets::BorderType::Thick),
255 }
256 }
257}
258
259impl From<crate::cli::args::BorderType> for BorderType {
260 fn from(border_type: crate::cli::args::BorderType) -> Self {
261 match border_type {
262 crate::cli::args::BorderType::None => BorderType::None,
263 crate::cli::args::BorderType::Plain => BorderType::Plain,
264 crate::cli::args::BorderType::Rounded => BorderType::Rounded,
265 crate::cli::args::BorderType::Thick => BorderType::Thick,
266 }
267 }
268}
269
270#[derive(
271 Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Hash, Default, Eq,
272)]
273#[serde(default)]
274pub struct Padding {
275 pub top: u16,
276 pub bottom: u16,
277 pub left: u16,
278 pub right: u16,
279}
280
281impl Padding {
282 pub fn new(top: u16, bottom: u16, left: u16, right: u16) -> Self {
283 Self {
284 top,
285 bottom,
286 left,
287 right,
288 }
289 }
290
291 pub fn uniform(padding: u16) -> Self {
292 Self {
293 top: padding,
294 bottom: padding,
295 left: padding,
296 right: padding,
297 }
298 }
299
300 pub fn horizontal(padding: u16) -> Self {
301 Self {
302 top: 0,
303 bottom: 0,
304 left: padding,
305 right: padding,
306 }
307 }
308
309 pub fn vertical(padding: u16) -> Self {
310 Self {
311 top: padding,
312 bottom: padding,
313 left: 0,
314 right: 0,
315 }
316 }
317}
318
319impl From<Padding> for ratatui::widgets::Padding {
320 fn from(padding: Padding) -> Self {
321 ratatui::widgets::Padding {
322 top: padding.top,
323 bottom: padding.bottom,
324 left: padding.left,
325 right: padding.right,
326 }
327 }
328}