mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-06 19:45:23 +00:00
feat(cli): add a --no-remote
flag to lock the application on the cli-invoked channel (#455)
This will disable the remote control panel and associated actions entirely. This is useful when the remote control is not needed or when the user wants `tv` to run in single-channel mode (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
4a584b437c
commit
b81873738a
@ -100,6 +100,7 @@ impl App {
|
|||||||
config: Config,
|
config: Config,
|
||||||
input: Option<String>,
|
input: Option<String>,
|
||||||
select_1: bool,
|
select_1: bool,
|
||||||
|
no_remote: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
||||||
let (render_tx, render_rx) = mpsc::unbounded_channel();
|
let (render_tx, render_rx) = mpsc::unbounded_channel();
|
||||||
@ -110,8 +111,13 @@ impl App {
|
|||||||
|
|
||||||
debug!("{:?}", keymap);
|
debug!("{:?}", keymap);
|
||||||
let (ui_state_tx, ui_state_rx) = mpsc::unbounded_channel();
|
let (ui_state_tx, ui_state_rx) = mpsc::unbounded_channel();
|
||||||
let television =
|
let television = Television::new(
|
||||||
Television::new(action_tx.clone(), channel, config, input);
|
action_tx.clone(),
|
||||||
|
channel,
|
||||||
|
config,
|
||||||
|
input,
|
||||||
|
no_remote,
|
||||||
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
keymap,
|
keymap,
|
||||||
@ -401,6 +407,7 @@ mod test {
|
|||||||
Config::default(),
|
Config::default(),
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
app.television
|
app.television
|
||||||
.results_picker
|
.results_picker
|
||||||
|
@ -102,6 +102,16 @@ pub struct Cli {
|
|||||||
#[arg(long, default_value = "false", verbatim_doc_comment)]
|
#[arg(long, default_value = "false", verbatim_doc_comment)]
|
||||||
pub select_1: bool,
|
pub select_1: bool,
|
||||||
|
|
||||||
|
/// Disable the remote control.
|
||||||
|
///
|
||||||
|
/// This will disable the remote control panel and associated actions
|
||||||
|
/// entirely. This is useful when the remote control is not needed or
|
||||||
|
/// when the user wants `tv` to run in single-channel mode (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_remote: bool,
|
||||||
|
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Option<Command>,
|
pub command: Option<Command>,
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ pub struct PostProcessedCli {
|
|||||||
pub autocomplete_prompt: Option<String>,
|
pub autocomplete_prompt: Option<String>,
|
||||||
pub keybindings: Option<KeyBindings>,
|
pub keybindings: Option<KeyBindings>,
|
||||||
pub select_1: bool,
|
pub select_1: bool,
|
||||||
|
pub no_remote: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PostProcessedCli {
|
impl Default for PostProcessedCli {
|
||||||
@ -46,6 +47,7 @@ impl Default for PostProcessedCli {
|
|||||||
autocomplete_prompt: None,
|
autocomplete_prompt: None,
|
||||||
keybindings: None,
|
keybindings: None,
|
||||||
select_1: false,
|
select_1: false,
|
||||||
|
no_remote: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,6 +113,7 @@ impl From<Cli> for PostProcessedCli {
|
|||||||
autocomplete_prompt: cli.autocomplete_prompt,
|
autocomplete_prompt: cli.autocomplete_prompt,
|
||||||
keybindings,
|
keybindings,
|
||||||
select_1: cli.select_1,
|
select_1: cli.select_1,
|
||||||
|
no_remote: cli.no_remote,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -326,6 +329,7 @@ mod tests {
|
|||||||
working_directory: Some("/home/user".to_string()),
|
working_directory: Some("/home/user".to_string()),
|
||||||
autocomplete_prompt: None,
|
autocomplete_prompt: None,
|
||||||
select_1: false,
|
select_1: false,
|
||||||
|
no_remote: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let post_processed_cli: PostProcessedCli = cli.into();
|
let post_processed_cli: PostProcessedCli = cli.into();
|
||||||
@ -365,6 +369,7 @@ mod tests {
|
|||||||
working_directory: None,
|
working_directory: None,
|
||||||
autocomplete_prompt: None,
|
autocomplete_prompt: None,
|
||||||
select_1: false,
|
select_1: false,
|
||||||
|
no_remote: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let post_processed_cli: PostProcessedCli = cli.into();
|
let post_processed_cli: PostProcessedCli = cli.into();
|
||||||
@ -395,6 +400,7 @@ mod tests {
|
|||||||
working_directory: None,
|
working_directory: None,
|
||||||
autocomplete_prompt: None,
|
autocomplete_prompt: None,
|
||||||
select_1: false,
|
select_1: false,
|
||||||
|
no_remote: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let post_processed_cli: PostProcessedCli = cli.into();
|
let post_processed_cli: PostProcessedCli = cli.into();
|
||||||
@ -420,6 +426,7 @@ mod tests {
|
|||||||
working_directory: None,
|
working_directory: None,
|
||||||
autocomplete_prompt: None,
|
autocomplete_prompt: None,
|
||||||
select_1: false,
|
select_1: false,
|
||||||
|
no_remote: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let post_processed_cli: PostProcessedCli = cli.into();
|
let post_processed_cli: PostProcessedCli = cli.into();
|
||||||
@ -448,6 +455,7 @@ mod tests {
|
|||||||
working_directory: None,
|
working_directory: None,
|
||||||
autocomplete_prompt: None,
|
autocomplete_prompt: None,
|
||||||
select_1: false,
|
select_1: false,
|
||||||
|
no_remote: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let post_processed_cli: PostProcessedCli = cli.into();
|
let post_processed_cli: PostProcessedCli = cli.into();
|
||||||
|
@ -65,7 +65,8 @@ async fn main() -> Result<()> {
|
|||||||
CLIPBOARD.with(<_>::default);
|
CLIPBOARD.with(<_>::default);
|
||||||
|
|
||||||
debug!("Creating application...");
|
debug!("Creating application...");
|
||||||
let mut app = App::new(channel, config, args.input, args.select_1);
|
let mut app =
|
||||||
|
App::new(channel, config, args.input, args.select_1, args.no_remote);
|
||||||
stdout().flush()?;
|
stdout().flush()?;
|
||||||
debug!("Running application...");
|
debug!("Running application...");
|
||||||
let output = app.run(stdout().is_terminal(), false).await?;
|
let output = app.run(stdout().is_terminal(), false).await?;
|
||||||
|
@ -35,7 +35,7 @@ pub struct Television {
|
|||||||
action_tx: UnboundedSender<Action>,
|
action_tx: UnboundedSender<Action>,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub channel: TelevisionChannel,
|
pub channel: TelevisionChannel,
|
||||||
pub remote_control: TelevisionChannel,
|
pub remote_control: Option<TelevisionChannel>,
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub current_pattern: String,
|
pub current_pattern: String,
|
||||||
pub results_picker: Picker,
|
pub results_picker: Picker,
|
||||||
@ -57,6 +57,7 @@ impl Television {
|
|||||||
mut channel: TelevisionChannel,
|
mut channel: TelevisionChannel,
|
||||||
config: Config,
|
config: Config,
|
||||||
input: Option<String>,
|
input: Option<String>,
|
||||||
|
no_remote: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut results_picker = Picker::new(input.clone());
|
let mut results_picker = Picker::new(input.clone());
|
||||||
if config.ui.input_bar_position == InputPosition::Bottom {
|
if config.ui.input_bar_position == InputPosition::Bottom {
|
||||||
@ -87,13 +88,20 @@ impl Television {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let remote_control = if no_remote {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(TelevisionChannel::RemoteControl(RemoteControl::new(
|
||||||
|
builtin_channels,
|
||||||
|
Some(cable_channels),
|
||||||
|
)))
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
action_tx,
|
action_tx,
|
||||||
config,
|
config,
|
||||||
channel,
|
channel,
|
||||||
remote_control: TelevisionChannel::RemoteControl(
|
remote_control,
|
||||||
RemoteControl::new(builtin_channels, Some(cable_channels)),
|
|
||||||
),
|
|
||||||
mode: Mode::Channel,
|
mode: Mode::Channel,
|
||||||
current_pattern: EMPTY_STRING.to_string(),
|
current_pattern: EMPTY_STRING.to_string(),
|
||||||
results_picker,
|
results_picker,
|
||||||
@ -118,9 +126,9 @@ impl Television {
|
|||||||
let builtin_channels = load_builtin_channels(Some(
|
let builtin_channels = load_builtin_channels(Some(
|
||||||
&cable_channels.keys().collect::<Vec<_>>(),
|
&cable_channels.keys().collect::<Vec<_>>(),
|
||||||
));
|
));
|
||||||
self.remote_control = TelevisionChannel::RemoteControl(
|
self.remote_control = Some(TelevisionChannel::RemoteControl(
|
||||||
RemoteControl::new(builtin_channels, Some(cable_channels)),
|
RemoteControl::new(builtin_channels, Some(cable_channels)),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump_context(&self) -> Ctx {
|
pub fn dump_context(&self) -> Ctx {
|
||||||
@ -170,7 +178,7 @@ impl Television {
|
|||||||
self.channel.find(pattern);
|
self.channel.find(pattern);
|
||||||
}
|
}
|
||||||
Mode::RemoteControl | Mode::SendToChannel => {
|
Mode::RemoteControl | Mode::SendToChannel => {
|
||||||
self.remote_control.find(pattern);
|
self.remote_control.as_mut().unwrap().find(pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,6 +196,8 @@ impl Television {
|
|||||||
if let Some(i) = self.rc_picker.selected() {
|
if let Some(i) = self.rc_picker.selected() {
|
||||||
return self
|
return self
|
||||||
.remote_control
|
.remote_control
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
.get_result(i.try_into().unwrap());
|
.get_result(i.try_into().unwrap());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -217,9 +227,10 @@ impl Television {
|
|||||||
Mode::Channel => {
|
Mode::Channel => {
|
||||||
(self.channel.result_count(), &mut self.results_picker)
|
(self.channel.result_count(), &mut self.results_picker)
|
||||||
}
|
}
|
||||||
Mode::RemoteControl | Mode::SendToChannel => {
|
Mode::RemoteControl | Mode::SendToChannel => (
|
||||||
(self.remote_control.total_count(), &mut self.rc_picker)
|
self.remote_control.as_ref().unwrap().total_count(),
|
||||||
}
|
&mut self.rc_picker,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
if result_count == 0 {
|
if result_count == 0 {
|
||||||
return;
|
return;
|
||||||
@ -236,9 +247,10 @@ impl Television {
|
|||||||
Mode::Channel => {
|
Mode::Channel => {
|
||||||
(self.channel.result_count(), &mut self.results_picker)
|
(self.channel.result_count(), &mut self.results_picker)
|
||||||
}
|
}
|
||||||
Mode::RemoteControl | Mode::SendToChannel => {
|
Mode::RemoteControl | Mode::SendToChannel => (
|
||||||
(self.remote_control.total_count(), &mut self.rc_picker)
|
self.remote_control.as_ref().unwrap().total_count(),
|
||||||
}
|
&mut self.rc_picker,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
if result_count == 0 {
|
if result_count == 0 {
|
||||||
return;
|
return;
|
||||||
@ -368,18 +380,20 @@ impl Television {
|
|||||||
|
|
||||||
pub fn update_rc_picker_state(&mut self) {
|
pub fn update_rc_picker_state(&mut self) {
|
||||||
if self.rc_picker.selected().is_none()
|
if self.rc_picker.selected().is_none()
|
||||||
&& self.remote_control.result_count() > 0
|
&& self.remote_control.as_ref().unwrap().result_count() > 0
|
||||||
{
|
{
|
||||||
self.rc_picker.select(Some(0));
|
self.rc_picker.select(Some(0));
|
||||||
self.rc_picker.relative_select(Some(0));
|
self.rc_picker.relative_select(Some(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.rc_picker.entries = self.remote_control.results(
|
self.rc_picker.entries =
|
||||||
// this'll be more than the actual rc height but it's fine
|
self.remote_control.as_mut().unwrap().results(
|
||||||
self.ui_state.layout.results.height.into(),
|
// this'll be more than the actual rc height but it's fine
|
||||||
u32::try_from(self.rc_picker.offset()).unwrap(),
|
self.ui_state.layout.results.height.into(),
|
||||||
);
|
u32::try_from(self.rc_picker.offset()).unwrap(),
|
||||||
self.rc_picker.total_items = self.remote_control.total_count();
|
);
|
||||||
|
self.rc_picker.total_items =
|
||||||
|
self.remote_control.as_ref().unwrap().total_count();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_input_action(&mut self, action: &Action) {
|
pub fn handle_input_action(&mut self, action: &Action) {
|
||||||
@ -408,6 +422,9 @@ impl Television {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_toggle_rc(&mut self) {
|
pub fn handle_toggle_rc(&mut self) {
|
||||||
|
if self.remote_control.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Channel => {
|
Mode::Channel => {
|
||||||
self.mode = Mode::RemoteControl;
|
self.mode = Mode::RemoteControl;
|
||||||
@ -417,7 +434,7 @@ impl Television {
|
|||||||
// this resets the RC picker
|
// this resets the RC picker
|
||||||
self.reset_picker_input();
|
self.reset_picker_input();
|
||||||
self.init_remote_control();
|
self.init_remote_control();
|
||||||
self.remote_control.find(EMPTY_STRING);
|
self.remote_control.as_mut().unwrap().find(EMPTY_STRING);
|
||||||
self.reset_picker_selection();
|
self.reset_picker_selection();
|
||||||
self.mode = Mode::Channel;
|
self.mode = Mode::Channel;
|
||||||
}
|
}
|
||||||
@ -426,16 +443,19 @@ impl Television {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_toggle_send_to_channel(&mut self) {
|
pub fn handle_toggle_send_to_channel(&mut self) {
|
||||||
|
if self.remote_control.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Channel | Mode::RemoteControl => {
|
Mode::Channel | Mode::RemoteControl => {
|
||||||
self.mode = Mode::SendToChannel;
|
self.mode = Mode::SendToChannel;
|
||||||
self.remote_control = TelevisionChannel::RemoteControl(
|
self.remote_control = Some(TelevisionChannel::RemoteControl(
|
||||||
RemoteControl::with_transitions_from(&self.channel),
|
RemoteControl::with_transitions_from(&self.channel),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
Mode::SendToChannel => {
|
Mode::SendToChannel => {
|
||||||
self.reset_picker_input();
|
self.reset_picker_input();
|
||||||
self.remote_control.find(EMPTY_STRING);
|
self.remote_control.as_mut().unwrap().find(EMPTY_STRING);
|
||||||
self.reset_picker_selection();
|
self.reset_picker_selection();
|
||||||
self.mode = Mode::Channel;
|
self.mode = Mode::Channel;
|
||||||
}
|
}
|
||||||
@ -462,12 +482,15 @@ impl Television {
|
|||||||
}
|
}
|
||||||
Mode::RemoteControl => {
|
Mode::RemoteControl => {
|
||||||
if let Some(entry) = self.get_selected_entry(None) {
|
if let Some(entry) = self.get_selected_entry(None) {
|
||||||
let new_channel =
|
let new_channel = self
|
||||||
self.remote_control.zap(entry.name.as_str())?;
|
.remote_control
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.zap(entry.name.as_str())?;
|
||||||
// this resets the RC picker
|
// this resets the RC picker
|
||||||
self.reset_picker_selection();
|
self.reset_picker_selection();
|
||||||
self.reset_picker_input();
|
self.reset_picker_input();
|
||||||
self.remote_control.find(EMPTY_STRING);
|
self.remote_control.as_mut().unwrap().find(EMPTY_STRING);
|
||||||
self.mode = Mode::Channel;
|
self.mode = Mode::Channel;
|
||||||
self.change_channel(new_channel);
|
self.change_channel(new_channel);
|
||||||
}
|
}
|
||||||
@ -479,7 +502,7 @@ impl Television {
|
|||||||
.transition_to(entry.name.as_str().try_into()?);
|
.transition_to(entry.name.as_str().try_into()?);
|
||||||
self.reset_picker_selection();
|
self.reset_picker_selection();
|
||||||
self.reset_picker_input();
|
self.reset_picker_input();
|
||||||
self.remote_control.find(EMPTY_STRING);
|
self.remote_control.as_mut().unwrap().find(EMPTY_STRING);
|
||||||
self.mode = Mode::Channel;
|
self.mode = Mode::Channel;
|
||||||
self.change_channel(new_channel);
|
self.change_channel(new_channel);
|
||||||
}
|
}
|
||||||
@ -589,7 +612,9 @@ impl Television {
|
|||||||
|
|
||||||
self.update_results_picker_state();
|
self.update_results_picker_state();
|
||||||
|
|
||||||
self.update_rc_picker_state();
|
if self.remote_control.is_some() {
|
||||||
|
self.update_rc_picker_state();
|
||||||
|
}
|
||||||
|
|
||||||
let selected_entry = self
|
let selected_entry = self
|
||||||
.get_selected_entry(Some(Mode::Channel))
|
.get_selected_entry(Some(Mode::Channel))
|
||||||
|
@ -39,7 +39,7 @@ fn setup_app(
|
|||||||
config.application.tick_rate = 100.0;
|
config.application.tick_rate = 100.0;
|
||||||
let input = None;
|
let input = None;
|
||||||
|
|
||||||
let mut app = App::new(chan, config, input, select_1);
|
let mut app = App::new(chan, config, input, select_1, false);
|
||||||
|
|
||||||
// retrieve the app's action channel handle in order to send a quit action
|
// retrieve the app's action channel handle in order to send a quit action
|
||||||
let tx = app.action_tx.clone();
|
let tx = app.action_tx.clone();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user