refactor(examples): add input modes to user input examples

This commit is contained in:
Florian Dehau 2020-01-19 18:41:00 +01:00
parent 9085c81e76
commit e6ce0ab9a7
2 changed files with 66 additions and 20 deletions

View file

@ -29,10 +29,17 @@ use unicode_width::UnicodeWidthStr;
use crate::util::event::{Event, Events};
enum InputMode {
Normal,
Editing,
}
/// App holds the state of the application
struct App {
/// Current value of the input box
input: String,
/// Current input mode
input_mode: InputMode,
/// History of recorded messages
messages: Vec<String>,
}
@ -41,6 +48,7 @@ impl Default for App {
fn default() -> App {
App {
input: String::new(),
input_mode: InputMode::Normal,
messages: Vec::new(),
}
}
@ -55,7 +63,7 @@ fn main() -> Result<(), failure::Error> {
let mut terminal = Terminal::new(backend)?;
// Setup event handlers
let events = Events::new();
let mut events = Events::new();
// Create default app state
let mut app = App::default();
@ -66,12 +74,24 @@ fn main() -> Result<(), failure::Error> {
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(2)
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
.constraints(
[
Constraint::Length(1),
Constraint::Length(3),
Constraint::Min(1),
]
.as_ref(),
)
.split(f.size());
let help_message = match app.input_mode {
InputMode::Normal => "Press q to exit, e to start editing.",
InputMode::Editing => "Press Esc to stop editing, Enter to record the message",
};
Paragraph::new([Text::raw(help_message)].iter()).render(&mut f, chunks[0]);
Paragraph::new([Text::raw(&app.input)].iter())
.style(Style::default().fg(Color::Yellow))
.block(Block::default().borders(Borders::ALL).title("Input"))
.render(&mut f, chunks[0]);
.render(&mut f, chunks[1]);
let messages = app
.messages
.iter()
@ -79,34 +99,47 @@ fn main() -> Result<(), failure::Error> {
.map(|(i, m)| Text::raw(format!("{}: {}", i, m)));
List::new(messages)
.block(Block::default().borders(Borders::ALL).title("Messages"))
.render(&mut f, chunks[1]);
.render(&mut f, chunks[2]);
})?;
// Put the cursor back inside the input box
write!(
terminal.backend_mut(),
"{}",
Goto(4 + app.input.width() as u16, 4)
Goto(4 + app.input.width() as u16, 5)
)?;
// stdout is buffered, flush it to see the effect immediately when hitting backspace
io::stdout().flush().ok();
// Handle input
match events.next()? {
Event::Input(input) => match input {
Key::Char('q') => {
break;
}
Key::Char('\n') => {
app.messages.push(app.input.drain(..).collect());
}
Key::Char(c) => {
app.input.push(c);
}
Key::Backspace => {
app.input.pop();
}
_ => {}
Event::Input(input) => match app.input_mode {
InputMode::Normal => match input {
Key::Char('e') => {
app.input_mode = InputMode::Editing;
events.disable_exit_key();
}
Key::Char('q') => {
break;
}
_ => {}
},
InputMode::Editing => match input {
Key::Char('\n') => {
app.messages.push(app.input.drain(..).collect());
}
Key::Char(c) => {
app.input.push(c);
}
Key::Backspace => {
app.input.pop();
}
Key::Esc => {
app.input_mode = InputMode::Normal;
events.enable_exit_key();
}
_ => {}
},
},
_ => {}
}

View file

@ -2,6 +2,7 @@ use std::io;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
use termion::event::Key;
use termion::input::TermRead;
@ -16,6 +17,7 @@ pub enum Event<I> {
pub struct Events {
rx: mpsc::Receiver<Event<Key>>,
input_handle: thread::JoinHandle<()>,
ignore_exit_key: Arc<AtomicBool>,
tick_handle: thread::JoinHandle<()>,
}
@ -41,8 +43,10 @@ impl Events {
pub fn with_config(config: Config) -> Events {
let (tx, rx) = mpsc::channel();
let ignore_exit_key = Arc::new(AtomicBool::new(false));
let input_handle = {
let tx = tx.clone();
let ignore_exit_key = ignore_exit_key.clone();
thread::spawn(move || {
let stdin = io::stdin();
for evt in stdin.keys() {
@ -51,7 +55,7 @@ impl Events {
if let Err(_) = tx.send(Event::Input(key)) {
return;
}
if key == config.exit_key {
if !ignore_exit_key.load(Ordering::Relaxed) && key == config.exit_key {
return;
}
}
@ -72,6 +76,7 @@ impl Events {
};
Events {
rx,
ignore_exit_key,
input_handle,
tick_handle,
}
@ -80,4 +85,12 @@ impl Events {
pub fn next(&self) -> Result<Event<Key>, mpsc::RecvError> {
self.rx.recv()
}
pub fn disable_exit_key(&mut self) {
self.ignore_exit_key.store(true, Ordering::Relaxed);
}
pub fn enable_exit_key(&mut self) {
self.ignore_exit_key.store(false, Ordering::Relaxed);
}
}