Back to Ratatui

v0.28.0

src/content/docs/highlights/v0.28.md

latest8.5 KB
Original Source

https://github.com/ratatui/ratatui/releases/tag/v0.28.0

⚠️ See the breaking changes for this release.

Crossterm 0.28.0 ⬆️

Crossterm is updated to version 0.28.0, which is a semver incompatible version with the previous version (0.27.0). Ratatui re-exports the version of crossterm that it is compatible with under ratatui::crossterm, which can be used to avoid incompatible versions in your dependency list.

See this issue for more information.


Chart: Add GraphType::Bar 📊

We have introduced a new variant to GraphType, named Bar, which is designed to draw a bar for each point in the dataset:

rust
let chart = Chart::new(vec![Dataset::default()
    .data(&data)
    .marker(symbols::Marker::Dot)
    .graph_type(GraphType::Bar)]);


Async Example 📚

We added a new example which demonstrates how to use Ratatui with widgets that fetch data asynchronously.

The code is available here.


Barchart: Support Lines 📈

Previously, Axis::labels accepted a Vec<Span>. To make it more flexible, we have changed it to accept a vector of any type that can be converted into a [Line] (e.g., &str, String, &Line, Span, etc.). This means any code using conversion methods that infer the type will need to be rewritten as follows.

diff
- Axis::default().labels(vec!["a".into(), "b".into()])
+ Axis::default().labels(["a", "b"])

Terminal: try_draw

We have added a new method to Terminal called try_draw, which functions similarly to Terminal::draw but allows the render callback to be a function or closure that returns a Result instead of nothing (()).

This makes it easier to handle fallible rendering methods using the ? operator:

rust
terminal.try_draw(|frame| {
    some_method_that_can_fail()?;
    another_fallible_method()?;
    Ok(())
})?;

The method returns Result::Ok with a CompletedFrame if successful, or Result::Err with the std::io::Error that caused the failure.


Terminal: Make terminal module private 🔒

The terminal module is now private and can not be used directly. The types under this module are exported from the root of the crate.

diff
- use ratatui::terminal::{CompletedFrame, Frame, Terminal, TerminalOptions, ViewPort};
+ use ratatui::{CompletedFrame, Frame, Terminal, TerminalOptions, ViewPort};

This simplifies the public API, making it more user-friendly for those unfamiliar with Rust's re-exports and avoiding clashes with other modules named terminal in backend code.


Backend: Add get/set_cursor_position() 📍

If you implement the Backend trait yourself, you need to update the implementation and add the get/set_cursor_position methods, which indicates more clearly what about the cursor to set.

These new methods return/accept Into<Position> which can be either a Position or a (u16, u16) tuple.

rust
backend.set_cursor_position(Position { x: 0, y: 20 })?;
let position = backend.get_cursor_position()?;

terminal.set_cursor_position((0, 20))?;
let position = terminal.set_cursor_position()?;

You can remove the get/set_cursor methods from your implementation as they are deprecated and a default implementation for them exists.


Buffer: Add cell, cell_mut and index 🧩

Buffer used to access elements with buf.get(x, y) or buf.get_mut(x, y). Now, we have added support for index operators and introduced buf.cell() and buf.cell_mut() methods.

These new methods use Into<Position> for coordinates, making them easier to use and safer by returning Option<&Cell> and Option<&mut Cell>, which helps avoid panics (yay).

rust
let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));

// Access cells
let cell = buf[(0, 0)];
let cell = buf[Position::new(0, 0)];

// Get symbol
let symbol = buf.cell((0, 0)).map(|cell| cell.symbol());
let symbol = buf.cell(Position::new(0, 0)).map(|cell| cell.symbol());

// Set symbol
buf[(0, 0)].set_symbol("🐀");
buf[Position::new(0, 0)].set_symbol("🐀");
buf.cell_mut((0, 0)).map(|cell| cell.set_symbol("🐀"));
buf.cell_mut(Position::new(0, 0)).map(|cell| cell.set_symbol("🐀"));

The existing get() and get_mut() methods are now marked as deprecated.


Frame: Rename size() to area() 🔄

It is just the more correct name. 🧀

Frame::size is now deprecated.


Text: Add Add and AddAssign implementations ✏️

You can now combine Line, Span, and Text types together while inferring their types!

rust
let line = Span::raw("Red").red() + Span::raw("blue").blue();
let line = Line::raw("Red").red() + Span::raw("blue").blue();
let line = Line::raw("Red").red() + Line::raw("Blue").blue();
let text = Line::raw("Red").red() + Line::raw("Blue").blue();
let text = Text::raw("Red").red() + Line::raw("Blue").blue();

let mut line = Line::raw("Red").red();
line += Span::raw("Blue").blue();

let mut text = Text::raw("Red").red();
text += Line::raw("Blue").blue();

line.extend(vec![Span::raw("1"), Span::raw("2"), Span::raw("3")]);

Text: Remove unnecessary lifetime 🔧

The ToText trait no longer has a lifetime parameter.

This change simplifies the trait and makes it easier to implement.


List/Table: New Scroll Methods 🔽

We have implemented new scroll_down_by(u16) and scroll_up_by(u16) methods for both ListState and TableState, which allow you to scroll through the items by a specified number of positions.

rust
let mut state = ListState::default();
state.select(Some(2));
state.scroll_down_by(4);
assert_eq!(state.selected, Some(6));

let mut state = TableState::default();
state.select(Some(3));
state.scroll_up_by(3);
assert_eq!(state.selected, Some(0));

Table: Navigation Methods 🧭

You can now navigate in the Table widget by using the following methods!

rust
let mut state = TableState::default();
state.select_first();
state.select_next();
state.select_previous();
state.select_last();

This is the equivalent API as in ListState.


Tracking Benchmarks ⏲️

We started using Bencher.dev to track benchmarks over time and easily catch any regressions.

You can view our benchmarks at https://bencher.dev/console/projects/ratatui.

For discussions about future improvements, check out the tracking issue.


Check Semver Violations 🚦

We have started experimenting with cargo-semver-checks in our CI to lint our API for semver violations!

See the PR for related discussion.


Other 💼

  • Return Size from Backend::size instead of Rect (#1254)
  • Remove unnecessary synchronization in Layout cache (#1245)
    • Layout::init_cache no longer returns bool and takes a NonZeroUsize instead of usize
  • Remove unnecessary allocations when creating Lines (#1237)
  • Avoid extra allocations when rendering List & Table(#1244 & #1242)
  • Add Size::ZERO and Position::ORIGIN constants to Layout (#1253)
  • Enable serde for Margin, Position, Rect and Size (#1255)
  • Prevent area mismatch in TestBackend (changes the serde representation) (#1252)
  • Allow removing all the axis labels in Chart (#1282)
  • Only apply style to first line when rendering a Line (#1247)
  • Ensure emojis are rendered (#1258)
  • Add Code of Conduct (#1279)

"If you are what you eat, then I only want to eat the good stuff." – Remy