Skip to content

Commit a1b1a68

Browse files
feat(config): add cli options for border types and add optional Padding
1 parent 7f7cea9 commit a1b1a68

File tree

9 files changed

+371
-27
lines changed

9 files changed

+371
-27
lines changed

television/cli/args.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub struct Cli {
3333
/// A list of the available channels can be displayed using the
3434
/// `list-channels` command. The channel can also be changed from within
3535
/// the application.
36-
#[arg(value_enum, index = 1, verbatim_doc_comment)]
36+
#[arg(index = 1, verbatim_doc_comment)]
3737
pub channel: Option<String>,
3838

3939
/// A preview line number offset template to use to scroll the preview to for each
@@ -151,6 +151,20 @@ pub struct Cli {
151151
#[arg(long, value_name = "STRING", verbatim_doc_comment)]
152152
pub input_prompt: Option<String>,
153153

154+
/// Sets the input panel border type.
155+
///
156+
/// Available options are: `none`, `plain`, `rounded`, `thick`.
157+
#[arg(long, value_enum, verbatim_doc_comment)]
158+
pub input_border: Option<BorderType>,
159+
160+
/// Sets the input panel padding.
161+
///
162+
/// Format: `top=INTEGER;left=INTEGER;bottom=INTEGER;right=INTEGER`
163+
///
164+
/// Example: `--input-padding='top=1;left=2;bottom=1;right=2'`
165+
#[arg(long, value_name = "STRING", verbatim_doc_comment)]
166+
pub input_padding: Option<String>,
167+
154168
/// Preview header template
155169
///
156170
/// When a channel is specified: This overrides the header defined in the channel prototype.
@@ -181,6 +195,30 @@ pub struct Cli {
181195
)]
182196
pub preview_footer: Option<String>,
183197

198+
/// Sets the preview panel border type.
199+
///
200+
/// Available options are: `none`, `plain`, `rounded`, `thick`.
201+
#[arg(
202+
long,
203+
value_enum,
204+
verbatim_doc_comment,
205+
conflicts_with = "no_preview"
206+
)]
207+
pub preview_border: Option<BorderType>,
208+
209+
/// Sets the preview panel padding.
210+
///
211+
/// Format: `top=INTEGER;left=INTEGER;bottom=INTEGER;right=INTEGER`
212+
///
213+
/// Example: `--preview-padding='top=1;left=2;bottom=1;right=2'`
214+
#[arg(
215+
long,
216+
value_name = "STRING",
217+
verbatim_doc_comment,
218+
conflicts_with = "no_preview"
219+
)]
220+
pub preview_padding: Option<String>,
221+
184222
/// Source command to use for the current channel.
185223
///
186224
/// When a channel is specified: This overrides the command defined in the channel prototype.
@@ -220,6 +258,20 @@ pub struct Cli {
220258
#[arg(long, value_name = "STRING", verbatim_doc_comment)]
221259
pub source_output: Option<String>,
222260

261+
/// Sets the results panel border type.
262+
///
263+
/// Available options are: `none`, `plain`, `rounded`, `thick`.
264+
#[arg(long, value_enum, verbatim_doc_comment)]
265+
pub results_border: Option<BorderType>,
266+
267+
/// Sets the results panel padding.
268+
///
269+
/// Format: `top=INTEGER;left=INTEGER;bottom=INTEGER;right=INTEGER`
270+
///
271+
/// Example: `--results-padding='top=1;left=2;bottom=1;right=2'`
272+
#[arg(long, value_name = "STRING", verbatim_doc_comment)]
273+
pub results_padding: Option<String>,
274+
223275
/// The delimiter byte to use for splitting the source's command output into entries.
224276
///
225277
/// This can be useful when the source command outputs multiline entries and you want to
@@ -509,6 +561,14 @@ pub enum LayoutOrientation {
509561
Portrait,
510562
}
511563

564+
#[derive(Debug, Clone, Copy, PartialEq, ValueEnum)]
565+
pub enum BorderType {
566+
None,
567+
Plain,
568+
Rounded,
569+
Thick,
570+
}
571+
512572
// Add validator functions
513573
fn validate_positive_float(s: &str) -> Result<f64, String> {
514574
match s.parse::<f64>() {

television/cli/mod.rs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
cli::args::{Cli, Command},
55
config::{
66
DEFAULT_PREVIEW_SIZE, KeyBindings, get_config_dir, get_data_dir,
7+
ui::{BorderType, Padding},
78
},
89
errors::cli_parsing_error_exit,
910
screen::layout::Orientation,
@@ -62,6 +63,12 @@ pub struct PostProcessedCli {
6263
pub preview_size: Option<u16>,
6364
pub preview_header: Option<String>,
6465
pub preview_footer: Option<String>,
66+
pub preview_border: Option<BorderType>,
67+
pub preview_padding: Option<Padding>,
68+
69+
// Results panel configuration
70+
pub results_border: Option<BorderType>,
71+
pub results_padding: Option<Padding>,
6572

6673
// Status bar configuration
6774
pub no_status_bar: bool,
@@ -82,6 +89,8 @@ pub struct PostProcessedCli {
8289
pub input: Option<String>,
8390
pub input_header: Option<String>,
8491
pub input_prompt: Option<String>,
92+
pub input_border: Option<BorderType>,
93+
pub input_padding: Option<Padding>,
8594

8695
// UI and layout configuration
8796
pub layout: Option<Orientation>,
@@ -112,6 +121,8 @@ pub struct PostProcessedCli {
112121
pub command: Option<Command>,
113122
}
114123

124+
const DEFAULT_BORDER_TYPE: BorderType = BorderType::Rounded;
125+
115126
impl Default for PostProcessedCli {
116127
fn default() -> Self {
117128
Self {
@@ -134,6 +145,12 @@ impl Default for PostProcessedCli {
134145
preview_size: Some(DEFAULT_PREVIEW_SIZE),
135146
preview_header: None,
136147
preview_footer: None,
148+
preview_border: Some(DEFAULT_BORDER_TYPE),
149+
preview_padding: None,
150+
151+
// Results panel configuration
152+
results_border: Some(DEFAULT_BORDER_TYPE),
153+
results_padding: None,
137154

138155
// Status bar configuration
139156
no_status_bar: false,
@@ -154,6 +171,8 @@ impl Default for PostProcessedCli {
154171
input: None,
155172
input_header: None,
156173
input_prompt: None,
174+
input_border: Some(DEFAULT_BORDER_TYPE),
175+
input_padding: None,
157176

158177
// UI and layout configuration
159178
layout: None,
@@ -300,11 +319,26 @@ pub fn post_process(cli: Cli, readable_stdin: bool) -> PostProcessedCli {
300319
});
301320

302321
// Determine layout
303-
let layout: Option<Orientation> =
304-
cli.layout.map(|layout_enum| match layout_enum {
305-
args::LayoutOrientation::Landscape => Orientation::Landscape,
306-
args::LayoutOrientation::Portrait => Orientation::Portrait,
307-
});
322+
let layout: Option<Orientation> = cli.layout.map(Orientation::from);
323+
324+
// borders
325+
let input_border = cli.input_border.map(BorderType::from);
326+
let results_border = cli.results_border.map(BorderType::from);
327+
let preview_border = cli.preview_border.map(BorderType::from);
328+
329+
// padding
330+
let input_padding = cli.input_padding.map(|p| {
331+
parse_padding_literal(&p, CLI_PADDING_DELIMITER)
332+
.unwrap_or_else(|e| cli_parsing_error_exit(&e.to_string()))
333+
});
334+
let results_padding = cli.results_padding.map(|p| {
335+
parse_padding_literal(&p, CLI_PADDING_DELIMITER)
336+
.unwrap_or_else(|e| cli_parsing_error_exit(&e.to_string()))
337+
});
338+
let preview_padding = cli.preview_padding.map(|p| {
339+
parse_padding_literal(&p, CLI_PADDING_DELIMITER)
340+
.unwrap_or_else(|e| cli_parsing_error_exit(&e.to_string()))
341+
});
308342

309343
PostProcessedCli {
310344
// Channel and source configuration
@@ -326,6 +360,12 @@ pub fn post_process(cli: Cli, readable_stdin: bool) -> PostProcessedCli {
326360
preview_size: cli.preview_size,
327361
preview_header: cli.preview_header,
328362
preview_footer: cli.preview_footer,
363+
preview_border,
364+
preview_padding,
365+
366+
// Results configuration
367+
results_border,
368+
results_padding,
329369

330370
// Status bar configuration
331371
no_status_bar: cli.no_status_bar,
@@ -346,6 +386,8 @@ pub fn post_process(cli: Cli, readable_stdin: bool) -> PostProcessedCli {
346386
input: cli.input,
347387
input_header: cli.input_header,
348388
input_prompt: cli.input_prompt,
389+
input_border,
390+
input_padding,
349391

350392
// UI and layout configuration
351393
layout,
@@ -432,6 +474,7 @@ fn validate_adhoc_mode_constraints(cli: &Cli, readable_stdin: bool) {
432474
}
433475

434476
const CLI_KEYBINDINGS_DELIMITER: char = ';';
477+
const CLI_PADDING_DELIMITER: char = ';';
435478

436479
/// Parse a keybindings literal into a `KeyBindings` struct.
437480
///
@@ -452,6 +495,17 @@ fn parse_keybindings_literal(
452495
toml::from_str(&toml_definition).map_err(|e| anyhow!(e))
453496
}
454497

498+
/// Parses the CLI padding values into `config::ui::Padding`
499+
fn parse_padding_literal(
500+
cli_padding: &str,
501+
delimiter: char,
502+
) -> Result<Padding> {
503+
let toml_definition = cli_padding
504+
.split(delimiter)
505+
.fold(String::new(), |acc, p| acc + p + "\n");
506+
toml::from_str(&toml_definition).map_err(|e| anyhow!(e))
507+
}
508+
455509
pub fn list_channels<P>(cable_dir: P)
456510
where
457511
P: AsRef<Path>,

television/config/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,14 @@ impl Config {
281281
.input_bar
282282
.border_type
283283
.clone_from(&input_bar.border_type);
284+
self.ui.input_bar.padding = input_bar.padding;
284285
}
285286
if let Some(results_panel) = &ui_spec.results_panel {
286287
self.ui
287288
.results_panel
288289
.border_type
289290
.clone_from(&results_panel.border_type);
291+
self.ui.results_panel.padding = results_panel.padding;
290292
}
291293
if let Some(preview_panel) = &ui_spec.preview_panel {
292294
self.ui.preview_panel.size = preview_panel.size;
@@ -301,6 +303,7 @@ impl Config {
301303
.preview_panel
302304
.border_type
303305
.clone_from(&preview_panel.border_type);
306+
self.ui.preview_panel.padding = preview_panel.padding;
304307
}
305308
}
306309
}
@@ -372,6 +375,7 @@ pub use ui::{DEFAULT_PREVIEW_SIZE, DEFAULT_UI_SCALE};
372375
#[cfg(test)]
373376
mod tests {
374377
use crate::action::Action;
378+
use crate::config::ui::Padding;
375379
use crate::event::Key;
376380

377381
use super::*;
@@ -619,16 +623,19 @@ mod tests {
619623
header: Some(Template::Raw("hello".to_string())),
620624
prompt: "world".to_string(),
621625
border_type: BorderType::Thick,
626+
padding: Padding::uniform(2),
622627
}),
623628
results_panel: Some(ResultsPanelConfig {
624629
border_type: BorderType::None,
630+
padding: Padding::uniform(2),
625631
}),
626632
preview_panel: Some(PreviewPanelConfig {
627633
size: 42,
628634
header: None, // does not overwrite "cow"
629635
footer: Some(Template::Raw("moo".to_string())),
630636
scrollbar: true,
631637
border_type: BorderType::Plain,
638+
padding: Padding::uniform(2),
632639
}),
633640
status_bar: Some(StatusBarConfig {
634641
separator_open: "open".to_string(),
@@ -653,7 +660,11 @@ mod tests {
653660
);
654661
assert_eq!(config.ui.input_bar.prompt, "world");
655662
assert_eq!(config.ui.input_bar.border_type, BorderType::Thick);
663+
assert_eq!(config.ui.input_bar.padding, Padding::uniform(2));
664+
656665
assert_eq!(config.ui.results_panel.border_type, BorderType::None);
666+
assert_eq!(config.ui.results_panel.padding, Padding::uniform(2));
667+
657668
assert_eq!(config.ui.preview_panel.size, 42);
658669
assert_eq!(
659670
config.ui.preview_panel.header.as_ref().unwrap().raw(),
@@ -665,6 +676,8 @@ mod tests {
665676
);
666677
assert!(config.ui.preview_panel.scrollbar);
667678
assert_eq!(config.ui.preview_panel.border_type, BorderType::Plain);
679+
assert_eq!(config.ui.preview_panel.padding, Padding::uniform(2));
680+
668681
assert_eq!(config.ui.status_bar.separator_open, "open");
669682
assert_eq!(config.ui.status_bar.separator_close, "close");
670683
assert!(config.ui.remote_control.show_channel_descriptions);

0 commit comments

Comments
 (0)