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
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
07f0ae6e80
@ -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