mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-06 19:45:23 +00:00
feat(cli): add a --no-help
flag to allow disabling showing the help panel (#456)
This will disable the help panel and associated toggling actions entirely. This is useful when the help panel is not needed or when the user wants `tv` to run with a minimal interface (e.g. when using it as a file picker for a script or embedding it in a larger application).
This commit is contained in:
parent
b81873738a
commit
5bf3d20c83
@ -30,7 +30,8 @@ fn draw(c: &mut Criterion) {
|
||||
]));
|
||||
channel.find("television");
|
||||
// Wait for the channel to finish loading
|
||||
let mut tv = Television::new(tx, channel, config, None);
|
||||
let mut tv =
|
||||
Television::new(tx, channel, config, None, false, false);
|
||||
for _ in 0..5 {
|
||||
// tick the matcher
|
||||
let _ = tv.channel.results(10, 0);
|
||||
|
@ -6,7 +6,7 @@ use tracing::{debug, trace};
|
||||
|
||||
use crate::channels::entry::{Entry, PreviewType};
|
||||
use crate::channels::{OnAir, TelevisionChannel};
|
||||
use crate::config::Config;
|
||||
use crate::config::{default_tick_rate, Config};
|
||||
use crate::keymap::Keymap;
|
||||
use crate::render::UiState;
|
||||
use crate::television::{Mode, Television};
|
||||
@ -16,12 +16,47 @@ use crate::{
|
||||
render::{render, RenderingTask},
|
||||
};
|
||||
|
||||
pub struct AppOptions {
|
||||
/// Whether the application should automatically select the first entry if there is only one
|
||||
/// entry available.
|
||||
pub select_1: bool,
|
||||
/// Whether the application should disable the remote control feature.
|
||||
pub no_remote: bool,
|
||||
/// Whether the application should disable the help panel feature.
|
||||
pub no_help: bool,
|
||||
pub tick_rate: f64,
|
||||
}
|
||||
|
||||
impl Default for AppOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
select_1: false,
|
||||
no_remote: false,
|
||||
no_help: false,
|
||||
tick_rate: default_tick_rate(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppOptions {
|
||||
pub fn new(
|
||||
select_1: bool,
|
||||
no_remote: bool,
|
||||
no_help: bool,
|
||||
tick_rate: f64,
|
||||
) -> Self {
|
||||
Self {
|
||||
select_1,
|
||||
no_remote,
|
||||
no_help,
|
||||
tick_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The main application struct that holds the state of the application.
|
||||
pub struct App {
|
||||
keymap: Keymap,
|
||||
// maybe move these two into config instead of passing them
|
||||
// via the cli?
|
||||
tick_rate: f64,
|
||||
/// The television instance that handles channels and entries.
|
||||
television: Television,
|
||||
/// A flag that indicates whether the application should quit during the next frame.
|
||||
@ -53,9 +88,7 @@ pub struct App {
|
||||
ui_state_tx: mpsc::UnboundedSender<UiState>,
|
||||
/// Render task handle
|
||||
render_task: Option<tokio::task::JoinHandle<Result<()>>>,
|
||||
/// Whether the application should automatically select the first entry if there is only one
|
||||
/// entry available.
|
||||
select_1: bool,
|
||||
options: AppOptions,
|
||||
}
|
||||
|
||||
/// The outcome of an action.
|
||||
@ -99,14 +132,12 @@ impl App {
|
||||
channel: TelevisionChannel,
|
||||
config: Config,
|
||||
input: Option<String>,
|
||||
select_1: bool,
|
||||
no_remote: bool,
|
||||
options: AppOptions,
|
||||
) -> Self {
|
||||
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
||||
let (render_tx, render_rx) = mpsc::unbounded_channel();
|
||||
let (_, event_rx) = mpsc::unbounded_channel();
|
||||
let (event_abort_tx, _) = mpsc::unbounded_channel();
|
||||
let tick_rate = config.application.tick_rate;
|
||||
let keymap = Keymap::from(&config.keybindings);
|
||||
|
||||
debug!("{:?}", keymap);
|
||||
@ -116,12 +147,12 @@ impl App {
|
||||
channel,
|
||||
config,
|
||||
input,
|
||||
no_remote,
|
||||
options.no_remote,
|
||||
options.no_help,
|
||||
);
|
||||
|
||||
Self {
|
||||
keymap,
|
||||
tick_rate,
|
||||
television,
|
||||
should_quit: false,
|
||||
should_suspend: false,
|
||||
@ -134,7 +165,7 @@ impl App {
|
||||
ui_state_rx,
|
||||
ui_state_tx,
|
||||
render_task: None,
|
||||
select_1,
|
||||
options,
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,7 +191,7 @@ impl App {
|
||||
// Event loop
|
||||
if !headless {
|
||||
debug!("Starting backend event loop");
|
||||
let event_loop = EventLoop::new(self.tick_rate, true);
|
||||
let event_loop = EventLoop::new(self.options.tick_rate, true);
|
||||
self.event_rx = event_loop.rx;
|
||||
self.event_abort_tx = event_loop.abort_tx;
|
||||
}
|
||||
@ -210,7 +241,7 @@ impl App {
|
||||
|
||||
// If `self.select_1` is true, the channel is not running, and there is
|
||||
// only one entry available, automatically select the first entry.
|
||||
if self.select_1
|
||||
if self.options.select_1
|
||||
&& !self.television.channel.running()
|
||||
&& self.television.channel.total_count() == 1
|
||||
{
|
||||
@ -406,8 +437,7 @@ mod test {
|
||||
TelevisionChannel::Stdin(StdinChannel::new(PreviewType::None)),
|
||||
Config::default(),
|
||||
None,
|
||||
true,
|
||||
false,
|
||||
AppOptions::default(),
|
||||
);
|
||||
app.television
|
||||
.results_picker
|
||||
|
@ -1,6 +1,7 @@
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Parser, Debug, Default)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct Cli {
|
||||
/// Which channel shall we watch?
|
||||
@ -112,6 +113,16 @@ pub struct Cli {
|
||||
#[arg(long, default_value = "false", verbatim_doc_comment)]
|
||||
pub no_remote: bool,
|
||||
|
||||
/// Disable the help panel.
|
||||
///
|
||||
/// This will disable the help panel and associated toggling actions
|
||||
/// entirely. This is useful when the help panel is not needed or
|
||||
/// when the user wants `tv` to run with a minimal interface (e.g. when
|
||||
/// using it as a file picker for a script or embedding it in a larger
|
||||
/// application).
|
||||
#[arg(long, default_value = "false", verbatim_doc_comment)]
|
||||
pub no_help: bool,
|
||||
|
||||
#[command(subcommand)]
|
||||
pub command: Option<Command>,
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ use crate::{
|
||||
|
||||
pub mod args;
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PostProcessedCli {
|
||||
pub channel: ParsedCliChannel,
|
||||
@ -31,6 +32,7 @@ pub struct PostProcessedCli {
|
||||
pub keybindings: Option<KeyBindings>,
|
||||
pub select_1: bool,
|
||||
pub no_remote: bool,
|
||||
pub no_help: bool,
|
||||
}
|
||||
|
||||
impl Default for PostProcessedCli {
|
||||
@ -48,6 +50,7 @@ impl Default for PostProcessedCli {
|
||||
keybindings: None,
|
||||
select_1: false,
|
||||
no_remote: false,
|
||||
no_help: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -114,6 +117,7 @@ impl From<Cli> for PostProcessedCli {
|
||||
keybindings,
|
||||
select_1: cli.select_1,
|
||||
no_remote: cli.no_remote,
|
||||
no_help: cli.no_help,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -319,17 +323,9 @@ mod tests {
|
||||
let cli = Cli {
|
||||
channel: "files".to_string(),
|
||||
preview: Some("bat -n --color=always {}".to_string()),
|
||||
no_preview: false,
|
||||
delimiter: ":".to_string(),
|
||||
tick_rate: Some(50.0),
|
||||
frame_rate: Some(60.0),
|
||||
keybindings: None,
|
||||
input: None,
|
||||
command: None,
|
||||
working_directory: Some("/home/user".to_string()),
|
||||
autocomplete_prompt: None,
|
||||
select_1: false,
|
||||
no_remote: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let post_processed_cli: PostProcessedCli = cli.into();
|
||||
@ -345,8 +341,8 @@ mod tests {
|
||||
delimiter: ":".to_string()
|
||||
})
|
||||
);
|
||||
assert_eq!(post_processed_cli.tick_rate, Some(50.0));
|
||||
assert_eq!(post_processed_cli.frame_rate, Some(60.0));
|
||||
assert_eq!(post_processed_cli.tick_rate, None);
|
||||
assert_eq!(post_processed_cli.frame_rate, None);
|
||||
assert_eq!(
|
||||
post_processed_cli.working_directory,
|
||||
Some("/home/user".to_string())
|
||||
@ -358,18 +354,8 @@ mod tests {
|
||||
fn test_from_cli_no_args() {
|
||||
let cli = Cli {
|
||||
channel: ".".to_string(),
|
||||
preview: None,
|
||||
no_preview: false,
|
||||
delimiter: ":".to_string(),
|
||||
tick_rate: Some(50.0),
|
||||
frame_rate: Some(60.0),
|
||||
keybindings: None,
|
||||
input: None,
|
||||
command: None,
|
||||
working_directory: None,
|
||||
autocomplete_prompt: None,
|
||||
select_1: false,
|
||||
no_remote: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let post_processed_cli: PostProcessedCli = cli.into();
|
||||
@ -390,17 +376,8 @@ mod tests {
|
||||
let cli = Cli {
|
||||
channel: "files".to_string(),
|
||||
preview: Some(":files:".to_string()),
|
||||
no_preview: false,
|
||||
delimiter: ":".to_string(),
|
||||
tick_rate: Some(50.0),
|
||||
frame_rate: Some(60.0),
|
||||
keybindings: None,
|
||||
input: None,
|
||||
command: None,
|
||||
working_directory: None,
|
||||
autocomplete_prompt: None,
|
||||
select_1: false,
|
||||
no_remote: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let post_processed_cli: PostProcessedCli = cli.into();
|
||||
@ -416,17 +393,8 @@ mod tests {
|
||||
let cli = Cli {
|
||||
channel: "files".to_string(),
|
||||
preview: Some(":env_var:".to_string()),
|
||||
no_preview: false,
|
||||
delimiter: ":".to_string(),
|
||||
tick_rate: Some(50.0),
|
||||
frame_rate: Some(60.0),
|
||||
keybindings: None,
|
||||
input: None,
|
||||
command: None,
|
||||
working_directory: None,
|
||||
autocomplete_prompt: None,
|
||||
select_1: false,
|
||||
no_remote: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let post_processed_cli: PostProcessedCli = cli.into();
|
||||
@ -442,20 +410,12 @@ mod tests {
|
||||
let cli = Cli {
|
||||
channel: "files".to_string(),
|
||||
preview: Some(":env_var:".to_string()),
|
||||
no_preview: false,
|
||||
delimiter: ":".to_string(),
|
||||
tick_rate: Some(50.0),
|
||||
frame_rate: Some(60.0),
|
||||
keybindings: Some(
|
||||
"quit=\"esc\";select_next_entry=[\"down\",\"ctrl-j\"]"
|
||||
.to_string(),
|
||||
),
|
||||
input: None,
|
||||
command: None,
|
||||
working_directory: None,
|
||||
autocomplete_prompt: None,
|
||||
select_1: false,
|
||||
no_remote: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let post_processed_cli: PostProcessedCli = cli.into();
|
||||
|
@ -260,7 +260,7 @@ fn default_frame_rate() -> f64 {
|
||||
60.0
|
||||
}
|
||||
|
||||
fn default_tick_rate() -> f64 {
|
||||
pub fn default_tick_rate() -> f64 {
|
||||
50.0
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ use television::cli::parse_channel;
|
||||
use television::utils::clipboard::CLIPBOARD;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
use television::app::App;
|
||||
use television::app::{App, AppOptions};
|
||||
use television::channels::{
|
||||
entry::PreviewType, stdin::Channel as StdinChannel, TelevisionChannel,
|
||||
};
|
||||
@ -65,8 +65,13 @@ async fn main() -> Result<()> {
|
||||
CLIPBOARD.with(<_>::default);
|
||||
|
||||
debug!("Creating application...");
|
||||
let mut app =
|
||||
App::new(channel, config, args.input, args.select_1, args.no_remote);
|
||||
let options = AppOptions::new(
|
||||
args.select_1,
|
||||
args.no_remote,
|
||||
args.no_help,
|
||||
config.application.tick_rate,
|
||||
);
|
||||
let mut app = App::new(channel, config, args.input, options);
|
||||
stdout().flush()?;
|
||||
debug!("Running application...");
|
||||
let output = app.run(stdout().is_terminal(), false).await?;
|
||||
|
@ -48,6 +48,7 @@ pub struct Television {
|
||||
pub colorscheme: Colorscheme,
|
||||
pub ticks: u64,
|
||||
pub ui_state: UiState,
|
||||
pub no_help: bool,
|
||||
}
|
||||
|
||||
impl Television {
|
||||
@ -55,9 +56,10 @@ impl Television {
|
||||
pub fn new(
|
||||
action_tx: UnboundedSender<Action>,
|
||||
mut channel: TelevisionChannel,
|
||||
config: Config,
|
||||
mut config: Config,
|
||||
input: Option<String>,
|
||||
no_remote: bool,
|
||||
no_help: bool,
|
||||
) -> Self {
|
||||
let mut results_picker = Picker::new(input.clone());
|
||||
if config.ui.input_bar_position == InputPosition::Bottom {
|
||||
@ -97,6 +99,10 @@ impl Television {
|
||||
)))
|
||||
};
|
||||
|
||||
if no_help {
|
||||
config.ui.show_help_bar = false;
|
||||
}
|
||||
|
||||
Self {
|
||||
action_tx,
|
||||
config,
|
||||
@ -114,6 +120,7 @@ impl Television {
|
||||
colorscheme,
|
||||
ticks: 0,
|
||||
ui_state: UiState::default(),
|
||||
no_help,
|
||||
}
|
||||
}
|
||||
|
||||
@ -592,6 +599,9 @@ impl Television {
|
||||
self.handle_toggle_send_to_channel();
|
||||
}
|
||||
Action::ToggleHelp => {
|
||||
if self.no_help {
|
||||
return Ok(());
|
||||
}
|
||||
self.config.ui.show_help_bar = !self.config.ui.show_help_bar;
|
||||
}
|
||||
Action::TogglePreview => {
|
||||
|
@ -1,7 +1,9 @@
|
||||
use std::{collections::HashSet, path::PathBuf, time::Duration};
|
||||
|
||||
use television::{
|
||||
action::Action, app::App, channels::TelevisionChannel,
|
||||
action::Action,
|
||||
app::{App, AppOptions},
|
||||
channels::TelevisionChannel,
|
||||
config::default_config_from_file,
|
||||
};
|
||||
use tokio::{task::JoinHandle, time::timeout};
|
||||
@ -39,7 +41,9 @@ fn setup_app(
|
||||
config.application.tick_rate = 100.0;
|
||||
let input = None;
|
||||
|
||||
let mut app = App::new(chan, config, input, select_1, false);
|
||||
let options =
|
||||
AppOptions::new(select_1, false, false, config.application.tick_rate);
|
||||
let mut app = App::new(chan, config, input, options);
|
||||
|
||||
// retrieve the app's action channel handle in order to send a quit action
|
||||
let tx = app.action_tx.clone();
|
||||
|
Loading…
x
Reference in New Issue
Block a user