convert prompt_confirmation_menu to crossterm
This commit is contained in:
parent
230f85b6ac
commit
ebe31b6df7
3 changed files with 105 additions and 78 deletions
|
@ -33,6 +33,6 @@ pub fn demo_confirmation_menu() {
|
||||||
creator: 09870987,
|
creator: 09870987,
|
||||||
description: "example confirmation".into(),
|
description: "example confirmation".into(),
|
||||||
},
|
},
|
||||||
]);
|
]).expect("confirmation menu demo failed");
|
||||||
println!("accept: {}, deny: {}", accept.len(), deny.len());
|
println!("accept: {}, deny: {}", accept.len(), deny.len());
|
||||||
}
|
}
|
||||||
|
|
|
@ -517,7 +517,7 @@ fn do_subcmd_trade(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if termion::is_tty(&stdout()) {
|
if termion::is_tty(&stdout()) {
|
||||||
let (accept, deny) = tui::prompt_confirmation_menu(confirmations);
|
let (accept, deny) = tui::prompt_confirmation_menu(confirmations)?;
|
||||||
for conf in &accept {
|
for conf in &accept {
|
||||||
let result = account.accept_confirmation(conf);
|
let result = account.accept_confirmation(conf);
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
|
|
177
src/tui.rs
177
src/tui.rs
|
@ -1,14 +1,17 @@
|
||||||
|
use crossterm::{
|
||||||
|
cursor,
|
||||||
|
event::{Event, KeyCode, KeyEvent, KeyModifiers},
|
||||||
|
execute,
|
||||||
|
style::{Print, PrintStyledContent, Stylize, Color, SetForegroundColor},
|
||||||
|
terminal::{Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
|
QueueableCommand,
|
||||||
|
};
|
||||||
use log::*;
|
use log::*;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::io::{Read, Write};
|
use std::io::{stdin, stdout, Read, Write};
|
||||||
use steamguard::Confirmation;
|
use steamguard::Confirmation;
|
||||||
use termion::{
|
use termion::{input::TermRead, raw::IntoRawMode};
|
||||||
event::{Event, Key},
|
|
||||||
input::TermRead,
|
|
||||||
raw::IntoRawMode,
|
|
||||||
screen::AlternateScreen,
|
|
||||||
};
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CAPTCHA_VALID_CHARS: Regex =
|
static ref CAPTCHA_VALID_CHARS: Regex =
|
||||||
|
@ -105,115 +108,139 @@ fn prompt_char_impl(input: &mut impl Read, text: &str, chars: &str) -> char {
|
||||||
/// Returns a tuple of (accepted, denied). Ignored confirmations are not included.
|
/// Returns a tuple of (accepted, denied). Ignored confirmations are not included.
|
||||||
pub(crate) fn prompt_confirmation_menu(
|
pub(crate) fn prompt_confirmation_menu(
|
||||||
confirmations: Vec<Confirmation>,
|
confirmations: Vec<Confirmation>,
|
||||||
) -> (Vec<Confirmation>, Vec<Confirmation>) {
|
) -> anyhow::Result<(Vec<Confirmation>, Vec<Confirmation>)> {
|
||||||
println!("press a key other than enter to show the menu.");
|
|
||||||
let mut to_accept_idx: HashSet<usize> = HashSet::new();
|
let mut to_accept_idx: HashSet<usize> = HashSet::new();
|
||||||
let mut to_deny_idx: HashSet<usize> = HashSet::new();
|
let mut to_deny_idx: HashSet<usize> = HashSet::new();
|
||||||
|
|
||||||
let mut screen = AlternateScreen::from(std::io::stdout().into_raw_mode().unwrap());
|
execute!(stdout(), EnterAlternateScreen)?;
|
||||||
let stdin = std::io::stdin();
|
crossterm::terminal::enable_raw_mode()?;
|
||||||
|
|
||||||
let mut selected_idx = 0;
|
let mut selected_idx = 0;
|
||||||
|
|
||||||
for c in stdin.events() {
|
loop {
|
||||||
match c.expect("could not get events") {
|
execute!(
|
||||||
Event::Key(Key::Char('a')) => {
|
stdout(),
|
||||||
|
Clear(ClearType::All),
|
||||||
|
cursor::MoveTo(1, 1),
|
||||||
|
PrintStyledContent(
|
||||||
|
"arrow keys to select, [a]ccept, [d]eny, [i]gnore, [enter] confirm choices\n\n"
|
||||||
|
.white()
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for i in 0..confirmations.len() {
|
||||||
|
stdout().queue(Print("\r"))?;
|
||||||
|
if selected_idx == i {
|
||||||
|
stdout().queue(SetForegroundColor(Color::Yellow))?;
|
||||||
|
stdout().queue(Print(" >"))?;
|
||||||
|
} else {
|
||||||
|
stdout().queue(SetForegroundColor(Color::White))?;
|
||||||
|
stdout().queue(Print(" "))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if to_accept_idx.contains(&i) {
|
||||||
|
stdout().queue(SetForegroundColor(Color::Green))?;
|
||||||
|
stdout().queue(Print("[a]"))?;
|
||||||
|
} else if to_deny_idx.contains(&i) {
|
||||||
|
stdout().queue(SetForegroundColor(Color::Red))?;
|
||||||
|
stdout().queue(Print("[d]"))?;
|
||||||
|
} else {
|
||||||
|
stdout().queue(Print("[ ]"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if selected_idx == i {
|
||||||
|
stdout().queue(SetForegroundColor(Color::Yellow))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout().queue(Print(format!(" {}\n", confirmations[i].description())))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout().flush()?;
|
||||||
|
|
||||||
|
match crossterm::event::read()? {
|
||||||
|
Event::Resize(_, _) => continue,
|
||||||
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('a'),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
to_accept_idx.insert(selected_idx);
|
to_accept_idx.insert(selected_idx);
|
||||||
to_deny_idx.remove(&selected_idx);
|
to_deny_idx.remove(&selected_idx);
|
||||||
}
|
}
|
||||||
Event::Key(Key::Char('d')) => {
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('d'),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
to_accept_idx.remove(&selected_idx);
|
to_accept_idx.remove(&selected_idx);
|
||||||
to_deny_idx.insert(selected_idx);
|
to_deny_idx.insert(selected_idx);
|
||||||
}
|
}
|
||||||
Event::Key(Key::Char('i')) => {
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('i'),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
to_accept_idx.remove(&selected_idx);
|
to_accept_idx.remove(&selected_idx);
|
||||||
to_deny_idx.remove(&selected_idx);
|
to_deny_idx.remove(&selected_idx);
|
||||||
}
|
}
|
||||||
Event::Key(Key::Char('A')) => {
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('A'),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
(0..confirmations.len()).for_each(|i| {
|
(0..confirmations.len()).for_each(|i| {
|
||||||
to_accept_idx.insert(i);
|
to_accept_idx.insert(i);
|
||||||
to_deny_idx.remove(&i);
|
to_deny_idx.remove(&i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Event::Key(Key::Char('D')) => {
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('D'),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
(0..confirmations.len()).for_each(|i| {
|
(0..confirmations.len()).for_each(|i| {
|
||||||
to_accept_idx.remove(&i);
|
to_accept_idx.remove(&i);
|
||||||
to_deny_idx.insert(i);
|
to_deny_idx.insert(i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Event::Key(Key::Char('I')) => {
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('I'),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
(0..confirmations.len()).for_each(|i| {
|
(0..confirmations.len()).for_each(|i| {
|
||||||
to_accept_idx.remove(&i);
|
to_accept_idx.remove(&i);
|
||||||
to_deny_idx.remove(&i);
|
to_deny_idx.remove(&i);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Event::Key(Key::Up) if selected_idx > 0 => {
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Up, ..
|
||||||
|
}) if selected_idx > 0 => {
|
||||||
selected_idx -= 1;
|
selected_idx -= 1;
|
||||||
}
|
}
|
||||||
Event::Key(Key::Down) if selected_idx < confirmations.len() - 1 => {
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Down,
|
||||||
|
..
|
||||||
|
}) if selected_idx < confirmations.len() - 1 => {
|
||||||
selected_idx += 1;
|
selected_idx += 1;
|
||||||
}
|
}
|
||||||
Event::Key(Key::Char('\n')) => {
|
Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Enter,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Event::Key(Key::Esc) | Event::Key(Key::Ctrl('c')) => {
|
Event::Key(KeyEvent {
|
||||||
return (vec![], vec![]);
|
code: KeyCode::Esc, ..
|
||||||
|
})
|
||||||
|
| Event::Key(KeyEvent {
|
||||||
|
code: KeyCode::Char('c'),
|
||||||
|
modifiers: KeyModifiers::CONTROL,
|
||||||
|
}) => {
|
||||||
|
return Ok((vec![], vec![]));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(
|
|
||||||
screen,
|
|
||||||
"{}{}{}arrow keys to select, [a]ccept, [d]eny, [i]gnore, [enter] confirm choices\n\n",
|
|
||||||
termion::clear::All,
|
|
||||||
termion::cursor::Goto(1, 1),
|
|
||||||
termion::color::Fg(termion::color::White)
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
for i in 0..confirmations.len() {
|
|
||||||
if selected_idx == i {
|
|
||||||
write!(
|
|
||||||
screen,
|
|
||||||
"\r{} >",
|
|
||||||
termion::color::Fg(termion::color::LightYellow)
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
write!(screen, "\r{} ", termion::color::Fg(termion::color::White)).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if to_accept_idx.contains(&i) {
|
execute!(stdout(), LeaveAlternateScreen)?;
|
||||||
write!(
|
crossterm::terminal::disable_raw_mode()?;
|
||||||
screen,
|
|
||||||
"{}[a]",
|
|
||||||
termion::color::Fg(termion::color::LightGreen)
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
} else if to_deny_idx.contains(&i) {
|
|
||||||
write!(
|
|
||||||
screen,
|
|
||||||
"{}[d]",
|
|
||||||
termion::color::Fg(termion::color::LightRed)
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
} else {
|
|
||||||
write!(screen, "[ ]").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if selected_idx == i {
|
return Ok((
|
||||||
write!(
|
|
||||||
screen,
|
|
||||||
"{}",
|
|
||||||
termion::color::Fg(termion::color::LightYellow)
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(screen, " {}\n", confirmations[i].description()).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
to_accept_idx
|
to_accept_idx
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| confirmations[*i].clone())
|
.map(|i| confirmations[*i].clone())
|
||||||
|
@ -222,7 +249,7 @@ pub(crate) fn prompt_confirmation_menu(
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| confirmations[*i].clone())
|
.map(|i| confirmations[*i].clone())
|
||||||
.collect(),
|
.collect(),
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn pause() {
|
pub(crate) fn pause() {
|
||||||
|
|
Loading…
Reference in a new issue