Skip to content

Commit a478a68

Browse files
committed
feat(config): BorderType
1 parent c8da07f commit a478a68

File tree

12 files changed

+209
-141
lines changed

12 files changed

+209
-141
lines changed

.config/config.toml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ use_nerd_font_icons = false
5454
# └───────────────────────────────────────┘
5555
ui_scale = 100
5656

57-
# Where to place the input bar in the UI (top or bottom)
58-
input_bar_position = "top"
59-
# The input prompt string (defaults to ">" if not specified)
60-
input_prompt = ">"
6157
# What orientation should tv be (landscape or portrait)
6258
orientation = "landscape"
6359
# The theme to use for the UI
@@ -100,6 +96,14 @@ remote_control = { enabled = true, visible = false }
10096

10197
# Feature-specific configurations
10298
# Each feature can have its own configuration section
99+
[ui.input_bar]
100+
# Where to place the input bar in the UI (top or bottom)
101+
position = "top"
102+
# The input prompt string (defaults to ">" if not specified)
103+
prompt = ">"
104+
# header = "{}"
105+
border_type = "rounded" # https://docs.rs/ratatui/latest/ratatui/widgets/block/enum.BorderType.html#variants
106+
103107
[ui.status_bar]
104108
# Status bar separators (bubble):
105109
#separator_open = ""
@@ -108,12 +112,16 @@ remote_control = { enabled = true, visible = false }
108112
separator_open = ""
109113
separator_close = ""
110114

115+
[ui.results_panel]
116+
border_type = "rounded"
117+
111118
[ui.preview_panel]
112119
# Preview panel size (percentage of screen width/height)
113120
size = 50
114121
#header = "{}"
115122
#footer = ""
116123
scrollbar = true
124+
border_type = "rounded"
117125

118126
[ui.remote_control]
119127
# Whether to show channel descriptions in remote control mode

television/channels/prototypes.rs

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use crate::cli::parse_source_entry_delimiter;
2+
use crate::config::ui::InputBarConfig;
23
use crate::{
34
config::{Binding, KeyBindings, ui},
45
features::Features,
5-
screen::layout::{InputPosition, Orientation},
6+
screen::layout::Orientation,
67
};
78
use anyhow::Result;
89
use rustc_hash::FxHashMap;
@@ -379,16 +380,14 @@ pub struct UiSpec {
379380
// `layout` is clearer for the user but collides with the overall `Layout` type.
380381
#[serde(rename = "layout", alias = "orientation", default)]
381382
pub orientation: Option<Orientation>,
382-
#[serde(default)]
383-
pub input_bar_position: Option<InputPosition>,
384-
#[serde(default)]
385-
pub input_header: Option<Template>,
386-
#[serde(default)]
387-
pub input_prompt: Option<String>,
388383
// Feature-specific configurations
389384
#[serde(default)]
385+
pub input_bar: Option<InputBarConfig>,
386+
#[serde(default)]
390387
pub preview_panel: Option<ui::PreviewPanelConfig>,
391388
#[serde(default)]
389+
pub results_panel: Option<ui::ResultsPanelConfig>,
390+
#[serde(default)]
392391
pub status_bar: Option<ui::StatusBarConfig>,
393392
#[serde(default)]
394393
pub help_panel: Option<ui::HelpPanelConfig>,
@@ -404,10 +403,9 @@ impl From<&crate::config::UiConfig> for UiSpec {
404403
ui_scale: Some(config.ui_scale),
405404
features: Some(config.features.clone()),
406405
orientation: Some(config.orientation),
407-
input_bar_position: Some(config.input_bar_position),
408-
input_header: config.input_header.clone(),
409-
input_prompt: Some(config.input_prompt.clone()),
406+
input_bar: Some(config.input_bar.clone()),
410407
preview_panel: Some(config.preview_panel.clone()),
408+
results_panel: Some(config.results_panel.clone()),
411409
status_bar: Some(config.status_bar.clone()),
412410
help_panel: Some(config.help_panel.clone()),
413411
remote_control: Some(config.remote_control.clone()),
@@ -417,7 +415,12 @@ impl From<&crate::config::UiConfig> for UiSpec {
417415

418416
#[cfg(test)]
419417
mod tests {
420-
use crate::{action::Action, config::Binding, event::Key};
418+
use crate::{
419+
action::Action,
420+
config::{Binding, ui::BorderType},
421+
event::Key,
422+
screen::layout::InputPosition,
423+
};
421424

422425
use super::*;
423426
use toml::from_str;
@@ -500,16 +503,23 @@ mod tests {
500503
[ui]
501504
layout = "landscape"
502505
ui_scale = 100
503-
input_bar_position = "bottom"
504-
input_header = "Input: {}"
505506
506507
[ui.features]
507508
preview_panel = { enabled = true, visible = true }
508509
510+
[ui.input_bar]
511+
position = "bottom"
512+
header = "Input: {}"
513+
border_type = "plain"
514+
509515
[ui.preview_panel]
510516
size = 66
511517
header = "Preview: {}"
512518
footer = "Press 'q' to quit"
519+
border_type = "thick"
520+
521+
[ui.results_panel]
522+
border_type = "none"
513523
514524
[keybindings]
515525
quit = ["esc", "ctrl-c"]
@@ -552,29 +562,23 @@ mod tests {
552562
assert!(ui.features.is_some());
553563
let features = ui.features.as_ref().unwrap();
554564
assert!(features.preview_panel.enabled);
555-
assert_eq!(ui.input_bar_position, Some(InputPosition::Bottom));
556565
assert_eq!(ui.preview_panel.as_ref().unwrap().size, 66);
557-
assert_eq!(ui.input_header.as_ref().unwrap().raw(), "Input: {}");
566+
let input_bar = ui.input_bar.as_ref().unwrap();
567+
assert_eq!(input_bar.position, InputPosition::Bottom);
568+
assert_eq!(input_bar.header.as_ref().unwrap().raw(), "Input: {}");
569+
assert_eq!(input_bar.border_type, BorderType::Plain);
570+
let preview_panel = ui.preview_panel.as_ref().unwrap();
558571
assert_eq!(
559-
ui.preview_panel
560-
.as_ref()
561-
.unwrap()
562-
.header
563-
.as_ref()
564-
.unwrap()
565-
.raw(),
572+
preview_panel.header.as_ref().unwrap().raw(),
566573
"Preview: {}"
567574
);
568575
assert_eq!(
569-
ui.preview_panel
570-
.as_ref()
571-
.unwrap()
572-
.footer
573-
.as_ref()
574-
.unwrap()
575-
.raw(),
576+
preview_panel.footer.as_ref().unwrap().raw(),
576577
"Press 'q' to quit"
577578
);
579+
assert_eq!(preview_panel.border_type, BorderType::Thick);
580+
581+
assert_eq!(ui.results_panel.unwrap().border_type, BorderType::None);
578582

579583
let keybindings = prototype.keybindings.unwrap();
580584
assert_eq!(
@@ -709,9 +713,8 @@ mod tests {
709713
assert_eq!(ui.orientation, Some(Orientation::Landscape));
710714
assert_eq!(ui.ui_scale, Some(40));
711715
assert!(ui.features.is_none());
712-
assert!(ui.input_bar_position.is_none());
716+
assert!(ui.input_bar.is_none());
713717
assert!(ui.preview_panel.is_some());
714-
assert!(ui.input_header.is_none());
715718
assert_eq!(
716719
ui.preview_panel
717720
.as_ref()

television/config/mod.rs

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,14 @@ impl Config {
243243
if let Some(value) = ui_spec.orientation {
244244
self.ui.orientation = value;
245245
}
246-
if let Some(value) = ui_spec.input_bar_position {
247-
self.ui.input_bar_position = value;
248-
}
249246

250247
// Apply clone fields
251248
if let Some(value) = &ui_spec.features {
252249
self.ui.features = value.clone();
253250
}
251+
if let Some(value) = &ui_spec.input_bar {
252+
self.ui.input_bar = value.clone();
253+
}
254254
if let Some(value) = &ui_spec.status_bar {
255255
self.ui.status_bar = value.clone();
256256
}
@@ -261,16 +261,6 @@ impl Config {
261261
self.ui.remote_control = value.clone();
262262
}
263263

264-
// Apply input_header
265-
if let Some(value) = &ui_spec.input_header {
266-
self.ui.input_header = Some(value.clone());
267-
}
268-
269-
// Apply input_prompt
270-
if let Some(value) = &ui_spec.input_prompt {
271-
self.ui.input_prompt.clone_from(value);
272-
}
273-
274264
// Handle preview_panel with field merging
275265
if let Some(preview_panel) = &ui_spec.preview_panel {
276266
self.ui.preview_panel.size = preview_panel.size;
@@ -487,8 +477,8 @@ mod tests {
487477
}
488478

489479
const USER_CONFIG_INPUT_PROMPT: &str = r#"
490-
[ui]
491-
input_prompt = "❯"
480+
[ui.input_bar]
481+
prompt = "❯"
492482
"#;
493483

494484
#[test]
@@ -507,7 +497,7 @@ mod tests {
507497
let config = Config::new(&config_env, None).unwrap();
508498

509499
// Verify that input_prompt was loaded from user config
510-
assert_eq!(config.ui.input_prompt, "❯");
500+
assert_eq!(config.ui.input_bar.prompt, "❯");
511501
}
512502

513503
#[test]

television/config/themes.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ struct Inner {
232232
// input
233233
input_text_fg: String,
234234
result_count_fg: String,
235-
//results
235+
// results
236236
result_name_fg: String,
237237
result_line_number_fg: String,
238238
result_value_fg: String,
@@ -241,9 +241,9 @@ struct Inner {
241241
// and falls back to match_fg
242242
selection_fg: Option<String>,
243243
match_fg: String,
244-
//preview
244+
// preview
245245
preview_title_fg: String,
246-
//modes
246+
// modes
247247
channel_mode_fg: String,
248248
channel_mode_bg: Option<String>,
249249
remote_control_mode_fg: String,

television/config/ui.rs

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,47 @@ use serde::{Deserialize, Serialize};
99
pub const DEFAULT_UI_SCALE: u16 = 100;
1010
pub const DEFAULT_PREVIEW_SIZE: u16 = 50;
1111

12+
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash)]
13+
#[serde(default)]
14+
pub struct InputBarConfig {
15+
pub position: InputPosition,
16+
pub header: Option<Template>,
17+
pub prompt: String,
18+
pub border_type: BorderType,
19+
}
20+
21+
impl Default for InputBarConfig {
22+
fn default() -> Self {
23+
Self {
24+
position: InputPosition::default(),
25+
header: None,
26+
prompt: ">".to_string(),
27+
border_type: BorderType::default(),
28+
}
29+
}
30+
}
31+
1232
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash, Default)]
1333
#[serde(default)]
1434
pub struct StatusBarConfig {
1535
pub separator_open: String,
1636
pub separator_close: String,
1737
}
1838

39+
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash, Default)]
40+
#[serde(default)]
41+
pub struct ResultsPanelConfig {
42+
pub border_type: BorderType,
43+
}
44+
1945
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash)]
2046
#[serde(default)]
2147
pub struct PreviewPanelConfig {
2248
pub size: u16,
2349
pub header: Option<Template>,
2450
pub footer: Option<Template>,
2551
pub scrollbar: bool,
52+
pub border_type: BorderType,
2653
}
2754

2855
impl Default for PreviewPanelConfig {
@@ -32,6 +59,7 @@ impl Default for PreviewPanelConfig {
3259
header: None,
3360
footer: None,
3461
scrollbar: true,
62+
border_type: BorderType::default(),
3563
}
3664
}
3765
}
@@ -104,17 +132,15 @@ pub struct ThemeOverrides {
104132
pub struct UiConfig {
105133
pub use_nerd_font_icons: bool,
106134
pub ui_scale: u16,
107-
pub input_bar_position: InputPosition,
108135
pub orientation: Orientation,
109136
pub theme: String,
110-
pub input_header: Option<Template>,
111-
#[serde(default = "default_input_prompt")]
112-
pub input_prompt: String,
113137
pub features: Features,
114138

115139
// Feature-specific configurations
140+
pub input_bar: InputBarConfig,
116141
pub status_bar: StatusBarConfig,
117142
pub preview_panel: PreviewPanelConfig,
143+
pub results_panel: ResultsPanelConfig,
118144
pub help_panel: HelpPanelConfig,
119145
pub remote_control: RemoteControlConfig,
120146

@@ -123,27 +149,43 @@ pub struct UiConfig {
123149
pub theme_overrides: ThemeOverrides,
124150
}
125151

126-
const DEFAULT_INPUT_PROMPT: &str = ">";
127-
fn default_input_prompt() -> String {
128-
String::from(DEFAULT_INPUT_PROMPT)
129-
}
130-
131152
impl Default for UiConfig {
132153
fn default() -> Self {
133154
Self {
134155
use_nerd_font_icons: false,
135156
ui_scale: DEFAULT_UI_SCALE,
136-
input_bar_position: InputPosition::Top,
137157
orientation: Orientation::Landscape,
138158
theme: String::from(DEFAULT_THEME),
139-
input_header: None,
140-
input_prompt: String::from(DEFAULT_INPUT_PROMPT),
141159
features: Features::default(),
160+
input_bar: InputBarConfig::default(),
142161
status_bar: StatusBarConfig::default(),
143162
preview_panel: PreviewPanelConfig::default(),
163+
results_panel: ResultsPanelConfig::default(),
144164
help_panel: HelpPanelConfig::default(),
145165
remote_control: RemoteControlConfig::default(),
146166
theme_overrides: ThemeOverrides::default(),
147167
}
148168
}
149169
}
170+
171+
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Hash, Default)]
172+
#[serde(rename_all = "snake_case")]
173+
pub enum BorderType {
174+
None,
175+
Plain,
176+
#[default]
177+
Rounded,
178+
Thick,
179+
}
180+
impl BorderType {
181+
pub fn to_ratatui_border_type(
182+
&self,
183+
) -> Option<ratatui::widgets::BorderType> {
184+
match self {
185+
BorderType::None => None,
186+
BorderType::Plain => Some(ratatui::widgets::BorderType::Plain),
187+
BorderType::Rounded => Some(ratatui::widgets::BorderType::Rounded),
188+
BorderType::Thick => Some(ratatui::widgets::BorderType::Thick),
189+
}
190+
}
191+
}

0 commit comments

Comments
 (0)