v0.29.0
We are excited to announce Ratatui 0.29.0! See the breaking changes for this release here.
Big shoutout to @dekirsu for the kickass animation above! We will start improving our website soon!
Sparkline: Empty bar style π
Section titled βSparkline: Empty bar style πβYou can now distinguish between empty bars and bars with a value of 0 in the Sparkline
widget.
Before:
After:
To achieve this, we added the absent_value_style
and absent_value_symbol
functions to the
Sparkline
widget.
let widget = Sparkline::default() .absent_value_style(Style::default().fg(Color::Red)) // new! .absent_value_symbol(symbols::shade::FULL) // new! .data([ None, // absent, will be rendered as a red full block Some(1), Some(2), Some(3), Some(4), Some(5), Some(6), Some(7), Some(8), ]);let buffer = render(widget, 12);let mut expected = Buffer::with_lines(["ββββββ
βββxxx"]);expected.set_style(Rect::new(0, 0, 1, 1), Style::default().fg(Color::Red));assert_eq!(buffer, expected);
Overlapping layouts π
Section titled βOverlapping layouts πβLayout::spacing
is now generic and can take:
- Zero or positive numbers, e.g.
Layout::spacing(1)
(current functionality) - Negative number, e.g.
Layout::spacing(-1)
(new!) - Variant of the
Spacing
(new!)Spacing::Space
Spacing::Overlap
This allows creating layouts with a shared pixel for segments. When spacing(negative_value)
is
used, spacing is ignored and all segments will be adjacent and have pixels overlapping.
Here is a snippet from the implementation:
let (segments, spacers) = Layout::horizontal([Length(10), Length(10), Length(10)]) .flex(Flex::Center) .spacing(-1) // new feature .split_with_spacers(lower);
for segment in segments.iter() { frame.render_widget( crate::widgets::Block::bordered() .border_set(crate::symbols::border::DOUBLE), *segment, );}for spacer in spacers.iter() { frame.render_widget(crate::widgets::Block::bordered(), *spacer);}
You can see that drawing a border on top of an existing border overwrites it:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Future versions will enhance border drawing by combining borders to handle overlaps better.
Table: Support selecting columns and cells ποΈ
Section titled βTable: Support selecting columns and cells ποΈβYou can now select columns and cells in a Table
widget!
To select a column or cell, use the TableState
methods select_column
and select_cell
. We also
added scroll_right_by
and scroll_left_by
along with other convenience methods.
let mut state = TableState::new().with_selected_column(Some(1));state.select_first_column();state.select_next_column();state.select_previous_column();state.select_last_column();state.scroll_right_by(4);state.scroll_left_by(20);state.select_column(Some(1));state.select_cell(Some((1, 5)));
The selected column and cell styles can be set using Table::column_highlight_style
and
Table::cell_highlight_style
.
For example:
let table = Table::new(rows, [Constraint::Length(5); 3]) .highlight_symbol(">>") .row_highlight_style(Style::new().red()) .column_highlight_style(Style::new().blue());
Tabs: Support deselection π«
Section titled βTabs: Support deselection π«βTabs::select()
now accepts Into<Option<usize>>
instead of usize
. This allows tabs to be
deselected by passing None
.
let tabs = Tabs::new(vec!["Tab 1", "Tab 2"]).select(None);
However, this breaks any code already using parameter type inference:
let selected = 1u8; let tabs = Tabs::new(["A", "B"]).select(selected.into()) let tabs = Tabs::new(["A", "B"]).select(selected as usize)
Terminal: Support scrolling regions π₯οΈ
Section titled βTerminal: Support scrolling regions π₯οΈβThe current implementation of Terminal::insert_before
used to cause the viewport to flicker as
described in this issue.
We introduced a new crate feature called scrolling-regions
to address this issue. This feature
uses terminal scrolling regions to implement Terminal::insert_before
without flickering.
To enable this feature for your Viewport
, update your Cargo.toml
as follows:
[dependencies]ratatui = { version = "0.29", features = ["scrolling-regions"] }
See the implementation for more details.
Color: HSLuv support π¨
Section titled βColor: HSLuv support π¨βAfter enabling the palette
feature, you can now use the Hsluv
struct to create colors in the
HSLuv color space:
use ratatui::{palette::Hsluv, style::Color};
let color: Color = Color::from_hsluv(Hsluv::new(0.0, 100.0, 0.0));assert_eq!(color, Color::Rgb(0, 0, 0));
Canvas: draw example π¨
Section titled βCanvas: draw example π¨βWe extended the Canvas
example to include a drawing feature. You can now draw on the canvas using
your mouse:
Ratatui logo widget πΌοΈ
Section titled βRatatui logo widget πΌοΈβWe added a new widget called RatatuiLogo
that can be used to render the Ratatui logo in the
terminal.
use ratatui::{Frame, widgets::RatatuiLogo};
fn draw(frame: &mut Frame) { frame.render_widget(RatatuiLogo::tiny(), frame.area()); // 2x15 characters frame.render_widget(RatatuiLogo::small(), frame.area()); // 2x27 characters}
Results in:
ββββββββββββ ββββββββ ββ βββββ
ββββ βββββββββββββββββ β βββββ ββββ ββ ββββ ββ ββββ β
You can also run the example using:
cargo run --example ratatui-logo
Line: Implement From<Cow<str>>
π
Section titled βLine: Implement From<Cow<str>> πβLine
now implements From<Cow<str>>
to allow for more flexible conversions.
let cow_str: Cow<'static, str> = Cow::Borrowed("hello, world");let line = Line::from(cow_str);
As this adds an extra conversion, ambiguous inferred values may no longer compile. In that case, use
Line::from(String::from(...))
instead.
Rect::area
now returns u32
π
Section titled βRect::area now returns u32 πβThe Rect::area()
function now returns a u32
instead of a u16
to allow for larger areas to be
calculated.
Previously, Rect::new()
would clamp the rectangleβs total area to u16::MAX
, maintaining its
aspect ratio. Now, it clamps the width and height separately to stay within u16::MAX
.
Deprecate block::Title
β οΈ
Section titled βDeprecate block::Title β οΈβratatui::widgets::block::Title
is deprecated in favor of using Line
to represent titles.
This removes an unnecessary layer of wrapping (string -> Span -> Line -> Title).
To update your code:
Block::new().title(Title::from("foo"));// becomes any ofBlock::new().title("foo");Block::new().title(Line::from("foo"));
Block::new().title(Title::from("foo").position(Position::TOP));// becomes any ofBlock::new().title_top("foo");Block::new().title_top(Line::from("foo"));
Block::new().title(Title::from("foo").position(Position::BOTTOM));// becomes any ofBlock::new().title_bottom("foo");Block::new().title_bottom(Line::from("foo"));
The Title
struct will be removed in a future release of Ratatui (likely 0.31).
For more information see this issue.
Better Debug
output π
Section titled βBetter Debug output πβThe Debug output for Text
, Line
, Span
, and Style
has been improved to be more concise and
easier to read.
For example, given this code:
Text::styled("Hello, world!", Color::Yellow).centered(),
The Debug output ({:?}
) will now look like this:
Text::from(Line::from(βHello, world!β)).yellow().centered()
DoubleEndedIterator
for Columns
and Rows
π
Section titled βDoubleEndedIterator for Columns and Rows πβYou can now iterate over the columns and rows in a layout in reverse order!
let rect = Rect::new(0, 0, 3, 2);let mut columns = Columns::new(rect);
assert_eq!(columns.next_back(), Some(Rect::new(2, 0, 1, 2)));assert_eq!(columns.next_back(), Some(Rect::new(1, 0, 1, 2)));assert_eq!(columns.next_back(), Some(Rect::new(0, 0, 1, 2)));assert_eq!(columns.next_back(), None);assert_eq!(columns.next(), None);
Pin unicode-width
π
Section titled βPin unicode-width πβWe use the unicode-width
crate to calculate the width of
characters. There was
a controversial change in 0.1.14
which
resulted in 0.1.13
being published as 0.2.0
. This also broke our tests:
assert_eq!("π©".width(), 2); // Womanassert_eq!("π¬".width(), 2); // Microscopeassert_eq!("π©βπ¬".width(), 4); // Woman scientist -> should be 4 but it expect 2
We decided to comply with these changes by pinning at 0.2.0
to avoid breaking applications when
there are breaking changes in the library.
See the discussion in #1271
Check in Cargo.lock βοΈ
Section titled βCheck in Cargo.lock βοΈβWe added Cargo.lock
to the repository due to the benefits it provides:
When kept up to date, this makes it possible to build any git version with the same versions of crates that were used for any version, without it, you can only use the current versions. This makes bugs in semver compatible code difficult to detect.
See:
- https://doc.rust-lang.org/cargo/faq.html#why-have-cargolock-in-version-control
- https://blog.rust-lang.org/2023/08/29/committing-lockfiles.html
Other πΌ
Section titled βOther πΌβ- Remove unused dependencies detected with cargo-machete (#1362)
- Remove the usage of prelude in doc examples (#1390)
- Add benchmark for
Table
(#1408) - Implement size hints for
Rect
iterators (#1420) - Update README.md (#1431 & #1419)
- Fix viewport resizing and clearing (#1353 & #1427)
βFood will come, Remy. Food always comes to those who love to cook.β β Gusteau