Code Monkey home page Code Monkey logo

ratatui's Introduction

Table of Contents

Demo

Ratatui

Ratatui is a crate for cooking up terminal user interfaces in Rust. It is a lightweight library that provides a set of widgets and utilities to build complex Rust TUIs. Ratatui was forked from the tui-rs crate in 2023 in order to continue its development.

Installation

Add ratatui and crossterm as dependencies to your cargo.toml:

cargo add ratatui crossterm

Ratatui uses Crossterm by default as it works on most platforms. See the Installation section of the Ratatui Website for more details on how to use other backends (Termion / Termwiz).

Introduction

Ratatui is based on the principle of immediate rendering with intermediate buffers. This means that for each frame, your app must render all widgets that are supposed to be part of the UI. This is in contrast to the retained mode style of rendering where widgets are updated and then automatically redrawn on the next frame. See the Rendering section of the Ratatui Website for more info.

You can also watch the FOSDEM 2024 talk about Ratatui which gives a brief introduction to terminal user interfaces and showcases the features of Ratatui, along with a hello world demo.

Other documentation

Quickstart

The following example demonstrates the minimal amount of code necessary to setup a terminal and render "Hello World!". The full code for this example which contains a little more detail is in the Examples directory. For more guidance on different ways to structure your application see the Application Patterns and Hello World tutorial sections in the Ratatui Website and the various Examples. There are also several starter templates available in the templates repository.

Every application built with ratatui needs to implement the following steps:

  • Initialize the terminal
  • A main loop to:
    • Handle input events
    • Draw the UI
  • Restore the terminal state

The library contains a [prelude] module that re-exports the most commonly used traits and types for convenience. Most examples in the documentation will use this instead of showing the full path of each type.

Initialize and restore the terminal

The [Terminal] type is the main entry point for any Ratatui application. It is a light abstraction over a choice of Backend implementations that provides functionality to draw each frame, clear the screen, hide the cursor, etc. It is parametrized over any type that implements the Backend trait which has implementations for Crossterm, Termion and Termwiz.

Most applications should enter the Alternate Screen when starting and leave it when exiting and also enable raw mode to disable line buffering and enable reading key events. See the backend module and the Backends section of the Ratatui Website for more info.

Drawing the UI

The drawing logic is delegated to a closure that takes a Frame instance as argument. The Frame provides the size of the area to draw to and allows the app to render any Widget using the provided render_widget method. See the Widgets section of the Ratatui Website for more info.

Handling events

Ratatui does not include any input handling. Instead event handling can be implemented by calling backend library methods directly. See the Handling Events section of the Ratatui Website for more info. For example, if you are using Crossterm, you can use the crossterm::event module to handle events.

Example

use std::io::{self, stdout};

use crossterm::{
    event::{self, Event, KeyCode},
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
    ExecutableCommand,
};
use ratatui::{prelude::*, widgets::*};

fn main() -> io::Result<()> {
    enable_raw_mode()?;
    stdout().execute(EnterAlternateScreen)?;
    let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;

    let mut should_quit = false;
    while !should_quit {
        terminal.draw(ui)?;
        should_quit = handle_events()?;
    }

    disable_raw_mode()?;
    stdout().execute(LeaveAlternateScreen)?;
    Ok(())
}

fn handle_events() -> io::Result<bool> {
    if event::poll(std::time::Duration::from_millis(50))? {
        if let Event::Key(key) = event::read()? {
            if key.kind == event::KeyEventKind::Press && key.code == KeyCode::Char('q') {
                return Ok(true);
            }
        }
    }
    Ok(false)
}

fn ui(frame: &mut Frame) {
    frame.render_widget(
        Paragraph::new("Hello World!")
            .block(Block::default().title("Greeting").borders(Borders::ALL)),
        frame.size(),
    );
}

Running this example produces the following output:

docsrs-hello

Layout

The library comes with a basic yet useful layout management object called Layout which allows you to split the available space into multiple areas and then render widgets in each area. This lets you describe a responsive terminal UI by nesting layouts. See the Layout section of the Ratatui Website for more info.

use ratatui::{prelude::*, widgets::*};

fn ui(frame: &mut Frame) {
    let main_layout = Layout::new(
        Direction::Vertical,
        [
            Constraint::Length(1),
            Constraint::Min(0),
            Constraint::Length(1),
        ],
    )
    .split(frame.size());
    frame.render_widget(
        Block::new().borders(Borders::TOP).title("Title Bar"),
        main_layout[0],
    );
    frame.render_widget(
        Block::new().borders(Borders::TOP).title("Status Bar"),
        main_layout[2],
    );

    let inner_layout = Layout::new(
        Direction::Horizontal,
        [Constraint::Percentage(50), Constraint::Percentage(50)],
    )
    .split(main_layout[1]);
    frame.render_widget(
        Block::default().borders(Borders::ALL).title("Left"),
        inner_layout[0],
    );
    frame.render_widget(
        Block::default().borders(Borders::ALL).title("Right"),
        inner_layout[1],
    );
}

Running this example produces the following output:

docsrs-layout

Text and styling

The Text, Line and Span types are the building blocks of the library and are used in many places. Text is a list of Lines and a Line is a list of Spans. A Span is a string with a specific style.

The style module provides types that represent the various styling options. The most important one is Style which represents the foreground and background colors and the text attributes of a Span. The style module also provides a Stylize trait that allows short-hand syntax to apply a style to widgets and text. See the Styling Text section of the Ratatui Website for more info.

use ratatui::{prelude::*, widgets::*};

fn ui(frame: &mut Frame) {
    let areas = Layout::new(
        Direction::Vertical,
        [
            Constraint::Length(1),
            Constraint::Length(1),
            Constraint::Length(1),
            Constraint::Length(1),
            Constraint::Min(0),
        ],
    )
    .split(frame.size());

    let span1 = Span::raw("Hello ");
    let span2 = Span::styled(
        "World",
        Style::new()
            .fg(Color::Green)
            .bg(Color::White)
            .add_modifier(Modifier::BOLD),
    );
    let span3 = "!".red().on_light_yellow().italic();

    let line = Line::from(vec![span1, span2, span3]);
    let text: Text = Text::from(vec![line]);

    frame.render_widget(Paragraph::new(text), areas[0]);
    // or using the short-hand syntax and implicit conversions
    frame.render_widget(
        Paragraph::new("Hello World!".red().on_white().bold()),
        areas[1],
    );

    // to style the whole widget instead of just the text
    frame.render_widget(
        Paragraph::new("Hello World!").style(Style::new().red().on_white()),
        areas[2],
    );
    // or using the short-hand syntax
    frame.render_widget(Paragraph::new("Hello World!").blue().on_yellow(), areas[3]);
}

Running this example produces the following output:

docsrs-styling

Status of this fork

In response to the original maintainer Florian Dehau's issue regarding the future of tui-rs, several members of the community forked the project and created this crate. We look forward to continuing the work started by Florian 🚀

In order to organize ourselves, we currently use a Discord server, feel free to join and come chat! There is also a Matrix bridge available at #ratatui:matrix.org.

While we do utilize Discord for coordinating, it's not essential for contributing. Our primary open-source workflow is centered around GitHub. For significant discussions, we rely on GitHub — please open an issue, a discussion or a PR.

Please make sure you read the updated contributing guidelines, especially if you are interested in working on a PR or issue opened in the previous repository.

Rust version requirements

Since version 0.23.0, The Minimum Supported Rust Version (MSRV) of ratatui is 1.67.0.

Widgets

Built in

The library comes with the following widgets:

Each widget has an associated example which can be found in the Examples folder. Run each example with cargo (e.g. to run the gauge example cargo run --example gauge), and quit by pressing q.

You can also run all examples by running cargo make run-examples (requires cargo-make that can be installed with cargo install cargo-make).

Third-party libraries, bootstrapping templates and widgets

  • ansi-to-tui — Convert ansi colored text to ratatui::text::Text
  • color-to-tui — Parse hex colors to ratatui::style::Color
  • templates — Starter templates for bootstrapping a Rust TUI application with Ratatui & crossterm
  • tui-builder — Batteries-included MVC framework for Tui-rs + Crossterm apps
  • tui-clap — Use clap-rs together with Tui-rs
  • tui-log — Example of how to use logging with Tui-rs
  • tui-logger — Logger and Widget for Tui-rs
  • tui-realm — Tui-rs framework to build stateful applications with a React/Elm inspired approach
  • tui-realm-treeview — Treeview component for Tui-realm
  • tui-rs-tree-widgets — Widget for tree data structures.
  • tui-windows — Tui-rs abstraction to handle multiple windows and their rendering
  • tui-textarea — Simple yet powerful multi-line text editor widget supporting several key shortcuts, undo/redo, text search, etc.
  • tui-input — TUI input library supporting multiple backends and tui-rs.
  • tui-term — A pseudoterminal widget library that enables the rendering of terminal applications as ratatui widgets.

Apps

Check out awesome-ratatui for a curated list of awesome apps/libraries built with ratatui!

Alternatives

You might want to checkout Cursive for an alternative solution to build text user interfaces in Rust.

Acknowledgments

Special thanks to Pavel Fomchenkov for his work in designing an awesome logo for the ratatui project and ratatui-org organization.

License

MIT

ratatui's People

Contributors

a-kenji avatar abusch avatar cjbassi avatar defiori avatar dependabot[bot] avatar dflemstr avatar dreadedhippy avatar edjopato avatar eeelco avatar emivvvvv avatar fdehau avatar hasezoey avatar josephfknight avatar joshka avatar joshrotenberg avatar karolinepauls avatar karthago1 avatar kdheepak avatar lunderberg avatar mindoodoo avatar nyurik avatar orhun avatar rhysd avatar sayanarijit avatar sectore avatar stevensonmt avatar thelostlambda avatar tieway59 avatar timonpost avatar valentin271 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ratatui's Issues

Block: `block.outer()` function

Problem

Sometimes one may want to adjust the outer block fluidly according to the inner components size, in which case inner_block.outer() is useful.
Not sure if there's already a workaround.

Solution

Similar to Block::inner, implement a Block::outer function.

Additional context

For example, a popup for user inputs.
The user input block is an inner block of the popup block. And users may want automatically adjust the outer block size according to the input length.

Format checking in incorrect workflows

Description

Improper formatting causes build/run workflows to fail instead of the lint workflow. This can make it confusing for new contributors when they forget to run cargo fmt before committing.

To Reproduce

Make a pull request with a commit that is not formatted using cargo fmt.

Expected behavior

6/7 of the workflows will fail due to the improper formatting, whereas the lint one may succeed (but does not always do so).

'bleeding' of style between paragraph and block

TL;DR; Is it correct that:

  • the style of the contained paragraph should set the style of its surrounding block.
  • the style set in the block (via block.style) sets the style for the blank space in the para even if the para has its own style set.

If I have a paragraph with a block around it then there are 4 combinations of setting styles for the text and box

  para         block
  -------      ------- 
  default     default
  explicit     default  
  default     explicit
  explicit     explicit      

by default I mean Style::default
by explicit i mean say Style::default().bg(Color::Red).fg(Color::Yellow)

I used

  • Style::default().bg(Color::Red).fg(Color::Yellow) for the block in my tests (setting block.border_style)
  • Style::default().bg(Color::Blue).fg(Color::Green) for the paragraph

In a modified version of the paragraph sample app. (source below)

My expectation was a follows (for each combo listed above)

  • white text on black everywhere
  • green on blue text in a white on black box
  • white on black text in a yellow on red box
  • green on blue text in a yellow on red box

What I get is

image

I also ran the same code with block.style instead of block.border_style. Nothing in the docs explains the difference between them. So I wanted to see what happened

image

This seems to be suggesting that block.style is intended to set the overall style of the para/block combo (para inherits from block), whereas block.border_style only sets the stye of the block. Fine.

But note that in the second case of both screen shots the para style has overriden the default of the block. This is a surprise

Also note the odd fact that although the para is explicity set to blue on green in the last case (of the last screen shot) the style set in the block style 'wins' in the 'blank' space

are these 2 intended behaviors?

I also tried with styled text rather that styling the paragraph (para always set to Style::default)

image

(There are only 2 combos now since the para style is always set to default but the text is set to white on blue)

Note the big difference for the second one. The style of the text is not propagated to the containing block.

and for completeness sake , style text with the block style set via block.border_style

image

here is the modified code of the paragraph sample

    let size = f.size();

    // Words made "loooong" to demonstrate line breaking.
    let s = "Veeeeeeeeeeeeeeeery    loooooooooooooooooong   striiiiiiiiiiiiiiiiiiiiiiiiiing.   ";
    let mut long_line = s.repeat(usize::from(size.width) / s.len() + 4);
    long_line.push('\n');

    //   let block = Block::default().style(Style::default()); //.bg(Color::White).fg(Color::Black));
    // f.render_widget(block, size);

    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .margin(5)
        .constraints(
            [
                Constraint::Percentage(25),
                Constraint::Percentage(25),
                Constraint::Percentage(25),
                Constraint::Percentage(25),
            ]
            .as_ref(),
        )
        .split(size);

    let text = vec![Spans::from("This is a line ")];

    let create_block = |title, dflt| {
        Block::default()
            .borders(Borders::ALL)
            .title(Span::styled(
                title,
                Style::default().add_modifier(Modifier::BOLD),
            ))
            .border_style(if dflt {
                Style::default()
            } else {
                Style::default().bg(Color::Red).fg(Color::Yellow)
            })
    };

    let paragraph = Paragraph::new(text.clone())
        .style(Style::default())
        .block(create_block("dflt dflt", true))
        .alignment(Alignment::Left);
    f.render_widget(paragraph, chunks[0]);

    let paragraph = Paragraph::new(text.clone())
        .style(Style::default().bg(Color::Blue).fg(Color::Green))
        .block(create_block("para = Gr/Bl bl = dflt", true))
        .alignment(Alignment::Left);
    f.render_widget(paragraph, chunks[1]);
    let paragraph = Paragraph::new(text.clone())
        .style(Style::default())
        .block(create_block("para = dflt, block = Y/red", false))
        .alignment(Alignment::Left);
    f.render_widget(paragraph, chunks[2]);
    let paragraph = Paragraph::new(text.clone())
        .style(Style::default().bg(Color::Blue).fg(Color::Green))
        .block(create_block("Para = b/g block = y/red ", false))
        .alignment(Alignment::Left);
    f.render_widget(paragraph, chunks[3]);
}

Environment

Add a description of the systems where you are observing the issue. For example:

  • OS: Windows
  • Terminal Emulator: windows terminal
  • Font: ?
  • Crate version: 0.20.1 (cloned current head)
  • Backend: crossterm

Why do I care about ths. Because I am writing a widget that greys out a paragraphs text temporarily. If I do that when the surrounding block is set to Style::default then it gets greyed out too. And there is no way for me to inspect the block to look at its style (there is no get_style method)

sophacles/extra-widgets - StyledList and Macros.

Problem

I have a repo of some widgets I've generalized from a couple personal projects.

Widgets:

SytledList - Similar to widgets::List, the StyledList is a list widget. It offers more
display/decoration options, optional separators between ListItems and fixes a handful
of small annoyances (e.g. a selected multi-line item will always be fully displayed).

Calendar - Display a calendar for a month. This has some styling options, such as
including day headers (M, T, W ...) and whether fill the grid with days from other months.
It also has the ability to style the individual dates.

(not actually a widget) text_macros - Some macros for reducing boilerplate when working
with larger text regions. For example, rather than:

Span::styled(
	"bold italic words",
	Style::default()
		.add_modifier(Modifier::BOLD)
		.add_modifier(Modifier::ITALIC),
);

one could write:

bold!(italic!("bold italic words"))

I'm not sure of the best way to share these.

Solutions

I see 3 possible solutions to "how do I share these?":

  • include them in the widgets module of ratatui. I'm happy to contribute them if there's interest.
  • use them to seed a sibling widget collections (ratatui-contrib or similar).
  • release them as an independent crate (extra-widgets is a terrible name, I'm open to suggestions!)

There are pros and cons to each of the approaches.

Having a project approved library of widgets is nice for users - there are more building blocks to make software from, and the widgets from the "blessed" library likely to be kept up-to-date with the ratatui core changes. This adds more work for maintainers, but also provides an easy mechanism to ensure that components don't lag behind ratatui itself (e.g. if a widget crate builds against ratatui version x, but a project builds against ratatui version y, and wants to include the widget, there may be compatibility issues).

Having individual crates for widgets allows for more exploration and various ways to implement a concept, and and allows for more change in any given widget - a standard library of widgets, like a standard library for a programming language, may stagnate to maintain compatability for older programs and doing each widget as an independent crate helps avoid that. It hurts discoverability and crates run the risk of being abandoned forcing users to find a new fork (at best) or a new widget.

Additional context

In the linked repo there are example programs for each widget in action. Additionally, in-depth rust docs can be generated from the repo - the --document-private-items flag provides even more info.

List: Allow alignment for `List` widget.

Problem

As far as I am aware, there is no built-in way to align a list to the center of the terminal window. The only way that I know of is to add spaces before a string within a ListItem to be able to pad it towards the right side.

Solution

Given that there's an alignment method for Paragraph, perhaps making this available across other widgets would be nice.

Method would be accessible to the List widget:

Entire list:

let list = List::new(items)
    .style(Style::default().fg(Color::White))
    .highlight_style(Style::default().add_modifier(Modifier::ITALIC))
    .alignment(Alignment::Center);

Alternatives

None that I know about.

tui::widgets::ListItem support for tui::widgets::Paragraph

Problem

I am writing a project that makes use of tui::widgets::ListItem to display commit-nodes of a git Repository. Currently, I am making use of tui::widgets::Text to display this git graph:

image

tui::widgets::Text displays a new line when \n is found on the string. It is working fine. Relevant code is here and here

However, now I'd like to add color to the left side panel. I tried to use tui::widgets::Spans to build the ListItem. Here is the new code and here you have the screen shot

image

As you can see, if a line feed char is inside a Span, ListItem won't write the next chars into a new line. But if a line feed char is inside a Text, ListItem will write the next chars into a new line.

Solution

My first thought was to make the Span widget react to a line feed char. But I think this is not the right solution, because a Span is meant to span one line, not to introduce new lines.

I saw the original tui code, and found out there is a Text#styled you can use and it -partially works for me-. Because yes, you can stylize a Text input and have line feeds inside and you will see a colored text and the line feeds in the ListItem. However, I want to colorize a -segment- of a string, not the entire string. And currently, there is no feature to build a ListItem from a Vec<Text>. I think it would be easier to build a trait to implement ListItem from a Paragraph rather than build it from a Vec<Text>

I am working on git_explorer as a side project to better understand rust, and I have already done this rg_explorer side project to practice with rust. I found very interesting the rust language and want to master it. I am willing to work on this issue, but I probably need some help to understand this source code (or at least the relevant part of this source code to build this feature).

Best

Text: Reflow module is convoluted and should be refactored / replaced

Problem

The wrapping in reflow is fairly convoluted, which makes it difficult to maintain (fix problems and add new features). This caused a problem in #193

https://github.com/tui-rs-revival/ratatui/blob/492af7a92d264a590ebbf4eb90b45657e30dfd30/src/widgets/reflow.rs#L55-L209

Solution / Constraints

  • Simplify the wrapping logic
  • Move the wrapping logic to the right place (perhaps under the text module?)
  • Implement the wrapping against a single Line to avoid having nested iterators
  • Allow the wrapping logic to be used in any widget, not just paragraph
  • Maintain backwards compatibility (allow graceful deprecation of the current methods rather than breaking all current code)
  • Spec this out before implementation (because implementing this without a spec leads to very long and painful PRs)

Tree: Integrate with tui-rs-tree-wdget

Problem

With this crate's recent release, I think it would be nice to have some more general-purpose widgets added to the current repertoire. tui-rs-tree-widget is one such widget that I think this crate would benefit from having. As discussed in this issue, both the maintainer and I are interested in this merge.

Solution

Add the Tree widget from tui-rs-tree-widget into this repository.

Bold text doesn't display correctly

Description

Ratatui doesn't seem to display bold text correctly even though both crossterm and termion do. I was seeing weird behavior and created this minimal repro case.

To Reproduce

    terminal.draw(|f| {
        let size = f.size();
        let para = Paragraph::new("This is displayed by ratatui and is not bold (but should be)")
            .style(Style::default().add_modifier(Modifier::BOLD));
        f.render_widget(para, size);
    })?;

Expected behavior

Text should be bold

Screenshots

Crossterm:
asciicast

Termion:
asciicast

Environment

  • OS: macos
  • Terminal Emulator: iterm2 / terminal.app (tried this running bash instead of zsh to ensure that it wasn't something environmental)
  • Font: Fira Code / Meno Regular
  • Crate version: 0.20
  • Backend: termion / crossterm

Additional context

Full source for repo:

use std::io::Stdout;
use std::{error, io, thread::sleep, time::Duration};

use ratatui::{
    style::{Modifier, Style},
    widgets::Paragraph,
    Terminal,
};

#[cfg(feature = "crossterm")]
use ratatui::backend::CrosstermBackend;

#[cfg(feature = "termion")]
use ratatui::backend::TermionBackend;

#[cfg(feature = "crossterm")]
use crossterm::terminal;

#[cfg(feature = "termion")]
use termion::raw::{IntoRawMode, RawTerminal};

type Result<T> = std::result::Result<T, Box<dyn error::Error>>;

#[cfg(feature = "termion")]
fn display_native() {
    use termion::style;

    println!(
        "{}This is displayed by termion and is bold{}",
        style::Bold,
        style::Reset
    );
}

#[cfg(feature = "crossterm")]
fn display_native() {
    use crossterm::style::Attribute;

    println!(
        "{}This is displayed by crossterm and is bold{}",
        Attribute::Bold,
        Attribute::Reset
    );
}

#[cfg(feature = "termion")]
fn get_backend() -> Result<TermionBackend<RawTerminal<Stdout>>> {
    let buffer = io::stdout().into_raw_mode()?;
    Ok(TermionBackend::new(buffer))
}

#[cfg(feature = "crossterm")]
fn get_backend() -> Result<CrosstermBackend<Stdout>> {
    let buffer = io::stdout();
    terminal::enable_raw_mode()?;
    crossterm::execute!(io::stdout(), terminal::EnterAlternateScreen)?;
    Ok(CrosstermBackend::new(buffer))
}

#[cfg(feature = "termion")]
fn cleanup() {}

#[cfg(feature = "crossterm")]
fn cleanup() {
    terminal::disable_raw_mode().unwrap();
    crossterm::execute!(io::stdout(), terminal::LeaveAlternateScreen).unwrap();
}

fn main() -> Result<()> {
    let backend = get_backend()?;
    let mut terminal = Terminal::new(backend)?;

    terminal.clear()?;
    display_native();

    sleep(Duration::from_secs(2));

    terminal.clear()?;
    terminal.draw(|f| {
        let size = f.size();
        let para = Paragraph::new("This is displayed by ratatui and is not bold (but should be)")
            .style(Style::default().add_modifier(Modifier::BOLD));
        f.render_widget(para, size);
    })?;

    sleep(Duration::from_secs(2));

    terminal.clear()?;
    cleanup();
    Ok(())
}

Cargo.toml:

[package]
name = "scratch"
version = "0.1.0"
edition = "2021"

[features]
default = ["crossterm"]
crossterm = ["dep:crossterm", "ratatui/crossterm"]
termion = ["dep:termion", "ratatui/termion"]

[dependencies]
termion = { version = "2.0", optional = true }
crossterm = { version = "0.26", optional = true }
ratatui = { version = "0.20.0", default-features = false }

Commands:

cargo run --no-default-features --features crossterm
cargo run --no-default-features --features termion

Move APPS.md in the wiki

          > I added [tui-rs-revival/ratatui/wiki/Apps](https://github.com/tui-rs-revival/ratatui/wiki/Apps) (which I copied from APPS.md)

It makes sense to separate something which is not directly related to the library but in that case we need to make use of the Wiki more often to make it visible. Maybe after #184 is merged, we can remove that file and add a note in README.md about it is now a part of the Wiki or something. I'm okay with either way and I think having a separate file definitely makes the application list more apparent to the user. Need some comments on this so maybe create a issue/discussion?

Originally posted by @orhun in #179 (comment)

use of undeclared crate or module `crossterm` in spite of default feature

Description

On windows, I expected to be able to run a demo with the crossterm backend after including ratatui (alone) in my dependencies, since crossterm is included as a default feature.

In Cargo.toml:

[dependencies]
ratatui = "0.20.1"

However, when I cargo build, I get an error:

use of undeclared crate or module `crossterm`

To Reproduce

Try to compile the gauge example on windows:

$ cargo build
   Compiling 
error[E0433]: failed to resolve: use of undeclared crate or module `crossterm`
 --> src\main.rs:1:5
  |
1 | use crossterm::{
  |     ^^^^^^^^^ use of undeclared crate or module `crossterm`

error[E0432]: unresolved import `crossterm`
 --> src\main.rs:1:5
  |
1 | use crossterm::{
  |     ^^^^^^^^^ use of undeclared crate or module `crossterm`

error: cannot determine resolution for the macro `execute`
  --> src\main.rs:61:5
   |
61 |     execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
   |     ^^^^^^^
   |
   = note: import resolution is stuck, try simplifying macro imports

error: cannot determine resolution for the macro `execute`
  --> src\main.rs:72:5
   |
72 |     execute!(
   |     ^^^^^^^
   |
   = note: import resolution is stuck, try simplifying macro imports

error[E0433]: failed to resolve: use of undeclared crate or module `crossterm`
  --> src\main.rs:98:12
   |
98 |         if crossterm::event::poll(timeout)? {
   |            ^^^^^^^^^ use of undeclared crate or module `crossterm`

Some errors have detailed explanations: E0432, E0433.
For more information about an error, try `rustc --explain E0432`.

Expected behavior

As crossterm is the default feature, this dependency should be installed and used automatically (unless one opts out of the default features). Right?

Environment

  • OS: Windows
  • Crate version: 0.7
  • Backend: unspecified (should default to crossterm)

Nightly releases

Problem

There might be some time gaps between new releases so the users cannot test/use the latest functionality without using ratatui as a git dependency in Cargo.toml which makes the crate unpublishable on crates.io. (crates.io doesn't accept git dependencies)

See what atuin did recently as a workaround: atuinsh/atuin@a515b06

Solution

Create automated nightly releases every week or so. ratatui can be used as e.g. version = "nightly-2023-04-21" afterwards.

Alternatives

Just plan things out and release more frequently.

Additional context

None.


We need comments/thoughts on this.

Move "apps using ratatui" to dedicated file

Considering the quantity of apps using ratatui that are referenced in our readme, maybe we could move them to a table in a separate markdown file ?

This would make the main README leaner, while at the same time enabling us to maybe give the option for adding more details since it would now be a dedicated file.

ratatui prints random bytes on exit

Description

When I execute one of the example code from your documentation, it prints random bytes on exit.

To Reproduce

use std::{io, thread, time::Duration};
use ratatui::{
    backend::CrosstermBackend,
    widgets::{Widget, Block, Borders},
    layout::{Layout, Constraint, Direction},
    Terminal
};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};

fn main() -> Result<(), io::Error> {
    // setup terminal
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;

    terminal.draw(|f| {
        let size = f.size();
        let block = Block::default()
            .title("Block")
            .borders(Borders::ALL);
        f.render_widget(block, size);
    })?;

    thread::sleep(Duration::from_millis(5000));

    // restore terminal
    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}

Expected behavior

To not print any byte to command line

Screenshots

Bildschirmfoto vom 2023-04-20 20-42-03

Environment

  • OS: Fedora 36
  • Terminal Emulator: GNOME Terminal
  • Font: Monospace
  • Crate version: 0.20.1
  • Backend: crossterm

Additional context

None

Rendering issues on demo

Description

The demo does not render correctly (after 1 frame it starts spamming the terminal with the layout code).

See here:
2023-05-30-180448_3840x1570_scrot

I'm using alacritty 0.12.1 (5fdfd47f), and running fish 3.6.1 inside tmux 3.3a on Arch Linux.

The demo fails like this with all backends, but some examples work e.g. list (albeit with some theming issues) :

2023-05-30-213245_1275x1573_scrot

Config details:
~/.config/alacritty/alacritty.yml:

window:
  decorations: none
scrolling:
  history: 0
font:
  normal:
    family: "Noto Sans Mono"
  size: 13.0
colors:
  primary:
    background: '0x151515'
    foreground: '0xd0d0d0'
  cursor:
    text: '0x151515'
    cursor: '0xd0d0d0'
  normal:
    black:   '0x151515'
    red:     '0xac4142'
    green:   '0x90a959'
    yellow:  '0xf4bf75'
    blue:    '0x6a9fb5'
    magenta: '0xaa759f'
    cyan:    '0x75b5aa'
    white:   '0xd0d0d0'
  bright:
    black:   '0x505050'
    red:     '0xd28445'
    green:   '0x202020'
    yellow:  '0x303030'
    blue:    '0xb0b0b0'
    magenta: '0xe0e0e0'
    cyan:    '0x8f5536'
    white:   '0xf5f5f5'
draw_bold_text_with_bright_colors: false
hints:
  enabled:
   - regex: "(ipfs:|ipns:|magnet:|mailto:|gemini:|gopher:|https:|http:
   ..|news:|file:|git:|ssh:|ftp:)\
             [^\u0000-\u001F\u007F-\u009F<>\"\\s{-}\\^⟨⟩`]+"
     command: chromium
     post_processing: true
     mouse:
       enabled: true
       mods: None
     binding:
       key: U
       mods: Control|Shift
key_bindings:
  - { key: V,        mods: Alt, action: Paste                            }
  - { key: C,        mods: Alt, action: Copy                             }
  - { key: V,        mods: Control|Shift,    action: ReceiveChar               }
  - { key: C,        mods: Control|Shift,    action: ReceiveChar                }

~/.tmux.conf :

# set scroll history to 100,000 lines
set-option -g history-limit 100000

# modern colors
set -g default-terminal "tmux-256color"
set -ga terminal-overrides ",alacritty:Tc"

# unbind the prefix and bind it to Ctrl-a like screen
# unbind C-b
# set -g prefix C-a
# bind C-a send-prefix
bind '"' split-window -c "#{pane_current_path}"
bind % split-window -h -c "#{pane_current_path}"
bind c new-window -c "#{pane_current_path}"

# copy to X clipboard
bind -T copy-mode-vi v send -X begin-selection
bind -T copy-mode-vi y send-keys -X copy-pipe 'xclip -in -selection clipboard &> /dev/null'
bind -T copy-mode-vi Enter send-keys -X cancel

# shortcut for moving tmux buffer to clipboard
# useful if you've selected with the mouse
bind-key -nr C-y run "tmux show-buffer | xclip -in -selection clipboard &> /dev/null"

# Avoid ESC delay
set -s escape-time 0

# Fix titlebar
set -g set-titles on
set -g set-titles-string "#T"

# VIM mode
set -g mode-keys vi

# Mouse friendly
set -g mouse on

# Move between panes with vi keys
bind h select-pane -L
bind j select-pane -D
bind k select-pane -U
bind l select-pane -R

set -g status-right ''
set -g status-right-length 0

source-file ~/.config/base16/tmux/base16-classic-dark.conf

~/.config/base16/tmux/base16-classic-dark.conf:

set-option -g status-style "fg=#B0B0B0,bg=#202020"
set-window-option -g window-status-style "fg=#B0B0B0,bg=default"
set-window-option -g window-status-current-style "fg=#F4BF75,bg=default"
set-option -g pane-border-style "fg=#202020"
set-option -g pane-active-border-style "fg=#303030"
set-option -g message-style "fg=#D0D0D0,bg=#202020"
set-option -g display-panes-active-colour "#90A959"
set-option -g display-panes-colour "#F4BF75"
set-window-option -g clock-mode-colour "#90A959"
set-window-option -g mode-style "fg=#B0B0B0,bg=#303030"
set-window-option -g window-status-bell-style "fg=#202020,bg=#AC4142"

To Reproduce

Above run with:
cargo run --example demo --release -- --tick-rate 200
from master with:
cargo 1.71.0-nightly (ac8401032 2023-05-02)
rustc 1.71.0-nightly (74c482104 2023-05-04)

Expected behavior

The demo should render correctly.

Screenshots

Environment

  • OS: Linux (Arch Linux)
  • Terminal Emulator: Alacritty
  • Font: Noto Sans Mono
  • Crate version: master
  • Backend: crossterm (default)

Additional context

The strange thing is that it works okay on my laptop with a similar setup (but more recent config), I haven't had time to compare them directly yet though.

Line/Text is defined multiple times

Description

I was trying to migrate my project from tui to this fork and i had everything working but for some reason my project started complaining that crates are not resolved so i've added them but that make ratatui to trow errors about multiple implementation of Line/Text. To reiterate it was working just few hours before and i don't think i did anything werid.

To Reproduce

Honestly have no idea haw to reproduce this issue.

Expected behavior

No conflicts in definiton of List/Text

Screenshots

image
Screenshot from 2023-05-31 22-26-30

Environment

  • OS: Linux
  • Terminal Emulator: kitty
  • Font:
  • Crate version: 0.21.0
  • Backend: crossterm

Additional context

Setup git precommit hooks for lint issues

Problem

We often see action check failures due to conventional commits, cargo clippy lints, and rust format.

Solution

There's a bunch of ways to have pre-commit git hooks that check rust format, clippy, and conventional commits. Evaluate the options and choose the appropriate one to implement. I'm not sure which one is the best / most used / idiomatic.

Alternatives

None

Additional context

Installation of any required tooling should be low impact. Bonus points for borrowing the config of some reasonably stable project.

Backend: Termwiz demo redraws more than it should

Description

The termwiz demo repaints more than it should.
This might be an issue in the demo, or in the termwiz backend.

To Reproduce

Inspect:

cargo r --example demo --features termwiz --no-default-features

vs:

cargo r --example demo --features termion --no-default-features
cargo r --example demo

Expected behavior

Ideally there should not be an obvious difference between the different backends in the example demo.

Screenshots

termwiz_ratatui.webm

System

This is on a system, that doesn't have access to hardware acceleration, which I believe makes this issue more obvious.

Marker to draw shapes without gaps in the vertical direction on canvas

Problem

When I draw lines in vertical direction on a canvas with block as marker then I have gaps between the blocks.
In my view this in not consistent because a line has no gaps horizontall

Here is a code example on a gist of mine.
This gist produces the following output onto the terminal, see picture under additional context.

In the picture the red line is fine, however the blue line in the vertical direction produces gaps which I do not want.

Solution

  • Implement Marker::Block in a way that there are no vertical gaps in a vertical line for example.

Alternatives

  • Implement a new marker like Block however it does not produce gaps in horizontal or vertical direction.

Additional context

For the example I have used this respo as tui dependency with the main branch on the commit 73f7f16

vertical_line_with_gaps

Question about the fork

Hello all, I've been following fdehau/tui-rs#654 with interest!

I maintain an RSS/Atom reader called Russ that is built on tui-rs (please feel free to add it to the list of projects using tui-rs, as I am extremely pleased with and grateful for tui-rs).

My question is this: given the fork, with regard to bug reports/feature requests/PRs, should I file those here, or upstream? Is the plan to merge the work in this fork upstream, or have this fork eventually replace upstream? Thank you to everyone actively working to keep tui-rs going, it's such a lovely project.

Reduce boilerplate on every `style()` call

Problem

There's a lot of boilerplate for common methods like style():

Paragraph::new("foo").style(Style::default().fg(Color::Blue).bg(Color::Red).add_modifier(Modifier::BOLD);

Solution

Make it easier to specify styles similar to Crossterm's Stylize

Paragraph::new("foo").blue().on_red().bold();

I think this can probably be implemented across every widget that supports style by adding a Styled trait and implementing a Stylize trait that has all the right methods (in crossterm these are generated using macros rather than the repetition I've written here).

pub trait Styled {
    fn style(&self) -> Style;
    fn set_style(self, style: Style) -> Self;
}

pub trait Stylize {
    fn blue(self) -> Self;
    fn on_red(self) -> Self;
    fn bold(self) -> Self;
}

impl<T> Stylize for T
where
    T: Styled,
{
    fn blue(self) -> Self {
        let style = self.style().fg(Color::Blue);
        self.set_style(style)
    }

    fn on_red(self) -> Self {
        let style = self.style().bg(Color::Red);
        self.set_style(style)
    }

    fn bold(self) -> Self {
        let style = self.style().add_modifier(Modifier::BOLD);
        self.set_style(style)
    }
}

impl<'a> Styled for Paragraph<'a> {
    fn style(&self) -> Style {
        self.style
    }
    fn set_style(self, style: Style) -> Paragraph<'a> {
        Paragraph::style(self, style)
    }
}

Alternatives

Perhaps:

Paragraph::new("foo").stylize().blue().on_red().bold();

Not sure about other options that would make sense.

Questions

  1. How should widgets that have multiple style methods (for different parts of the widget) act?

Additional context

This is a RFC. There are probably better ways of doing this, and I want to consider them before implementing this approach. Perhaps there's some magic that we can do using AsRef / Deref for this?

Text: Need better text handling

Problem

The way that this crate handles text is flawed, in my opinion. There are two major pain points and one minor pain point that I have encountered when dealing with Text, Spans, and Span.

Major pain points

  • Text is an ambiguous term to describe "a collection of lines." Spans is a bad name because it creates confusion between Spans and Spans. This is especially an issue in code comments, where markdown's inline code blocks are not supported.
  • Spans should not have an unnamed field. Spans.0 makes code unreadable in a way that is entirely unnecessary; the reason I suspect this has not been changed is because it is a breaking change. But I believe it to be a warranted one. It is my opinion that no crate should have publicly-exposed unnamed fields.

Minor pain points

  • The fact that representing line breaks requires turning your Span into a Spans and placing it in a Text is rather annoying. I call this a minor pain point because, although it makes manipulating text (especially existing text) rather unwieldy, it may have a good reason for being implemented the way it is. Feedback on this would be appreciated.

Solution

  • Spans needs to be renamed to Line.
  • Spans/Line needs a named field. If the previous suggestion is adopted, I would recommend Line.spans.
  • It may be a good idea to rename Text to something else - though I'm not really aware of any specific name for a collection of lines.
  • This will be a breaking change, so I think that general text-manipulation improvements should be bundled into a minor version release like 0.21.0.

Alternatives

I'm not sure what alternatives to this feature would be - feel free to let me know any ideas you have.

Support conversion from `String` to `Color`

Problem

In my projects I sometimes need a conversion like this:

"black" => TuiColor::Black,
"red" => TuiColor::Red,
"green" => TuiColor::Green,

Solution

Implement FromStr for ratatui::style::Color.

An example can be found here.

In the example, I used rgb crate for converting HEX values to Color as well. Since it introduces a new dependency, I'm not sure if we should do that as well.

Alternatives

None.

Additional context

I also did something like this in the past: https://github.com/orhun/kmon/blob/66f27006fed614c048469b2511b600d07c8912d6/src/style.rs#L110-L152

RFC: Design of Scrollable Widgets

Problem

There are a few widgets that need scrolling behavior. It would be wasteful to implement this on each widget rather than doing it properly once. There is existing scroll behavior in Table, Paragraph and List. There are a few issues / PRs related to scrolling currently in the queue

Current Behavior

Paragraph:

  • does not impl StatefulWidget
  • implements scrolling as an x,y offset (Paragraph::default().scroll((1,2)))
  • renders vertical scroll, by ignoring lines before the scroll offset and after the render area
  • renders horizontal scroll by chopping the left part of the line off
  • has no scroll bar

Table:

  • impl StatefulWidget with TableState: { selected, offset }
  • implements scrolling as a single vertical offset to the row that will be at the top of the scroll view
  • overrides the offset to ensure that the selected row is visible
  • each row has a height field (longer cells are truncated)
  • each row can have a bottom margin
  • the table can have a header which is an always visible row
  • has no scroll bar
  • renders only complete rows

List

  • impl StatefulWidget with Liststate: { selected, offset }
  • implements scrolling as a single vertical offset to the item that will be at the top of the scroll view
  • overrides the offset to ensure that the selected item is visible
  • each item has a calculated height based on the text content
  • has a PR for padding and truncation
  • has a PR for implementing widgets as items (which can be variable height)
  • has no scroll bar
  • renders only complete rows (though there is a PR for truncation

Elsewhere:

tui_rs_tree_widget::Tree

  • impl StatefulWidget with TreeState { selected, offset, opened }
  • scrolling is a vertical offset to item that will be at top of scroll view
  • overrides to ensure selected is visible
  • calculates item heights based on text.height()
  • only renders full items
  • no scroll bar

tui-logger

  • TuiWidgetInnerState has an offset
  • Not 100% sure how it renders just taking a small look at the code, but seems to have a wrapped lines concept, so I'm assuming probably similar to the calculations above
  • similar to above

tui-textarea

tui-input

  • just provides the scroll part, not the widget part
  • handles scroll as just the horizontal position of some text to be displayed by whatever method is useful. (Example uses a paragraph and passes the value from visual_scroll to the paragraph.scroll() method)

I'm sure there are other things to look at. Please add them if you know of them.

Features of solution

Brainstorming ideas
TODO: refine these as must-have, should-have, could-have, won't have (MoSCoW)

  1. Will be used by each ratatui widgets that currently scroll: Paragraph, List, Table
  2. Can be used by those that don't currently: Calendar, Barchart, Chart, Sparkline?
  3. Can be used by external widgets that need scroll behavior
  4. Scrolling based on a line / row / item basis
  5. Supports non-scrolled areas in a widget (e.g. table or list header / footer)
  6. Supports scrolling a specified item into view (e.g. selected list item, top / bottom item)
  7. Supports single line / row / item scrolling (possibly both line and item in the same widget)
  8. Supports multiple scrolling (mouse scroll is often nice to scroll by some amount > 1)
  9. Supports scrolling by partial / full page
  10. Supports / guides truncation / partial rendering of items
  11. Only supports the StatefulWidget approach (not Widget)
  12. Should avoid having to implement the same logic on each widget (e.g. struct over trait when reasonable)
  13. Scrollbar
  14. Hide scrollbar (for helping clipboard copy work)
  15. Height calculation
  16. Visible items calculation
  17. Querying scroll direction (useful in a list, where scrolling up / down should show the full new item)
  18. Smooth scroll (scrolling to an item / page in steps based on tick rate)
  19. Should not alter existing traits to implement
  20. Should be able to implement existing widget scroll config on top of this (and mark calls as deprecated to guide users to new implementation)
  21. Should be keyboard and mouse friendly
  22. Horizontal and vertical scroll bars
  23. Show scrollbar options should be { Always, Auto, Never }

Questions / things to look into / choices

  1. Are there requirements / constraints that I've missed?
  2. Should the scroll methods return a Result to indicate when they cannot scroll in the requested direction? Or should they just let the render method just fix the invalid scroll?
    a. if the scroll returns a result, we can beep / flash / take some action to load more results when a user tries to scroll past the end / beginning of a list of items.
  3. ScrollState or ViewPort approach?
  4. Should the behavior for scrolling by items be seperated from the behavior for scrolling by lines?
  5. Struct vs Trait for scroll behavior
  6. How much of the common behavior can we pull out of widgets and into the types that support scrolling implementation (e.g. scrolling specific items into view and truncation)?
  7. Does this conflict with anything currently being worked on?
    a. Maybe the flexbox PR? #23
  8. Are there any other examples of scrolling currently being implemented elsewhere worth looking at?
    a. libraries in other languages?
  9. Should we be able to scroll tables horizontally by cells? This behavior might be useful to implement a carousel object
  10. How would infinite scroll be handled?
  11. I wonder if it's worth splitting out methods for helping with layout / item bounds into a scrollable trait widgets can implement to make it easy to calculate bounds / visibility of items.

Possible implementation approaches

  1. ScrollableWidget as a Wrapper around other widgets
    Pros:
  2. Scrollable Widgets implement a Scrollable trait which contains methods for handling scroll position
    Pros:
    • ?
      Cons:
    • Every time the widget is constructed, we need to rese
  3. ListState / TableState etc. implement a Scrollable trait, and have to handle each scroll method.
    Pros:
    • easy obvious interaction (list_state.scroll_down())
      Cons:
    • duplicate implementation in each state that handles scrolling
  4. Add a ScrollState type with all the scrolling behavior and include this in ListState, TableState etc.
    Pros:
    • no duplication
    • can put methods for calculating visible area in this
    • can use the same scroll state for multiple widgets!
      Cons:
    • extra step for handling scroll methods (list_state.scroll_state.scroll_down()

And for scrollbar:

  1. Implement scrollbar in each widget (too much duplication)
  2. Implement a ScrollBar widget that each scrollable widget can use during render.
  3. Implement the ScrollBar as part of the Block widget.

add with overflow panic in ratatui-0.21.0/src/layout.rs:464:43

Description

intersection panic at ratatui-0.21.0/src/layout.rs:464:43

To Reproduce

version:

ratatui = "=0.21.0"

source code:

fn main() {
    let test_backend = ratatui::backend::TestBackend::new(58596, 58596);
    let _rect1 = if let Ok(x) = ratatui::backend::Backend::size(&(test_backend)) {
        x
    } else {
        use std::process;
        process::exit(0);
    };
    let _rect2 = ratatui::layout::Rect::new(58596, 58596, 58368, 0);
    let _local16 = ratatui::layout::Rect::intersection(_rect1, _rect2);
}

Error message

thread 'main' panicked at 'attempt to add with overflow', /home/yxz/.cargo/registry/src/mirrors.ustc.edu.cn-61ef6e0cd06fb9b8/ratatui-0.21.0/src/layout.rs:464:43

Screenshots

image

Buffer: unicode-width and emojis

I'm not a user of helix or tui. But I found this helix issue and thought I'd mention it here:
helix-editor/helix#6012

Helix uses a fork of a subset of tui. Notably the core of Buffer is basically the same (AFAIK).

I'm pretty sure that this bug is shared by everything using the tui-style Buffer.
AFAIK, the reason is that unicode-width doesn't (or can't?) report the column width for emojis. See readme and issues:
https://github.com/unicode-rs/unicode-width

I won't have anything to contribute here, or any personal interest, really.
Perhaps you should get into contact with the helix team for this, as this effects both.

The table widget example is misguiding

Description

The table widget example is potentially misguiding users on the crossterm Events API. The table widget example uses the KeyCode enum in crossterm without filtering for the KeyEventKind::Press, which leads in events being doubled when pressing keys. It isn't a bug per se, I just don't think that was the behavior that was intended from the snippet.

line 108-115 in this file for the exact part

Enable clippy

Clippy will be disabled in #51.

This issue is to re-enable clippy with linting fixes.

Pseudoterminal Widget

Problem

I'm having a lot of difficulty trying to emulate a terminal through a Paragraph widget in my shell project https://github.com/Eyesonjune18/rush. I think that it might make sense at some point to have a dedicated terminal widget that has built-in support for ANSI sequences etc.

Solution

  • PseudoTerminal or Terminal widget type
  • Would manage process input and output passthrough using stdin/stdout/stderr
  • Support for interactive processes
  • Support for shell prompts (maybe)

Alternatives

The alternative to having a dedicated widget for this is to use a Paragraph widget, but I've ran into quite a few problems doing so - especially in terms of the features mentioned above.

Test: Add benchmarking for widgets

Problem

There might be performance issues/concerns about tui widgets and adding benchmarks is important to uncover them.

Solution

Add benches for widgets. See #22 for an example.

Alternatives

None.

Additional context

#22

Open the discussions tab

First of all, thank you for making this project revive.
I'm suggesting to enable the discussions on this repository. Indeed, people may want to ask questions, share news of this project, and perhaps announce that the original owner came back.
Well, it will be a great opportunity to exchange about this project.

List: Create a WidgetList where each item in the list is a Widget rather than Text

Problem

Sometimes the layout options available in providing a List with Text are not quite enough.

  • One example is wrapping within the list items #128
  • Another example is where I wanted to create list items that had a title, border, etc.

Solution

Create widgets::WidgetList which has WidgetListItems as a parallel to the current List widget. The algorithm for deciding what is rendered will be similar to the current list (effectively a viewport that contains the currently selected Text item, scrolled into view if necessary, and that only renders the items in the current view)

  • Backward compatibility: NA
  • Ease of use: widgets::WidgetList might be a slightly confusing name
  • consistency: seems appropropriate

Alternatives

  1. Perhaps any places where we accept Text (or even Spans) should be migrated to accept a widget instead. Does it make sense to set the title of a block to a widget perhaps? What about the highlight character of a list? Should tables contain widgets instead of text? Pros: really flexible. Cons: really big change with some difficult backwards compatibility constraints.

Additional context

My particular use case for this is for https://github.com/joshka/tooters where I want to be able to do some neat things like show images using a widget wrapped around https://docs.rs/viuer/latest/viuer/ and make URLs clickable by wrapping them in OSC 8 escape sequences (as a UrlWidget, not just as text).

asciicast

List: Add wrap for ListItem

Problem

A long line in ListItem does not break into multipe lines

Solution

Add a possibilty to wrap ListItem text, same thing as Paragraph

Alternatives

Not that I know about

Line: Allow `Line`s to be individually truncated/wrapped

Idea

Similar to #149 , one could also individually wrap different Lines inside the same paragraph. These Lines would default to the LineComposer used for the paragraph but can override that to use their own.

Use cases

The uses cases would be the same as #149. People could use this to have differently formatted lines (truncated, word wrapped, character wrapped, etc.) inside the same paragraph with some specific scrolling and styling.

Feedback

This is just an idea and probably not worth the effort if there is no demand by the user base. So we would like to have some feedback from you as both the project maintainers/contributors and users. Is this feature even desired?

`Grapheme` is not the proper term for what it is used for

Description

Grapheme, the term given to the subdivisions of a Span by the original tui-rs developer, is not the right word to describe what it actually contains. A grapheme is essentially the smallest component of a written language that has a certain phonetic sound. For instance, d is a grapheme, but so is sh. The term stops working when considering multi-symbol graphemes.

Expected behavior

It should probably be renamed to Char, or something similar, because although Unicode doesn't have chars exactly, it is at least clearer than Grapheme. Symbol may also work and is more accurate, but one of the fields is already called Symbol and I'm not a huge fan of fields that have the same name as their enclosing struct. I'm open to other suggestions.

Layout: Ability for a layout chunk to grow and shrink automatically

Problem

The layout chunk size are static by default.

Solution

Having a new constraints that allow a chunk to grow and or shrink based on its content. The new constraints should allow to define the min and max size for the chunk. Additionally, it would be great to have an indicator that the size of the chunk content is larger than the size of the chunk itself and hence enable the scrolling automatically.

Alternatives

An alternative is to update the chunk constraints for each step of the loop.

Additional context

Paragraph: Vertical alignment for Paragraphs?

Problem

Currently only horizontal alignment is supported for Paragraphs, which makes vertically positioning paragraphs very awkward.

Solution

Add some sort of vertical_align method that takes in a VAlignment type as an argument, and offset paragraph rendering vertically accordingly. The VAlignment type should at the very least look like this:

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive] // if we want to add more alignment types in the future
pub enum VAlignment {
    Top,
    Center,
    Bottom,
}

Alternatives

Users can manually currently allocate an appropriate amount of space for the paragraph with Layout, but this is not very flexible as the user has to know the height beforehand. Using some sort of Flex API would also solve the problem, though it appears that that API is still very early in development. Finally we could avoid creating a new alignment type, but the horizontal variant names could potentially confuse the user.

Switch to `main` branch

Problem

The word master has been deprecated in favor of main branch, but this repo defaults to master branch.

Solution

Switch to main branch.

Note: the prs should auto update to main branch, but might take some time to update.

MSRV is not tested on CI

Description

MSRV of this crate is 1.59.0, but it is no longer tested on CI due to #80. Now Rust 1.63 is used because earlier than 1.63 cannot build cargo-make. Since we usually don't care about old compilers when we modify source code, compatibility with old compilers can be easy broken. It should be protected by checks on CI.

To Reproduce

N/A

Expected behavior

1.59.0 should be tested on CI.

Screenshots

N/A

Environment

  • OS: Linux/macOS/Windows
  • Terminal Emulator: N/A
  • Font: N/A
  • Crate version: N/A
  • Backend: n/A

Additional context

I'll make a pull request to fix this issue.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.