4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Building a terminal application with tui-rs

Posted at

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 and pancurses) 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 and rows will be used to construct Table. 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() and io::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

Screen Shot 2019-12-14 at 15.48.57.png

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.

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?