Command-line tools are the programmer’s secret weapon. They are easy to install, quick to start up, and have a simple interface that can be completed with a single command or shortcut key.

And the best way to make their own! Tui.rs is a cross-platform library that will give you a perfect command line interface in no time

Project address: github.com/fdehau/tui-…

Official documentation: docs. Rs /tui/latest/…

You’ve probably had this dilemma: My application needs an interface, but using a framework like Qt is tedious. Now here comes TUi.RS, Rust’s command-line UI library that is easy to use with a variety of components built into it, and cool enough to use across platforms.

Easy to implement a piece of code that runs seamlessly on Linux/Windows/Mac!

Not only will you be able to pick up the TUi. RS quickly, but you will also be able to gain a variety of powerful tools built on it!

A, install,

Tui.rs is written in Rust, and like all other Rust dependencies, you can simply add a dependency to cargo. Toml:

[dependencies]
tui = "0.17"
crossterm = "0.22"
Copy the code

If the official example is needed, clone the official repository directly:

$ git clone http://github.com/fdehau/tui-rs.git
$ cd tui-rs
$ cargo run --example demo
Copy the code

Quick start

2.1 At a glance

We mainly use the following modules provided by TUi.RS for UI writing (all UI elements implement widgets or StatefuWidget traits) :

  • bakendA back-end for generating the management command line
  • layoutUsed to manage the layout of UI components
  • styleUse to add styles to the UI
  • symbolsDescribes the style of points used to draw a scatter plot
  • textUsed to describe styled text
  • widgetsContains predefined UI components

The following code implements a very simple TUI interface:

use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use std::{io, time::Duration};
use tui::{
    backend::{Backend, CrosstermBackend},
    layout::{Alignment, Constraint, Direction, Layout},
    style::{Color, Modifier, Style},
    text::{Span, Spans, Text},
    widgets::{Block, Borders, Paragraph, Widget},
    Frame, Terminal,
};

struct App {
    url: String.// Store some data or UI state
}
fn main() - >Result<(), io::Error> {
    // Initialize the terminalenable_raw_mode()? ;let mutstdout = io::stdout(); execute! (stdout, EnterAlternateScreen, EnableMouseCapture)? ;let backend = CrosstermBackend::new(stdout);
    let mutterminal = Terminal::new(backend)? ;let mut app = App {
        url: String::from(r"https://hellogithub.com/"),};// Render the interface
    run_app(&mutterminal, app)? ;// Restore the terminaldisable_raw_mode()? ; execute! ( terminal.backend_mut(), LeaveAlternateScreen, DisableMouseCapture )? ; terminal.show_cursor()? ;Ok(())}fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result< > () {loop {
        terminal.draw(|f| ui(f, &mutapp))? ;// Handle keystroke events
        if crossterm::event::poll(Duration::from_secs(1))? {
            if let Event::Key(key) = event::read()? {
                match key.code {
                    KeyCode::Char(ch) => {
                        if 'q' == ch {
                            break; }} _ => {}}}}// Handle other logic
    }
    Ok(())}fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
    //
    let chunks = Layout::default() // Get the default construct first
        .constraints([Constraint::Length(3), Constraint::Min(3)].as_ref()) // Divide the area according to the rule of 3 lines and minimum 3 lines
        .direction(Direction::Vertical) // Vertical split
        .split(f.size()); // Split the entire Terminal area
    let paragraph = Paragraph::new(Span::styled(
        app.url.as_str(),
        Style::default().add_modifier(Modifier::BOLD),
    ))
    .block(Block::default().borders(Borders::ALL).title("HelloGitHub"))
    .alignment(tui::layout::Alignment::Left);
    f.render_widget(paragraph, chunks[0]);

    let paragraph = Paragraph::new("Share interesting, entry-level Open Source projects on GitHub")
        .style(Style::default().bg(Color::White).fg(Color::Black))
        .block(Block::default().borders(Borders::ALL).title("Purpose"))
        .alignment(Alignment::Center);
    f.render_widget(paragraph, chunks[1]);
}
Copy the code

This code may seem like a lot, but most of it is set in stone and doesn’t require every redesign. Let’s take a closer look at the details.

2.2 Creating a Template

The official template for using TUi.RS design is provided by example. I hope readers can follow this template to ensure the readability of the program.

A lifetime using tui. Rs might look something like this:

Its modules can be roughly divided into:

  • app.rsImplement App structure, used to process UI logic, save UI state
  • ui.rsImplement UI rendering function

But for small programs, you can also write them all in main.rs.

First, we will look at the Terminal operations at the beginning and end. Each run will save the original Terminal interface content and run it on a new form, and then restore it to the original Terminal form at the end, effectively preventing the original window content from being messed up. This part of the code template has been officially provided and basically needs no modification:

fn main() - >Result<(), io::Error> {
    / / configuration Terminalenable_raw_mode()? ;// Start the raw mode of the command line
    let mutstdout = io::stdout(); execute! (stdout, EnterAlternateScreen, EnableMouseCapture)? ;// Run the UI on a new interface, save the original terminal content, and enable mouse capture
    let backend = CrosstermBackend::new(stdout);
    let mutterminal = Terminal::new(backend)? ;// Initialize app resources
    let mut app = App {
        url: String::from(r"https://hellogithub.com/"),};// Program main logic loop...... //
    run_app(&mutterminal, app)? ;/ / recovery Terminaldisable_raw_mode()? ;// Disable raw modeexecute! ( terminal.backend_mut(), LeaveAlternateScreen,// Revert to the original command line window
        DisableMouseCapture		// Disable mouse capture)? ; terminal.show_cursor()? ;// Display the cursor

    Ok(())}Copy the code

Next comes the run_app function that handles UI logic, such as user keystrokes, UI state changes, and so on

fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result< > () {loop {
        / / rendering UI
        terminal.draw(|f| ui(f, &mutapp))? ;// Handle keystroke events
        if crossterm::event::poll(Duration::from_secs(1))? { // the poll method is non-blocking polling
            if let Event::Key(key) = event::read()? { // Read directly blocks waiting if no event arrives
                match key.code { // Determine the user's keystroke
                    KeyCode::Char(ch) => {
                        if 'q' == ch {
                            break; }} _ => {}}}}// Handle other logic
    }
    Ok(())}Copy the code

This function is not useful for simple interfaces. However, if our program needs to update some component state (such as list selections, user input, external data interaction, etc.), it should be handled uniformly here.

After that, we’ll use the terminal.draw() method to draw the interface, which accepts a closure:

fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
    // Get the split window
    let chunks = Layout::default() // Get the default construct first
        .constraints([Constraint::Length(3), Constraint::Min(3)].as_ref()) // Divide the area according to the rule of 3 lines and minimum 3 lines
        .direction(Direction::Vertical) // Split vertically
        .split(f.size()); // Split the entire Terminal area
    let paragraph = Paragraph::new(Span::styled(
        app.url.as_str(),
        Style::default().add_modifier(Modifier::BOLD),
    ))
    .block(Block::default().borders(Borders::ALL).title("HelloGitHub"))
    .alignment(tui::layout::Alignment::Left);
    f.render_widget(paragraph, chunks[0]);

    let paragraph = Paragraph::new("Share interesting, entry-level Open Source projects on GitHub")
        .style(Style::default().bg(Color::White).fg(Color::Black))
        .block(Block::default().borders(Borders::ALL).title("Purpose"))
        .alignment(Alignment::Center);
    f.render_widget(paragraph, chunks[1]);
}
Copy the code

Here, there is the following flow:

  1. useLayoutGiven according to demandConstraintShred the form and get chunks. Each chunk can also be usedLayoutLet’s go ahead and split
  2. Instantiate the components, and each component is implementeddefaultMethod, we should use it firstxxx::default()Get the default object and use the default object to update the component style. For example,Block::default().borders(Borders::ALL)Style::default().bg(Color::White)And so on. This is also the official recommendation.
  3. usef.render_widgetRenders a component to a form, for components that have an existing state like a list (such as the currently selected element)f.render_stateful_widgetTo render

For details on how to use other built-in components of TUi.RS, you can check the official example file.

Note that we only care about how the UI components are displayed and what they are, and that logic should be handled in run_app so as not to disrupt the architecture or affect the UI rendering (you don’t want the UI to get stuck halfway through drawing because of some IO operation, do you?).

This concludes the introduction to TUi.RS. It is actually very easy to write UI with Tui.RS, and anyone can get started quickly by building from authoring templates and official examples.

Third, more practical tools

Here are a few popular open source projects built on TUi.RS, all of which are command line tools.

3.1 Real-time stock data

Support to view data such as different time dimensions and trading volume, real-time stock data from Yahoo.

Address: github.com/tarkah/tick…

3.2 File Transfer Tool

Supports multi-function terminal file transfer tools such as SCP, SFTP, FTP, and S3.

Address: github.com/veeso/terms…

3.3 Network Monitoring Tools

Displays the current network usage by process, connection, remote IP address, and host name.

Address: github.com/imsnif/band…

Limited by space, other open source projects are not introduced here. Interested partners can go to the project home page to find.

Four, the last

That’s all for this article, and I hope you found it useful.

Finally, thank you for reading!!

Here is HelloGitHub sharing interesting, entry-level open source projects on GitHub. Your every like, message, share is the biggest encouragement to us!