This change simplifys UI code that uses the Frame type. E.g.:
```rust
fn draw<B: Backend>(frame: &mut Frame<B>) {
// ...
}
```
Frame was generic over Backend because it stored a reference to the
terminal in the field. Instead it now directly stores the viewport area
and current buffer. These are provided at creation time and are valid
for the duration of the frame.
BREAKING CHANGE: Frame is no longer generic over Backend. Code that
accepted `Frame<Backend>` will now need to accept `Frame`. To migrate
existing code, remove any generic parameters from code that uses an
instance of a Frame. E.g. the above code becomes:
```rust
fn draw(frame: &mut Frame) {
// ...
}
```
- add `Rect::is_empty()` that checks whether either height or width == 0
- refactored `Rect` into layout/rect.rs from layout.rs. No public API change as
the module is private and the type is re-exported under the `layout` module.
Adds a new `Block::border_set` method that allows the user to specify
the symbols used for the border.
Added two new border types: `BorderType::QuadrantOutside` and
`BorderType::QuadrantInside`. These are used to draw borders using the
unicode quadrant characters (which look like half block "pixels").
QuadrantOutside:
```
▛▀▀▜
▌ ▐
▙▄▄▟
```
QuadrantInside:
```
▗▄▄▖
▐ ▌
▝▀▀▘
```
Fixes: https://github.com/ratatui-org/ratatui/issues/528
BREAKING CHANGES:
- BorderType::to_line_set is renamed to to_border_set
- BorderType::line_symbols is renamed to border_symbols
* feat(line): add `Line::raw` constructor
There is already `Span::raw` and `Text::raw` methods
and this commit simply adds `Line::raw` method for symmetry.
Multi-line content is converted to multiple spans with the new line removed
An earlier change introduced a bug where the width of value labels with
unicode characters was incorrectly using the string length in bytes
instead of the unicode character count. This reverts the earlier change.
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
The `Spans` type (plural, not singular) was replaced with a more ergonomic `Line` type
in Ratatui v0.21.0 and marked deprecated byt left for backwards compatibility. This is now
removed.
- `Line` replaces `Spans`
- `Buffer::set_line` replaces `Buffer::set_spans`
Windows cannot compile termion, so it is not included in the docs.
Rustdoc will fail if it cannot find a link, so the docs fail to build
on windows.
This replaces the link to TermionBackend with one that does not fail
during checks.
Fixes https://github.com/ratatui-org/ratatui/issues/498
The layout cache now uses a LruCache with default size set to 16 entries.
Previously the cache was backed by a HashMap, and was able to grow
without bounds as a new entry was added for every new combination of
layout parameters.
- Added a new method (`layout::init_cache(usize)`) that allows the cache
size to be changed if necessary. This will only have an effect if it is called
prior to any calls to `layout::split()` as the cache is wrapped in a `OnceLock`
This allows writing unit tests without having to call set_style on the
expected buffer.
E.g.:
```rust
use crate::style::Stylize;
let mut buf = Buffer::empty(Rect::new(0, 0, 10, 10));
buf.set_string(0, 0, "foo", Style::new().red());
buf.set_string(0, 1, "bar", Style::new().blue());
assert_eq!(buf, Buffer::with_lines(vec!["foo".red(), "bar".blue()]));
```
Inspired by https://github.com/ratatui-org/ratatui/issues/493#issuecomment-1714844468
This commit adds `prelude::*` all doc examples and widget::* to those
that need it. This is done to highlight the use of the prelude and
simplify the examples.
- Examples in Type and module level comments show all imports and use
`prelude::*` and `widget::*` where possible.
- Function level comments hide imports unless there are imports other
than `prelude::*` and `widget::*`.
The Stylize trait was introduced in 0.22 to make styling less verbose.
This adds a bunch of documentation comments to the style module and
types to make this easier to discover.
Although the `Stylize` trait is already implemented for `&str` which
extends to `String`, it is not implemented for `String` itself. This
commit adds an impl of Stylize that returns a Span<'static> for `String`
so that code can call Stylize methods on temporary `String`s.
E.g. the following now compiles instead of failing with a compile error
about referencing a temporary value:
let s = format!("hello {name}!", "world").red();
BREAKING CHANGE: This may break some code that expects to call Stylize
methods on `String` values and then use the String value later. This
will now fail to compile because the String is consumed by set_style
instead of a slice being created and consumed.
This can be fixed by cloning the `String`. E.g.:
let s = String::from("hello world");
let line = Line::from(vec![s.red(), s.green()]); // fails to compile
let line = Line::from(vec![s.clone().red(), s.green()]); // works
A bug in the rendering caused the top line of the chart to be rendered
using the style of the chart, instead of the dataset style. This is
fixed by only setting the style for the width of the text, and not the
entire row.
Fixes: https://github.com/ratatui-org/ratatui/issues/379
For image (sixel, iTerm2, Kitty...) support that handles graphics in
terms of `Rect` so that the image area can be included in layouts.
For example: an image is loaded with a known pixel-size, and drawn, but
the image protocol has no mechanism of knowing the actual cell/character
area that been drawn on. It is then impossible to skip overdrawing the
area.
Returning the window size in pixel-width / pixel-height, together with
colums / rows, it can be possible to account the pixel size of each cell
/ character, and then known the `Rect` of a given image, and also resize
the image so that it fits exactly in a `Rect`.
Crossterm and termwiz also both return both sizes from one syscall,
while termion does two.
Add a `Size` struct for the cases where a `Rect`'s `x`/`y` is unused
(always zero).
`Size` is not "clipped" for `area < u16::max_value()` like `Rect`. This
is why there are `From` implementations between the two.
Calculating the label_offset is unnecessary, if we just render the
group label after rendering the bars. We can just reuse bar_y.
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
* docs(lib): extract feature documentation from Cargo.toml
* chore(deps): make `document-features` optional dependency
* docs(lib): document the serde feature from features section
> Sixel is a bitmap graphics format supported by terminals.
> "Sixel mode" is entered by sending the sequence ESC+Pq.
> The "String Terminator" sequence ESC+\ exits the mode.
The graphics are then rendered with the top left positioned at the
cursor position.
It is actually possible to render sixels in ratatui with just
`buf.get_mut(x, y).set_symbol("^[Pq ... ^[\")`. But any buffer covering
the "image area" will overwrite the graphics. This is most likely the same
buffer, even though it consists of empty characters `' '`, except for
the top-left character that starts the sequence.
Thus, either the buffer or cells must be specialized to avoid drawing
over the graphics. This patch specializes the `Cell` with a
`set_skip(bool)` method, based on James' patch:
https://github.com/TurtleTheSeaHobo/tui-rs/tree/sixel-support
I unsuccessfully tried specializing the `Buffer`, but as far as I can tell
buffers get merged all the way "up" and thus skipping must be set on the
Cells. Otherwise some kind of "skipping area" state would be required,
which I think is too complicated.
Having access to the buffer now it is possible to skipp all cells but the
first one which can then `set_symbol(sixel)`. It is up to the user to
deal with the graphics size and buffer area size. It is possible to get
the terminal's font size in pixels with a syscall.
An image widget for ratatui that uses this `skip` flag is available at
https://github.com/benjajaja/ratatu-image.
Co-authored-by: James <james@rectangle.pizza>
* feat(barchart): Add direction attribute
Enable rendring the bars horizontally. In some cases this allow us to
make more efficient use of the available space.
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
* feat(barchart)!: render the group labels depending on the alignment
This is a breaking change, since the alignment by default is set to
Left and the group labels are always rendered in the center.
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
---------
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
The merge of the table unit tests after the rounding layout fix was not
rebased correctly, this addresses the broken tests, makes them more
concise while adding comments to help clarify that the rounding behavior
is working as expected.
Previously the layout used the floor of the calculated start and width
as the value to use for the split Rects. This resulted in gaps between
the split rects.
This change modifies the layout to round to the nearest column instead
of taking the floor of the start and width. This results in the start
and end of each rect being rounded the same way and being strictly
adjacent without gaps.
Because there is a required constraint that ensures that the last end is
equal to the area end, there is no longer the need to fixup the last
item width when the fill (as e.g. width = x.99 now rounds to x+1 not x).
The colors example has been updated to use Ratio(1, 8) instead of
Percentage(13), as this now renders without gaps for all possible sizes,
whereas previously it would have left odd gaps between columns.
* test(layout): add tests for split()
* refactor(layout): simplify and doc split()
This is mainly a reduction in density of the code with a goal of
improving mainatainability so that the algorithm is clear.
Removes some unnecessary code and makes the function more readable.
Instead of creating a temporary result and mutating it, we just create
the result directly from the list of changes.
* feat(list): add option to always allocate the "selection" column width
Before this option was available, selecting a item in a list when nothing was selected
previously made the row layout change (the same applies to unselecting) by adding the width
of the "highlight symbol" in the front of the list, this option allows to configure this
behavior.
* style: change "highlight_spacing" doc comment to use inline code-block for reference
* feat(table): add option to configure selection layout changes
Before this option was available, selecting a row in the table when no row was selected
previously made the tables layout change (the same applies to unselecting) by adding the width
of the "highlight symbol" in the front of the first column, this option allows to configure this
behavior.
* refactor(table): refactor "get_columns_widths" to return (x, width)
and "render" to make use of that
* refactor(table): refactor "get_columns_widths" to take in a selection_width instead of a boolean
also refactor "render" to make use of this change
* fix(table): rename "highlight_set_selection_space" to "highlight_spacing"
* style(table): apply doc-comment suggestions from code review
Co-authored-by: Dheepak Krishnamurthy <me@kdheepak.com>
---------
Co-authored-by: Dheepak Krishnamurthy <me@kdheepak.com>
Reorder the derive fields to be more consistent:
Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash
Hash trait won't be impl in this PR due to rust std design.
If we need hash trait for f64 related structs in the future,
we should consider wrap f64 into a new type.
see: https://github.com/ratatui-org/ratatui/issues/307
The track symbol is now optional, simplifying composition with other
widgets.
BREAKING_CHANGE: The `track_symbol` needs to be set in the following way
now:
```
let scrollbar = Scrollbar::default().track_symbol(Some("-"));
```
Implement `Clone & Copy` common traits for most structs in src.
Only implement `Copy` for structs that are simple and trivial to copy.
Reorder the derive fields to be more consistent:
Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash
see: https://github.com/ratatui-org/ratatui/issues/307
Implement `Debug & Default` common traits for most structs in src.
Reorder the derive fields to be more consistent:
Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash
see: https://github.com/ratatui-org/ratatui/issues/307
The symbols and sets are moved from `widgets::scrollbar` to
`symbols::scrollbar`. This makes it consistent with the other symbol
sets and allows us to make the scrollbar module private rather than
re-exporting it.
BREAKING CHANGE: The symbols are now in the `symbols` module. To update
your code, add an import for `ratatui:🔣:scrollbar::*` (or the
specific symbols you need). The scrollbar module is no longer public. To
update your code, remove any `widgets::scrollbar` imports and replace it
with `ratatui::widgets::Scrollbar`.
This unlikely to happen, since nobody wants to add an empty group.
Even we fix the panic, things will not render correctly.
So it is better to just not add them to the BarChart.
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
* ci: parallelize CI jobs
- remove the dependency on the lint job from all other jobs
- implement workflow concurrency
- reorder the workflow so that the lint, clippy and coverage jobs are
scheduled before the test jobs
- run jobs which run for each backend in parallel by calling e.g.
cargo make test-termion, instead of cargo make test
- add a coverage task to the makefile
- change "cargo-make check" to check all features valid for OS in
parallel
- run clippy only on the ubuntu-latest runner and check all features
valid in parallel
- tidy up the workflow file
* ci: simplify Makefile OS detection
Use platform overrides to significantly simplify the Makefile logic
See https://github.com/sagiegurari/cargo-make\#platform-override
* fix(termwiz): skip doc test that requires stdout
* fix(title): remove default alignment and position
* test(block): add test cases for alignment
* test(block): extend the unit tests for block title alignment
This helps to keep the prelude small and less likely to conflict with
other crates.
- remove widgets module from prelude as the entire module can be just as
easily imported with `use ratatui::widgets::*;`
- move prelude module into its own file
- update examples to import widgets module instead of just prelude
- added several modules to prelude to make it possible to qualify
imports that collide with other types that have similar names
This commit adds the underline_color() function to the Style and Cell
structs. This enables setting the underline color of text on the
crossterm backend. This is a no-op for the termion and termwiz backends
as they do not support this feature.
* feat(stylize): allow all widgets to be styled
- Add styled impl to:
- Barchart
- Chart (including Axis and Dataset),
- Guage and LineGuage
- List and ListItem
- Sparkline
- Table, Row, and Cell
- Tabs
- Style
- Allow modifiers to be removed (e.g. .not_italic())
- Allow .bg() to recieve Into<Color>
- Made shorthand methods consistent with modifier names (e.g. dim() not
dimmed() and underlined() not underline())
- Simplify integration tests
- Add doc comments
- Simplified stylize macros with https://crates.io/crates/paste
* build: run clippy before tests
Runny clippy first means that we fail fast when there is an issue that
can easily be fixed rather than having to wait 30-40s for the failure
for now the value is converted to a string and then printed. in many
cases the values are too wide or double values. so it make sense
to set a custom value text instead of the default behavior.
this patch suggests to add a method
"fn text_value(mut self, text_value: String)"
to the Bar, which allows to override the value printed in the bar
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
* feat(barchart): allow to add a group of bars
Example: to show the revenue of different companies:
┌────────────────────────┐
│ ████ │
│ ████ │
│ ████ ████ │
│ ▄▄▄▄ ████ ████ ████ │
│ ████ ████ ████ ████ │
│ ████ ████ ████ ████ │
│ █50█ █60█ █90█ █55█ │
│ Mars April │
└────────────────────────┘
new structs are introduced: Group and Bar.
the data function is modified to accept "impl Into<Group<'a>>".
a new function "group_gap" is introduced to set the gap between each group
unit test changed to allow the label to be in the center
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
* feat(barchart)!: center labels by default
The bar labels are currently printed string from the left side of
bar. This commit centers the labels under the bar.
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
---------
Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
Allows Modifiers to be added or removed from `Style` in a const context.
This can be used in the following way:
```
const DEFAULT_MODIFIER: Modifier = Modifier::BOLD.union(Modifier::ITALIC);
const DEFAULT_STYLE: Style = Style::new()
.fg(Color::Red).bg(Color::Black).add_modifier(DEFAULT_MODIFIER);
```
Before this change, it wasn't possible to build all features and all
targets at the same time, which prevents rust-analyzer from working
for the whole project.
Adds a bacon.toml file to the project, which is used by bacon
https://dystroy.org/bacon/
Configures docs.rs to show the feature flags that are necessary to
make modules / types / functions available.
This allows the following types to be used in a constant context:
- `Layout`
- `Rect`
- `Style`
- `BorderType`
- `Padding`
- `Block`
Also adds several missing `new()` functions to the above types.
Blocks can now be used in the following way:
```
const DEFAULT_BLOCK: Block = Block::new()
.title_style(Style::new())
.title_alignment(Alignment::Left)
.title_position(Position::Top)
.borders(Borders::ALL)
.border_style(Style::new())
.style(Style::reset())
.padding(Padding::uniform(1));
```
Layouts can now be used in the following way:
``
const DEFAULT_LAYOUT: Layout = Layout::new()
.direction(Direction::Horizontal)
.margin(1)
.expand_to_fill(false);
```
Rects can now be used in the following way:
```
const RECT: Rect = Rect {
x: 0,
y: 0,
width: 10,
height: 10,
};
```
* docs(src\widgets\scrollbar.rs): wrap scrollbar's visualisation in text block
'cargo doc' and 'rust-analyzer' removes many whitespaces thus making those parts render improperly
* docs(src/widgets/calendar.rs): fix `no item named ...` for calendar.rs
* style(src/widgets/block.rs): format `block.rs`
Represents a scrollbar widget that renders a track, thumb and arrows
either horizontally or vertically. State is kept in ScrollbarState, and
passed as a parameter to the render function.
* fix(gauge): render gauge with unicode correctly
Gauge now correctly renders a block rather than a space when in unicode mode.
* docs: update demo.gif
- remove existing gif
- upload using VHS (https://github.com/charmbracelet/vhs)
- add instructions to RELEASE.md
- link new gif in README
- Adds a new typos.toml
- Prevents ratatui from being marked as a typo of ratatouille
- Changes paragraph tests so that the truncated word is a valid word
This is an opinionated default that helps avoid horizontal scrolling.
100 is the most common width on github rust projects and works well for
displaying code on a 16in macbook pro.
BREAKING CHANGE: The serde representation of bitflags has changed. Any
existing serialized types that have Borders or Modifiers will need to be
re-serialized. This is documented in the bitflags changelog.
https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md#200-rc2
`Paragraph` now supports rendering each line in the paragraph with a
different alignment (Center, Left and Right) rather than the entire
paragraph being aligned the same. Each line either overrides the
paragraph's alignment or inherits it if the line's alignment is
unspecified.
- Adds `alignment` field to `Line` and builder methods on `Line` and
`Span`
- Updates reflow module types to be line oriented rather than symbol
oriented to take account of each lines alignment
- Adds unit tests to `Paragraph` to fully capture the existing and new
behavior
---------
Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
- `Default::default` is hard to read
- a few `map` -> `map_or`
- simplified `match` -> `let-if`
Signed-off-by: Yuri Astrakhan <YuriAstrakhan@gmail.com>
Adds a `border!` macro that takes TOP, BOTTOM, LEFT, RIGHT, and ALL and
returns a Borders object. An empty `border!()` call returns
Borders::NONE
This is gated behind a `macros` feature flag to ensure short build
times. To enable, add the following to your `Cargo.toml`:
```toml
ratatui = { version = 0.21.0, features = ["macros"] }
```
Co-authored-by: C-Jameson <booksncode@gmail.com>
* refactor: add Line type to replace Spans
`Line` is a significantly better name over `Spans` as the plural causes
confusion and the type really is a representation of a line of text made
up of spans.
This is a backwards compatible version of the approach from
https://github.com/tui-rs-revival/ratatui/pull/175
There is a significant amount of code that uses the Spans type and
methods, so instead of just renaming it, we add a new type and replace
parameters that accepts a `Spans` with a parameter that accepts
`Into<Line>`.
Note that the examples have been intentionally left using `Spans` in
this commit to demonstrate the compiler warnings that will be emitted in
existing code.
Implementation notes:
- moves the Spans code to text::spans and publicly reexports on the text
module. This makes the test in that module only relevant to the Spans
type.
- adds a line module with a copy of the code and tests from Spans with a
single addition: `impl<'a> From<Spans<'a>> for Line<'a>`
- adds tests for `Spans` (created and checked before refactoring)
- adds the same tests for `Line`
- updates all widget methods that accept and store Spans to instead
store `Line` and accept `Into<Line>`
* refactor: move text::Masked to text::masked::Masked
Re-exports the Masked type at text::Masked
* refactor: replace Spans with Line in tests/examples/docs
* build: bump MSRV to 1.65
The latest version of the time crate requires Rust 1.65.0
```
cargo +1.64.0-x86_64-apple-darwin test --no-default-features \
--features serde,crossterm,all-widgets --lib --tests --examples
error: package `time v0.3.21` cannot be built because it requires rustc
1.65.0 or newer, while the currently active rustc version is 1.64.0
```
* feat(backend): add termwiz backend and demo
* ci(termwiz): add termwiz to makefile.toml
---------
Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
Co-authored-by: Prabir Shrestha <mail@prabir.me>
The latest version of the time crate requires Rust 1.65.0
```
cargo +1.64.0-x86_64-apple-darwin test --no-default-features \
--features serde,crossterm,all-widgets --lib --tests --examples
error: package `time v0.3.21` cannot be built because it requires rustc
1.65.0 or newer, while the currently active rustc version is 1.64.0
```
Also fixes several clippy warnings added in 1.63/1.65. Although these
have been since moved to nursery / pedantic, it doesn't hurt to fix
these issues as part of this change:
- https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq (nursery)
- https://rust-lang.github.io/rust-clippy/master/index.html#bool_to_int_with_if (pedantic)
Adds a new type Masked that can mask data with a mask character, and can
be used anywhere we expect Cow<'a, str> or Text<'a>. E.g. Paragraph,
ListItem, Table Cells etc.
BREAKING CHANGE:
Because Masked implements From for Text<'a>, code that binds
Into<Text<'a>> without type annotations may no longer compile
(e.g. `Paragraph::new("".as_ref())`)
To fix this, annotate or call to_string() / to_owned() / as_str()
- also adds builder methods on list state to make it easy to construct
a list state with selected and offset as a one-liner. Uses `with_` as
the prefix for these methods as the selected method currently acts as
a getter rather than a builder.
- cargo tarpaulin suggests only two lines are not covered (the two
match patterns of the self.start_corner match 223 and 227).
the body of these lines is covered, so this is probably 100% coverage.
- The implementation of Debug is customized to make it easy to use the
output (particularly the content) directly when writing tests (by
surrounding it with `Buffer::with_lines(vec![])`). The styles part of
the message shows the position of every style change, rather than the
style of each cell, which reduces the verbosity of the detail, while
still showing everything necessary to debug the buffer.
```rust
Buffer {
area: Rect { x: 0, y: 0, width: 12, height: 2 },
content: [
"Hello World!",
"G'day World!",
],
styles: [
x: 0, y: 0, fg: Reset, bg: Reset, modifier: (empty),
x: 0, y: 1, fg: Green, bg: Yellow, modifier: BOLD,
]
}
```
- The assert_buffer_eq! macro shows debug view and diff of the two
buffers, which makes it easy to understand exactly where the difference
is.
- Also adds a unit test for buffer_set_string_multi_width_overwrite
which was missing from the buffer tests