mirror of
https://github.com/fdehau/tui-rs.git
synced 2024-11-15 06:12:49 +00:00
8cdfc883b9
It basically never makes sense to render without syncing the size. Without resizing, if shrinking, we get artefacts. If growing, we may get panics (before this change the Rustbox sample (the only one which didn't handle resizing on its own) panicked because the widget would get an updated size, while the terminal would not).
121 lines
3.5 KiB
Rust
121 lines
3.5 KiB
Rust
extern crate failure;
|
|
/// A simple example demonstrating how to handle user input. This is
|
|
/// a bit out of the scope of the library as it does not provide any
|
|
/// input handling out of the box. However, it may helps some to get
|
|
/// started.
|
|
///
|
|
/// This is a very simple example:
|
|
/// * A input box always focused. Every character you type is registered
|
|
/// here
|
|
/// * Pressing Backspace erases a character
|
|
/// * Pressing Enter pushes the current input in the history of previous
|
|
/// messages
|
|
extern crate termion;
|
|
extern crate tui;
|
|
extern crate unicode_width;
|
|
|
|
#[allow(dead_code)]
|
|
mod util;
|
|
|
|
use std::io::{self, Write};
|
|
|
|
use termion::cursor::Goto;
|
|
use termion::event::Key;
|
|
use termion::input::MouseTerminal;
|
|
use termion::raw::IntoRawMode;
|
|
use termion::screen::AlternateScreen;
|
|
use tui::backend::TermionBackend;
|
|
use tui::layout::{Constraint, Direction, Layout};
|
|
use tui::style::{Color, Style};
|
|
use tui::widgets::{Block, Borders, List, Paragraph, Text, Widget};
|
|
use tui::Terminal;
|
|
use unicode_width::UnicodeWidthStr;
|
|
|
|
use util::event::{Event, Events};
|
|
|
|
/// App holds the state of the application
|
|
struct App {
|
|
/// Current value of the input box
|
|
input: String,
|
|
/// History of recorded messages
|
|
messages: Vec<String>,
|
|
}
|
|
|
|
impl Default for App {
|
|
fn default() -> App {
|
|
App {
|
|
input: String::new(),
|
|
messages: Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<(), failure::Error> {
|
|
// Terminal initialization
|
|
let stdout = io::stdout().into_raw_mode()?;
|
|
let stdout = MouseTerminal::from(stdout);
|
|
let stdout = AlternateScreen::from(stdout);
|
|
let backend = TermionBackend::new(stdout);
|
|
let mut terminal = Terminal::new(backend)?;
|
|
|
|
// Setup event handlers
|
|
let events = Events::new();
|
|
|
|
// Create default app state
|
|
let mut app = App::default();
|
|
|
|
loop {
|
|
// Handle resize
|
|
let size = terminal.size()?;
|
|
|
|
// Draw UI
|
|
terminal.draw(|mut f| {
|
|
let chunks = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.margin(2)
|
|
.constraints([Constraint::Length(3), Constraint::Min(1)].as_ref())
|
|
.split(size);
|
|
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]);
|
|
let messages = app
|
|
.messages
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, m)| Text::raw(format!("{}: {}", i, m)));
|
|
List::new(messages)
|
|
.block(Block::default().borders(Borders::ALL).title("Messages"))
|
|
.render(&mut f, chunks[1]);
|
|
})?;
|
|
|
|
// Put the cursor back inside the input box
|
|
write!(
|
|
terminal.backend_mut(),
|
|
"{}",
|
|
Goto(4 + app.input.width() as u16, 4)
|
|
)?;
|
|
|
|
// 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();
|
|
}
|
|
_ => {}
|
|
},
|
|
_ => {}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|