What is tui-rs
tui-rs
is a Rust library for building terminal applications. It provides several builtin widgets including: List
, Table
, Paragraph
etc, which meet most of use cases. There is a demo from the master repository.
Features
- The crate supports 4 backends(
termion
,rustbox
,crossterm
andpancurses
) to draw to terminal. But the crate itself doesn't provide interface for input handling, you need to make use of one of the backends cited directly. -
tui-rs
re-draw new frame for the entire UI on each iteration. But no need to worry about the overhead for highly dynamic contents due to the implementation of the crate itself as well as the speed of Rust.
Example
Here I'll give an example how to build a simple terminal application with tui-rs
and backend termion
.
initialize
// 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)?;
terminal.hide_cursor()?;
handle event loop and application states
loop {
terminal.draw(|mut f| {
// contents of the new frame
// ...
});
// event handling
// ...
}
draw table
- style: here are the colors provided.
let selected_style = Style::default().fg(Color::Yellow).modifier(Modifier::BOLD);
let normal_style = Style::default().fg(Color::White);
- contents:
hearder
androws
will be used to constructTable
.Iterator
should be implemented for both of them.
let header = ["Header1", "Header2", "Header3"];
let rows = app.items.iter().enumerate().map(|(i, item)| {
if i == app.selected {
Row::StyledData(item.into_iter(), selected_style)
} else {
Row::StyledData(item.into_iter(), normal_style)
}
});
- table: here is API for
Table
widget. We need to set up the layout and then pass the area for table in.render()
.
let rects = Layout::default()
.constraints([Constraint::Percentage(100)].as_ref())
.margin(5)
.split(f.size());
Table::new(header.into_iter(), rows)
.block(Block::default().borders(Borders::ALL).title("Table"))
.widths(&[
Constraint::Percentage(50),
Constraint::Length(30),
Constraint::Max(10),
])
.render(&mut f, rects[0]);
event handling
- implement event wrapper with
mpsc::channal()
andio::stdin()
let (tx, rx) = mpsc::channel();
let input_handle = {
let tx = tx.clone();
thread::spawn(move || {
let stdin = io::stdin();
for evt in stdin.keys() {
match evt {
Ok(key) => {
if let Err(_) = tx.send(Event::Input(key)) {
return;
}
if key == config.exit_key {
return;
}
}
Err(_) => {}
}
}
})
};
- events detection
match events.next()? {
Event::Input(key) => match key {
Key::Char('q') => {
break;
}
Key::Down => {
app.selected += 1;
if app.selected > app.items.len() - 1 {
app.selected = 0;
}
}
Key::Up => {
if app.selected > 0 {
app.selected -= 1;
} else {
app.selected = app.items.len() - 1;
}
}
_ => {}
},
_ => {}
};
result
Conclusion
tui-rs
make it easy to build a terminal application. You can not only build a GUI with the provided widgets, but also write your own to achieve highly-interactive features.