Compare commits

..

1 Commits

Author SHA1 Message Date
Josh McKinney
9ea840d87f feat(layout): accept (x, y) tuple for Rect::offset
This allows callers to call `Rect::offset((x, y))`` instead of the more
verbose `Rect::offset(Offset { x, y })`.
2024-03-28 16:22:43 -07:00
77 changed files with 728 additions and 1329 deletions

5
.github/CODEOWNERS vendored
View File

@@ -5,7 +5,4 @@
# https://git-scm.com/docs/gitignore#_pattern_format
# Maintainers
* @orhun @joshka @kdheepak @Valentin271 @EdJoPaTo
# Past maintainers
# @mindoodoo @sayanarijit
* @orhun @mindoodoo @sayanarijit @joshka @kdheepak @Valentin271 @EdJoPaTo

View File

@@ -2,7 +2,7 @@
This document contains a list of breaking changes in each version and some notes to help migrate
between versions. It is compiled manually from the commit history and changelog. We also tag PRs on
GitHub with a [breaking change] label.
github with a [breaking change] label.
[breaking change]: (https://github.com/ratatui-org/ratatui/issues?q=label%3A%22breaking+change%22)
@@ -37,7 +37,7 @@ This is a quick summary of the sections below:
- `Scrollbar`: symbols moved to `symbols` module
- MSRV is now 1.67.0
- [v0.22.0](#v0220)
- `serde` representation of `Borders` and `Modifiers` has changed
- serde representation of `Borders` and `Modifiers` has changed
- [v0.21.0](#v0210)
- MSRV is now 1.65.0
- `terminal::ViewPort` is now an enum
@@ -49,14 +49,14 @@ This is a quick summary of the sections below:
## [v0.26.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.26.0)
### `Flex::Start` is the new default flex mode for `Layout` ([#881])
### `Flex::Start` is the new default flex mode for `Layout`
[#881]: https://github.com/ratatui-org/ratatui/pull/881
Previously, constraints would stretch to fill all available space, violating constraints if
necessary.
With v0.26.0, `Flex` modes are introduced, and the default is `Flex::Start`, which will align
With v0.26.0, `Flex` modes are introduced and the default is `Flex::Start`, which will align
areas associated with constraints to be beginning of the area. With v0.26.0, additionally,
`Min` constraints grow to fill excess space. These changes will allow users to build layouts
more easily.
@@ -108,7 +108,7 @@ by removing the call to `.collect()`.
[#751]: https://github.com/ratatui-org/ratatui/pull/751
The default() implementation of Table now sets the column_spacing field to 1 and the segment_size
field to `SegmentSize::None`. This will affect the rendering of a small amount of apps.
field to SegmentSize::None. This will affect the rendering of a small amount of apps.
To use the previous default values, call `table.segment_size(Default::default())` and
`table.column_spacing(0)`.
@@ -222,8 +222,8 @@ widget in the default configuration would not show any indication of the selecte
[#664]: https://github.com/ratatui-org/ratatui/pull/664
Previously `Table`s could be constructed without `widths`. In almost all cases this is an error.
A new `widths` parameter is now mandatory on `Table::new()`. Existing code of the form:
Previously `Table`s could be constructed without widths. In almost all cases this is an error.
A new widths parameter is now mandatory on `Table::new()`. Existing code of the form:
```diff
- Table::new(rows).widths(widths)
@@ -281,7 +281,7 @@ let layout = layout::new(Direction::Vertical, [Constraint::Min(1), Constraint::M
## [v0.24.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.24.0)
### `ScrollbarState` field type changed from `u16` to `usize` ([#456])
### ScrollbarState field type changed from `u16` to `usize` ([#456])
[#456]: https://github.com/ratatui-org/ratatui/pull/456
@@ -385,12 +385,12 @@ The MSRV of ratatui is now 1.67 due to an MSRV update in a dependency (`time`).
## [v0.22.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.22.0)
### `bitflags` updated to 2.3 ([#205])
### bitflags updated to 2.3 ([#205])
[#205]: https://github.com/ratatui-org/ratatui/issues/205
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`
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)..
## [v0.21.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.21.0)
@@ -422,9 +422,9 @@ let terminal = Terminal::with_options(backend, TerminalOptions {
[#168]: https://github.com/ratatui-org/ratatui/issues/168
A new type `Masked` was introduced that implements `From<Text<'a>>`. This causes any code that
A new type `Masked` was introduced that implements `From<Text<'a>>`. This causes any code that did
previously did not need to use type annotations to fail to compile. To fix this, annotate or call
`to_string()` / `to_owned()` / `as_str()` on the value. E.g.:
to_string() / to_owned() / as_str() on the value. E.g.:
```diff
- let paragraph = Paragraph::new("".as_ref());

View File

@@ -2,457 +2,7 @@
All notable changes to this project will be documented in this file.
## [0.26.2](https://github.com/ratatui-org/ratatui/releases/tag/v0.26.2) - 2024-04-15
This is a patch release that fixes bugs and adds enhancements, including new iterator constructors, List scroll padding, and various rendering improvements. ✨
**Release highlights**: <https://ratatui.rs/highlights/v0262/>
### Features
- [11b452d](https://github.com/ratatui-org/ratatui/commit/11b452d56fe590188ee7a53fa2dde95513b1a4c7)
*(layout)* Mark various functions as const by @EdJoPaTo in [#951](https://github.com/ratatui-org/ratatui/pull/951)
- [1cff511](https://github.com/ratatui-org/ratatui/commit/1cff51193466f5a94d202b6233d56889eccf6d7b)
*(line)* Impl Styled for Line by @joshka in [#968](https://github.com/ratatui-org/ratatui/pull/968)
````text
This adds `FromIterator` impls for `Line` and `Text` that allow creating
`Line` and `Text` instances from iterators of `Span` and `Line`
instances, respectively.
```rust
let line = Line::from_iter(vec!["Hello".blue(), " world!".green()]);
let line: Line = iter::once("Hello".blue())
.chain(iter::once(" world!".green()))
.collect();
let text = Text::from_iter(vec!["The first line", "The second line"]);
let text: Text = iter::once("The first line")
.chain(iter::once("The second line"))
.collect();
```
````
- [654949b](https://github.com/ratatui-org/ratatui/commit/654949bb00b4522130642f9ad50ab4d9095d921b)
*(list)* Add Scroll Padding to Lists by @CameronBarnes in [#958](https://github.com/ratatui-org/ratatui/pull/958)
````text
Introduces scroll padding, which allows the api user to request that a certain number of ListItems be kept visible above and below the currently selected item while scrolling.
```rust
let list = List::new(items).scroll_padding(1);
```
````
Fixes:https://github.com/ratatui-org/ratatui/pull/955
- [26af650](https://github.com/ratatui-org/ratatui/commit/26af65043ee9f165459dec228d12eaeed9997d92)
*(text)* Add push methods for text and line by @joshka in [#998](https://github.com/ratatui-org/ratatui/pull/998)
````text
Adds the following methods to the `Text` and `Line` structs:
- Text::push_line
- Text::push_span
- Line::push_span
This allows for adding lines and spans to a text object without having
to call methods on the fields directly, which is useful for incremental
construction of text objects.
````
- [b5bdde0](https://github.com/ratatui-org/ratatui/commit/b5bdde079e0e1eda98b9b1bbbba011b770e5b167)
*(text)* Add `FromIterator` impls for `Line` and `Text` by @joshka in [#967](https://github.com/ratatui-org/ratatui/pull/967)
````text
This adds `FromIterator` impls for `Line` and `Text` that allow creating
`Line` and `Text` instances from iterators of `Span` and `Line`
instances, respectively.
```rust
let line = Line::from_iter(vec!["Hello".blue(), " world!".green()]);
let line: Line = iter::once("Hello".blue())
.chain(iter::once(" world!".green()))
.collect();
let text = Text::from_iter(vec!["The first line", "The second line"]);
let text: Text = iter::once("The first line")
.chain(iter::once("The second line"))
.collect();
```
````
- [12f67e8](https://github.com/ratatui-org/ratatui/commit/12f67e810fad0f907546408192a2380b590ff7bd)
*(uncategorized)* Impl Widget for `&str` and `String` by @kdheepak in [#952](https://github.com/ratatui-org/ratatui/pull/952)
````text
Currently, `f.render_widget("hello world".bold(), area)` works but
`f.render_widget("hello world", area)` doesn't. This PR changes that my
implementing `Widget` for `&str` and `String`. This makes it easier to
render strings with no styles as widgets.
Example usage:
```rust
terminal.draw(|f| f.render_widget("Hello World!", f.size()))?;
```
---------
````
### Bug Fixes
- [0207160](https://github.com/ratatui-org/ratatui/commit/02071607848c51250b4663722c52e19c8ce1c5e2)
*(line)* Line truncation respects alignment by @TadoTheMiner in [#987](https://github.com/ratatui-org/ratatui/pull/987)
````text
When rendering a `Line`, the line will be truncated:
- on the right for left aligned lines
- on the left for right aligned lines
- on bot sides for centered lines
E.g. "Hello World" will be rendered as "Hello", "World", "lo wo" for
left, right, centered lines respectively.
````
Fixes:https://github.com/ratatui-org/ratatui/issues/932
- [c56f49b](https://github.com/ratatui-org/ratatui/commit/c56f49b9fb1c7f1c8c97749119e85f81882ca9a9)
*(list)* Saturating_sub to fix highlight_symbol overflow by @mrjackwills in [#949](https://github.com/ratatui-org/ratatui/pull/949)
````text
An overflow (pedantically an underflow) can occur if the
highlight_symbol is a multi-byte char, and area is reduced to a size
less than that char length.
````
- [b7778e5](https://github.com/ratatui-org/ratatui/commit/b7778e5cd15d0d4b28f7bbb8b3c62950748e333a)
*(paragraph)* Unit test typo by @joshka in [#1022](https://github.com/ratatui-org/ratatui/pull/1022)
- [943c043](https://github.com/ratatui-org/ratatui/commit/943c0431d968a82b23a2f31527f32e57f86f8a7c)
*(scrollbar)* Dont render on 0 length track by @EdJoPaTo in [#964](https://github.com/ratatui-org/ratatui/pull/964)
````text
Fixes a panic when `track_length - 1` is used. (clamp panics on `-1.0`
being smaller than `0.0`)
````
- [742a5ea](https://github.com/ratatui-org/ratatui/commit/742a5ead066bec14047f6ab7ffa3ac8307eea715)
*(text)* Fix panic when rendering out of bounds by @joshka in [#997](https://github.com/ratatui-org/ratatui/pull/997)
````text
Previously it was possible to cause a panic when rendering to an area
outside of the buffer bounds. Instead this now correctly renders nothing
to the buffer.
````
- [f6c4e44](https://github.com/ratatui-org/ratatui/commit/f6c4e447e65fe10f4fc7fcc9e9c4312acad41096)
*(uncategorized)* Ensure that paragraph correctly renders styled text by @joshka in [#992](https://github.com/ratatui-org/ratatui/pull/992)
````text
Paragraph was ignoring the new `Text::style` field added in 0.26.0
````
Fixes:https://github.com/ratatui-org/ratatui/issues/990
- [35e971f](https://github.com/ratatui-org/ratatui/commit/35e971f7ebb0deadc613b561b15511abd48bdb54)
*(uncategorized)* Scrollbar thumb not visible on long lists by @ThomasMiz in [#959](https://github.com/ratatui-org/ratatui/pull/959)
````text
When displaying somewhat-long lists, the `Scrollbar` widget sometimes did not display a thumb character, and only the track will be visible.
````
### Refactor
- [6fd5f63](https://github.com/ratatui-org/ratatui/commit/6fd5f631bbd58156d9fcae196040bb0248097819)
*(lint)* Prefer idiomatic for loops by @EdJoPaTo
- [37b957c](https://github.com/ratatui-org/ratatui/commit/37b957c7e167a7ecda07b8a60cee5de71efcc55e)
*(lints)* Add lints to scrollbar by @EdJoPaTo
- [c12bcfe](https://github.com/ratatui-org/ratatui/commit/c12bcfefa26529610886040bd96f2b6762436b15)
*(non-src)* Apply pedantic lints by @EdJoPaTo in [#976](https://github.com/ratatui-org/ratatui/pull/976)
````text
Fixes many not yet enabled lints (mostly pedantic) on everything that is
not the lib (examples, benches, tests). Therefore, this is not containing
anything that can be a breaking change.
Lints are not enabled as that should be the job of #974. I created this
as a separate PR as its mostly independent and would only clutter up the
diff of #974 even more.
Also see
https://github.com/ratatui-org/ratatui/pull/974#discussion_r1506458743
---------
````
- [8719608](https://github.com/ratatui-org/ratatui/commit/8719608bdaf32ba92bdfdd60569cf73f7070a618)
*(span)* Rename to_aligned_line into into_aligned_line by @EdJoPaTo in [#993](https://github.com/ratatui-org/ratatui/pull/993)
````text
With the Rust method naming conventions these methods are into methods
consuming the Span. Therefore, it's more consistent to use `into_`
instead of `to_`.
```rust
Span::to_centered_line
Span::to_left_aligned_line
Span::to_right_aligned_line
```
Are marked deprecated and replaced with the following
```rust
Span::into_centered_line
Span::into_left_aligned_line
Span::into_right_aligned_line
```
````
- [b831c56](https://github.com/ratatui-org/ratatui/commit/b831c5688c6f1fbfa6ae2bcd70d803a54fcf0196)
*(widget-ref)* Clippy::needless_pass_by_value by @EdJoPaTo
- [359204c](https://github.com/ratatui-org/ratatui/commit/359204c9298cc26ea21807d886d596de0329bacc)
*(uncategorized)* Simplify to io::Result by @EdJoPaTo in [#1016](https://github.com/ratatui-org/ratatui/pull/1016)
````text
Simplifies the code, logic stays exactly the same.
````
- [8e68db9](https://github.com/ratatui-org/ratatui/commit/8e68db9e2f57fcbf7cb5140006bbbd4dd80bf907)
*(uncategorized)* Remove pointless default on internal structs by @EdJoPaTo in [#980](https://github.com/ratatui-org/ratatui/pull/980)
See #978
Also remove other derives. They are unused and just slow down
compilation.
- [3be189e](https://github.com/ratatui-org/ratatui/commit/3be189e3c6ebd418d13138ff32bc4a749dc840cf)
*(uncategorized)* Clippy::thread_local_initializer_can_be_made_const by @EdJoPaTo
````text
enabled by default on nightly
````
- [5c4efac](https://github.com/ratatui-org/ratatui/commit/5c4efacd1d70bb295d90ffaa73853dc206c187fb)
*(uncategorized)* Clippy::map_err_ignore by @EdJoPaTo
- [bbb6d65](https://github.com/ratatui-org/ratatui/commit/bbb6d65e063df9a74ab6487b2216183c1fdd7230)
*(uncategorized)* Clippy::else_if_without_else by @EdJoPaTo
- [fdb14dc](https://github.com/ratatui-org/ratatui/commit/fdb14dc7cd69788e2ed20709e767f7631b11ffa2)
*(uncategorized)* Clippy::redundant_type_annotations by @EdJoPaTo
- [9b3b23a](https://github.com/ratatui-org/ratatui/commit/9b3b23ac14518a1ef23065d4a5da0fb047b18213)
*(uncategorized)* Remove literal suffix by @EdJoPaTo
````text
its not needed and can just be assumed
````
related:clippy::(un)separated_literal_suffix
- [58b6e0b](https://github.com/ratatui-org/ratatui/commit/58b6e0be0f4db3d90005e130e4b84cd865179785)
*(uncategorized)* Clippy::should_panic_without_expect by @EdJoPaTo
- [c870a41](https://github.com/ratatui-org/ratatui/commit/c870a41057ac0c14c2e72e762b37689dc32e7b23)
*(uncategorized)* Clippy::many_single_char_names by @EdJoPaTo
- [a6036ad](https://github.com/ratatui-org/ratatui/commit/a6036ad78911653407f607f5efa556a055d3dce9)
*(uncategorized)* Clippy::similar_names by @EdJoPaTo
- [060d26b](https://github.com/ratatui-org/ratatui/commit/060d26b6dc6e1027dbf46ae98b0ebba83701f941)
*(uncategorized)* Clippy::match_same_arms by @EdJoPaTo
- [fcbea9e](https://github.com/ratatui-org/ratatui/commit/fcbea9ee68591344a29a7b2e83f1c8c878857aeb)
*(uncategorized)* Clippy::uninlined_format_args by @EdJoPaTo
- [14b24e7](https://github.com/ratatui-org/ratatui/commit/14b24e75858af48f39d5880e7f6c9adeac1b1da9)
*(uncategorized)* Clippy::if_not_else by @EdJoPaTo
- [5ed1f43](https://github.com/ratatui-org/ratatui/commit/5ed1f43c627053f25d9ee711677ebec6cb8fcd85)
*(uncategorized)* Clippy::redundant_closure_for_method_calls by @EdJoPaTo
- [c8c7924](https://github.com/ratatui-org/ratatui/commit/c8c7924e0ca84351f5ed5c54e79611ce16d4dc37)
*(uncategorized)* Clippy::too_many_lines by @EdJoPaTo
- [e3afe7c](https://github.com/ratatui-org/ratatui/commit/e3afe7c8a14c1cffd7de50782a7acf0f95f41673)
*(uncategorized)* Clippy::unreadable_literal by @EdJoPaTo
- [a1f54de](https://github.com/ratatui-org/ratatui/commit/a1f54de7d60fa6c57be29bf8f02a675e58b7b9c2)
*(uncategorized)* Clippy::bool_to_int_with_if by @EdJoPaTo
- [b8ea190](https://github.com/ratatui-org/ratatui/commit/b8ea190bf2cde8c18e2ac8276d2eb57d219db263)
*(uncategorized)* Clippy::cast_lossless by @EdJoPaTo
- [0de5238](https://github.com/ratatui-org/ratatui/commit/0de5238ed3613f2d663f5e9628ca7b2aa205ed02)
*(uncategorized)* Dead_code by @EdJoPaTo
````text
enabled by default, only detected by nightly yet
````
- [df5dddf](https://github.com/ratatui-org/ratatui/commit/df5dddfbc9c679d15a5a90ea79bb1f8946d5cb9c)
*(uncategorized)* Unused_imports by @EdJoPaTo
````text
enabled by default, only detected on nightly yet
````
- [f1398ae](https://github.com/ratatui-org/ratatui/commit/f1398ae6cb1abd32106923d64844b482c7ba6f82)
*(uncategorized)* Clippy::useless_vec by @EdJoPaTo
````text
Lint enabled by default but only nightly finds this yet
````
- [525848f](https://github.com/ratatui-org/ratatui/commit/525848ff4e066526d402fecf1d5b9c63cff1f22a)
*(uncategorized)* Manually apply clippy::use_self for impl with lifetimes by @EdJoPaTo
- [660c718](https://github.com/ratatui-org/ratatui/commit/660c7183c7a10dc453d80dfb651d9534536960b9)
*(uncategorized)* Clippy::empty_line_after_doc_comments by @EdJoPaTo
- [ab951fa](https://github.com/ratatui-org/ratatui/commit/ab951fae8166c9321728ba942b48552dfe4d9c55)
*(uncategorized)* Clippy::return_self_not_must_use by @EdJoPaTo
- [3cd4369](https://github.com/ratatui-org/ratatui/commit/3cd436917649a93b4b80d0c4a0343284e0585522)
*(uncategorized)* Clippy::doc_markdown by @EdJoPaTo
- [9bc014d](https://github.com/ratatui-org/ratatui/commit/9bc014d7f16efdb70fcd6b6b786fe74eac7b9bdf)
*(uncategorized)* Clippy::items_after_statements by @EdJoPaTo
- [36a0cd5](https://github.com/ratatui-org/ratatui/commit/36a0cd56e5645533a1d6c2720536fa10a56b0d40)
*(uncategorized)* Clippy::deref_by_slicing by @EdJoPaTo
- [f7f6692](https://github.com/ratatui-org/ratatui/commit/f7f66928a8833532a3bc97292665640285e7aafa)
*(uncategorized)* Clippy::equatable_if_let by @EdJoPaTo
- [01418eb](https://github.com/ratatui-org/ratatui/commit/01418eb7c2e1874cb4070828c485d81ea171b18d)
*(uncategorized)* Clippy::default_trait_access by @EdJoPaTo
- [8536760](https://github.com/ratatui-org/ratatui/commit/8536760e7802a498f7c6d9fe8fb4c7920a1c6e71)
*(uncategorized)* Clippy::inefficient_to_string by @EdJoPaTo
- [a558b19](https://github.com/ratatui-org/ratatui/commit/a558b19c9a7b90a1ed3f309301f49f0b483e02ec)
*(uncategorized)* Clippy::implicit_clone by @EdJoPaTo
- [5b00e3a](https://github.com/ratatui-org/ratatui/commit/5b00e3aae98cb5c20c10bec944948a75ac83f956)
*(uncategorized)* Clippy::use_self by @EdJoPaTo
- [27680c0](https://github.com/ratatui-org/ratatui/commit/27680c05ce1670f026ad23c446ada321c1c755f0)
*(uncategorized)* Clippy::semicolon_if_nothing_returned by @EdJoPaTo
### Documentation
- [14461c3](https://github.com/ratatui-org/ratatui/commit/14461c3a3554c95905ebca433fc3d4dae1e1acda)
*(breaking-changes)* Typos and markdownlint by @EdJoPaTo in [#1009](https://github.com/ratatui-org/ratatui/pull/1009)
- [d0067c8](https://github.com/ratatui-org/ratatui/commit/d0067c8815d5244d319934d58a9366c8ad36b3e5)
*(license)* Update copyright years by @orhun in [#962](https://github.com/ratatui-org/ratatui/pull/962)
- [88bfb5a](https://github.com/ratatui-org/ratatui/commit/88bfb5a43027cf3410ad560772c5bfdbaa3d58b7)
*(text)* Update Text and Line docs by @joshka in [#969](https://github.com/ratatui-org/ratatui/pull/969)
- [3b002fd](https://github.com/ratatui-org/ratatui/commit/3b002fdcab964ce3f65f55dc8053d9678ae247a3)
*(uncategorized)* Update incompatible code warning in examples readme by @joshka in [#1013](https://github.com/ratatui-org/ratatui/pull/1013)
### Performance
- [e02f476](https://github.com/ratatui-org/ratatui/commit/e02f4768ce2ee30473200fe98e2687e42acb9c33)
*(borders)* Allow border!() in const by @EdJoPaTo in [#977](https://github.com/ratatui-org/ratatui/pull/977)
````text
This allows more compiler optimizations when the macro is used.
````
- [541f0f9](https://github.com/ratatui-org/ratatui/commit/541f0f99538762a07d68a71b2989ecc6ff6f71ef)
*(cell)* Use const CompactString::new_inline by @EdJoPaTo in [#979](https://github.com/ratatui-org/ratatui/pull/979)
````text
Some minor find when messing around trying to `const` all the things.
While `reset()` and `default()` can not be `const` it's still a benefit
when their contents are.
````
- [65e7923](https://github.com/ratatui-org/ratatui/commit/65e792375396c3160d76964ef0dfc4fb1e53be41)
*(scrollbar)* Const creation by @EdJoPaTo in [#963](https://github.com/ratatui-org/ratatui/pull/963)
````text
A bunch of `const fn` allow for more performance and `Default` now uses the `const` new implementations.
````
- [8195f52](https://github.com/ratatui-org/ratatui/commit/8195f526cb4b321f337dcbe9e689cc7f6eb84065)
*(uncategorized)* Clippy::needless_pass_by_value by @EdJoPaTo
- [183c07e](https://github.com/ratatui-org/ratatui/commit/183c07ef436cbb8fb0bec418042b44b4fedd836f)
*(uncategorized)* Clippy::trivially_copy_pass_by_ref by @EdJoPaTo
- [a13867f](https://github.com/ratatui-org/ratatui/commit/a13867ffceb2f8f57f4540049754c2f916fd3efc)
*(uncategorized)* Clippy::cloned_instead_of_copied by @EdJoPaTo
- [3834374](https://github.com/ratatui-org/ratatui/commit/3834374652b46c5ddbfedcf8dea2086fd762f884)
*(uncategorized)* Clippy::missing_const_for_fn by @EdJoPaTo
### Miscellaneous Tasks
- [125ee92](https://github.com/ratatui-org/ratatui/commit/125ee929ee9009b97a270e2e105a3f1167ab13d7)
*(docs)* Fix: fix typos in crate documentation by @orhun in [#1002](https://github.com/ratatui-org/ratatui/pull/1002)
- [38c17e0](https://github.com/ratatui-org/ratatui/commit/38c17e091cf3f4de2d196ecdd6a40129019eafc4)
*(editorconfig)* Set and apply some defaults by @EdJoPaTo
- [07da90a](https://github.com/ratatui-org/ratatui/commit/07da90a7182035b24f870bcbf0a0ffaad75eb48b)
*(funding)* Add eth address for receiving funds from drips.network by @BenJam in [#994](https://github.com/ratatui-org/ratatui/pull/994)
- [078e97e](https://github.com/ratatui-org/ratatui/commit/078e97e4ff65c02afa7c884914ecd38a6e959b58)
*(github)* Add EdJoPaTo as a maintainer by @orhun in [#986](https://github.com/ratatui-org/ratatui/pull/986)
- [b0314c5](https://github.com/ratatui-org/ratatui/commit/b0314c5731b32f51f5b6ca71a5194c6d7f265972)
*(uncategorized)* Remove conventional commit check for PR by @Valentin271 in [#950](https://github.com/ratatui-org/ratatui/pull/950)
````text
This removes conventional commit check for PRs.
Since we use the PR title and description this is useless. It fails a
lot of time and we ignore it.
IMPORTANT NOTE: This does **not** mean Ratatui abandons conventional
commits. This only relates to commits in PRs.
````
### Build
- [6e6ba27](https://github.com/ratatui-org/ratatui/commit/6e6ba27a122560bcf47b0efd20b7095f1bfd8714)
*(lint)* Warn on pedantic and allow the rest by @EdJoPaTo
- [c4ce7e8](https://github.com/ratatui-org/ratatui/commit/c4ce7e8ff6f00875e1ead5b68052f0db737bd44d)
*(uncategorized)* Enable more satisfied lints by @EdJoPaTo
````text
These lints dont generate warnings and therefore dont need refactoring.
I think they are useful in the future.
````
- [a4e84a6](https://github.com/ratatui-org/ratatui/commit/a4e84a6a7f6f5b80903799028f30e2a4438f2807)
*(uncategorized)* Increase msrv to 1.74.0 by @EdJoPaTo [**breaking**]
````text
configure lints in Cargo.toml requires 1.74.0
````
BREAKING CHANGE:rust 1.74 is required now
### New Contributors
* @TadoTheMiner made their first contribution in [#987](https://github.com/ratatui-org/ratatui/pull/987)
* @BenJam made their first contribution in [#994](https://github.com/ratatui-org/ratatui/pull/994)
* @CameronBarnes made their first contribution in [#958](https://github.com/ratatui-org/ratatui/pull/958)
* @ThomasMiz made their first contribution in [#959](https://github.com/ratatui-org/ratatui/pull/959)
**Full Changelog**: https://github.com/ratatui-org/ratatui/compare/v0.26.1...0.26.2
## [0.26.1](https://github.com/ratatui-org/ratatui/releases/tag/v0.26.1) - 2024-02-12
## [0.26.1](https://github.com/ratatui-org/ratatui/releases/tag/0.26.1) - 2024-02-12
This is a patch release that fixes bugs and adds enhancements, including new iterators, title options for blocks, and various rendering improvements. ✨
@@ -611,7 +161,7 @@ Here is the list of contributors who have contributed to `ratatui` for the first
* @mo8it
* @m4rch3n1ng
## [0.26.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.26.0) - 2024-02-02
## [0.26.0](https://github.com/ratatui-org/ratatui/releases/tag/0.26.0) - 2024-02-02
We are excited to announce the new version of `ratatui` - a Rust library that's all about cooking up TUIs 🐭
@@ -2268,7 +1818,7 @@ Shout out to our new sponsors!
* @atuinsh
* @JeftavanderHorst!
## [0.25.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.25.0) - 2023-12-18
## [0.25.0](https://github.com/ratatui-org/ratatui/releases/tag/0.25.0) - 2023-12-18
We are thrilled to announce the new version of `ratatui` - a Rust library that's all about cooking up TUIs 🐭
@@ -2753,7 +2303,7 @@ Here is the list of contributors who have contributed to `ratatui` for the first
* @YeungKC
* @lyuha
## [0.24.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.24.0) - 2023-10-23
## [0.24.0](https://github.com/ratatui-org/ratatui/releases/tag/0.24.0) - 2023-10-23
We are excited to announce the new version of `ratatui` - a Rust library that's all about cooking up TUIs 🐭

View File

@@ -1,13 +1,11 @@
[package]
name = "ratatui"
version = "0.26.2" # crate version
version = "0.26.1" # crate version
authors = ["Florian Dehau <work@fdehau.com>", "The Ratatui Developers"]
description = "A library that's all about cooking up terminal user interfaces"
documentation = "https://docs.rs/ratatui/latest/ratatui/"
repository = "https://github.com/ratatui-org/ratatui"
homepage = "https://ratatui.rs"
keywords = ["tui", "terminal", "dashboard"]
categories = ["command-line-interface"]
repository = "https://github.com/ratatui-org/ratatui"
readme = "README.md"
license = "MIT"
exclude = [
@@ -41,7 +39,7 @@ unicode-segmentation = "1.10"
unicode-width = "0.1"
document-features = { version = "0.2.7", optional = true }
lru = "0.12.0"
stability = "0.2.0"
stability = "0.1.1"
compact_str = "0.7.1"
[dev-dependencies]
@@ -60,14 +58,12 @@ palette = "0.7.3"
pretty_assertions = "1.4.0"
rand = "0.8.5"
rand_chacha = "0.3.1"
rstest = "0.19.0"
rstest = "0.18.2"
serde_json = "1.0.109"
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
cargo = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
cast_possible_truncation = "allow"
cast_possible_wrap = "allow"

View File

@@ -162,7 +162,7 @@ fn handle_events() -> io::Result<bool> {
fn ui(frame: &mut Frame) {
frame.render_widget(
Paragraph::new("Hello World!")
.block(Block::bordered().title("Greeting")),
.block(Block::default().title("Greeting").borders(Borders::ALL)),
frame.size(),
);
}
@@ -207,11 +207,11 @@ fn ui(frame: &mut Frame) {
)
.split(main_layout[1]);
frame.render_widget(
Block::bordered().title("Left"),
Block::default().borders(Borders::ALL).title("Left"),
inner_layout[0],
);
frame.render_widget(
Block::bordered().title("Right"),
Block::default().borders(Borders::ALL).title("Right"),
inner_layout[1],
);
}
@@ -327,23 +327,24 @@ Running this example produces the following output:
[Termwiz]: https://crates.io/crates/termwiz
[tui-rs]: https://crates.io/crates/tui
[GitHub Sponsors]: https://github.com/sponsors/ratatui-org
[Crate Badge]: https://img.shields.io/crates/v/ratatui?logo=rust&style=flat-square&logoColor=E05D44&color=E05D44
[License Badge]: https://img.shields.io/crates/l/ratatui?style=flat-square&color=1370D3
[CI Badge]: https://img.shields.io/github/actions/workflow/status/ratatui-org/ratatui/ci.yml?style=flat-square&logo=github
[Crate Badge]: https://img.shields.io/crates/v/ratatui?logo=rust&style=flat-square
[License Badge]: https://img.shields.io/crates/l/ratatui?style=flat-square
[CI Badge]:
https://img.shields.io/github/actions/workflow/status/ratatui-org/ratatui/ci.yml?style=flat-square&logo=github
[CI Workflow]: https://github.com/ratatui-org/ratatui/actions/workflows/ci.yml
[Codecov Badge]:
https://img.shields.io/codecov/c/github/ratatui-org/ratatui?logo=codecov&style=flat-square&token=BAQ8SOKEST&color=C43AC3&logoColor=C43AC3
https://img.shields.io/codecov/c/github/ratatui-org/ratatui?logo=codecov&style=flat-square&token=BAQ8SOKEST
[Codecov]: https://app.codecov.io/gh/ratatui-org/ratatui
[Deps.rs Badge]: https://deps.rs/repo/github/ratatui-org/ratatui/status.svg?style=flat-square
[Deps.rs]: https://deps.rs/repo/github/ratatui-org/ratatui
[Discord Badge]:
https://img.shields.io/discord/1070692720437383208?label=discord&logo=discord&style=flat-square&color=1370D3&logoColor=1370D3
https://img.shields.io/discord/1070692720437383208?label=discord&logo=discord&style=flat-square
[Discord Server]: https://discord.gg/pMCEU9hNEj
[Docs Badge]: https://img.shields.io/docsrs/ratatui?logo=rust&style=flat-square&logoColor=E05D44
[Docs Badge]: https://img.shields.io/docsrs/ratatui?logo=rust&style=flat-square
[Matrix Badge]:
https://img.shields.io/matrix/ratatui-general%3Amatrix.org?style=flat-square&logo=matrix&label=Matrix&color=C43AC3
https://img.shields.io/matrix/ratatui-general%3Amatrix.org?style=flat-square&logo=matrix&label=Matrix
[Matrix]: https://matrix.to/#/#ratatui:matrix.org
[Sponsors Badge]: https://img.shields.io/github/sponsors/ratatui-org?logo=github&style=flat-square&color=1370D3
[Sponsors Badge]: https://img.shields.io/github/sponsors/ratatui-org?logo=github&style=flat-square
<!-- cargo-rdme end -->

View File

@@ -1,10 +1,11 @@
use criterion::{criterion_group, criterion_main, BatchSize, Bencher, Criterion};
use criterion::{criterion_group, criterion_main, BatchSize, Bencher, BenchmarkId, Criterion};
use ratatui::{
buffer::Buffer,
layout::{Alignment, Rect},
layout::Rect,
prelude::Alignment,
widgets::{
block::{Position, Title},
Block, Padding, Widget,
Block, Borders, Padding, Widget,
},
};
@@ -12,31 +13,32 @@ use ratatui::{
fn block(c: &mut Criterion) {
let mut group = c.benchmark_group("block");
for (width, height) in [
(100, 50), // vertically split screen
(200, 50), // 1080p fullscreen with medium font
(256, 256), // Max sized area
for buffer_size in [
Rect::new(0, 0, 100, 50), // vertically split screen
Rect::new(0, 0, 200, 50), // 1080p fullscreen with medium font
Rect::new(0, 0, 256, 256), // Max sized area
] {
let buffer_size = Rect::new(0, 0, width, height);
let buffer_area = buffer_size.area();
// Render an empty block
group.bench_with_input(
format!("render_empty/{width}x{height}"),
BenchmarkId::new("render_empty", buffer_area),
&Block::new(),
|b, block| render(b, block, buffer_size),
);
// Render with all features
group.bench_with_input(
format!("render_all_feature/{width}x{height}"),
&Block::bordered()
.padding(Padding::new(5, 5, 2, 2))
BenchmarkId::new("render_all_feature", buffer_area),
&Block::new()
.borders(Borders::ALL)
.title("test title")
.title(
Title::from("bottom left title")
.alignment(Alignment::Right)
.position(Position::Bottom),
),
)
.padding(Padding::new(5, 5, 2, 2)),
|b, block| render(b, block, buffer_size),
);
}

View File

@@ -1,9 +1,4 @@
# git-cliff ~ configuration file
# https://git-cliff.org/docs/configuration
[remote.github]
owner = "ratatui-org"
repo = "ratatui"
# configuration for https://github.com/orhun/git-cliff
[changelog]
# changelog header
@@ -27,9 +22,7 @@ body = """
{% macro commit(commit) -%}
- [{{ commit.id | truncate(length=7, end="") }}]({{ "https://github.com/ratatui-org/ratatui/commit/" ~ commit.id }})
*({{commit.scope | default(value = "uncategorized") | lower }})* {{ commit.message | upper_first | trim }}\
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif -%}\
{% if commit.github.pr_number %} in [#{{ commit.github.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.github.pr_number }}){%- endif %}\
*({{commit.scope | default(value = "uncategorized") | lower }})* {{ commit.message | upper_first }}
{%- if commit.breaking %} [**breaking**]{% endif %}
{%- if commit.body %}
@@ -56,28 +49,6 @@ body = """
{%- endif -%}
{%- endfor -%}
{%- endfor %}
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
### New Contributors
{%- endif %}\
{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %}
* @{{ contributor.username }} made their first contribution
{%- if contributor.pr_number %} in \
[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
{%- endif %}
{%- endfor -%}
{% if version %}
{% if previous.version %}
**Full Changelog**: {{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}
{% endif %}
{% else -%}
{% raw %}\n{% endraw %}
{% endif %}
{%- macro remote_url() -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}\
{% endmacro %}
"""
@@ -97,7 +68,7 @@ filter_unconventional = true
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" },
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/ratatui-org/ratatui/issues/${2}))" },
{ pattern = '(better safe shared layout cache)', replace = "perf(layout): ${1}" },
{ pattern = '(Clarify README.md)', replace = "docs(readme): ${1}" },
{ pattern = '(Update README.md)', replace = "docs(readme): ${1}" },

View File

@@ -1,15 +0,0 @@
# https://rust-lang.github.io/rust-clippy/master/index.html#/multiple_crate_versions
# ratatui -> bitflags v2.3
# termwiz -> wezterm-blob-leases -> mac_address -> nix -> bitflags v1.3.2
# crossterm -> all the windows- deps https://github.com/ratatui-org/ratatui/pull/1064#issuecomment-2078848980
allowed-duplicate-crates = [
"bitflags",
"windows-targets",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]

View File

@@ -1,20 +1,20 @@
# Examples
This folder might use unreleased code. View the examples for the latest release instead.
This folder contains unreleased code. View the [examples for the latest release
(0.25.0)](https://github.com/ratatui-org/ratatui/tree/v0.25.0/examples) instead.
> [!WARNING]
>
> There may be backwards incompatible changes in these examples, as they are designed to compile
> There are backwards incompatible changes in these examples, as they are designed to compile
> against the `main` branch.
>
> There are a few workaround for this problem:
>
> - View the examples as they were when the latest version was release by selecting the tag that
> matches that version. E.g. <https://github.com/ratatui-org/ratatui/tree/v0.26.1/examples>.
> - If you're viewing this file on GitHub, there is a combo box at the top of this page which
> allows you to select any previous tagged version.
> - To view the code locally, checkout the tag. E.g. `git switch --detach v0.26.1`.
> - Use the latest [alpha version of Ratatui] in your app. These are released weekly on Saturdays.
> matches that version. E.g. <https://github.com/ratatui-org/ratatui/tree/v0.25.0/examples>. There
> is a combo box at the top of this page which allows you to select any previous tagged version.
> - To view the code locally, checkout the tag using `git switch --detach v0.25.0`.
> - Use the latest [alpha version of Ratatui]. These are released weekly on Saturdays.
> - Compile your code against the main branch either locally by adding e.g. `path = "../ratatui"` to
> the dependency, or remotely by adding `git = "https://github.com/ratatui-org/ratatui"`
>

View File

@@ -26,7 +26,7 @@ use crossterm::{
};
use ratatui::{
prelude::*,
widgets::{Bar, BarChart, BarGroup, Block, Paragraph},
widgets::{Bar, BarChart, BarGroup, Block, Borders, Paragraph},
};
struct Company<'a> {
@@ -159,7 +159,7 @@ fn ui(frame: &mut Frame, app: &App) {
let [left, right] = horizontal.areas(bottom);
let barchart = BarChart::default()
.block(Block::bordered().title("Data1"))
.block(Block::default().title("Data1").borders(Borders::ALL))
.data(&app.data)
.bar_width(9)
.bar_style(Style::default().fg(Color::Yellow))
@@ -217,7 +217,7 @@ fn draw_bar_with_group_labels(f: &mut Frame, app: &App, area: Rect) {
let groups = create_groups(app, false);
let mut barchart = BarChart::default()
.block(Block::bordered().title("Data1"))
.block(Block::default().title("Data1").borders(Borders::ALL))
.bar_width(7)
.group_gap(3);
@@ -246,7 +246,7 @@ fn draw_horizontal_bars(f: &mut Frame, app: &App, area: Rect) {
let groups = create_groups(app, true);
let mut barchart = BarChart::default()
.block(Block::bordered().title("Data1"))
.block(Block::default().title("Data1").borders(Borders::ALL))
.bar_width(1)
.group_gap(1)
.bar_gap(0)
@@ -292,7 +292,9 @@ fn draw_legend(f: &mut Frame, area: Rect) {
)]),
];
let block = Block::bordered().style(Style::default().fg(Color::White));
let block = Block::default()
.borders(Borders::ALL)
.style(Style::default().fg(Color::White));
let paragraph = Paragraph::new(text).block(block);
f.render_widget(paragraph, area);
}

View File

@@ -160,20 +160,23 @@ fn render_border_type(
frame: &mut Frame,
area: Rect,
) {
let block = Block::bordered()
let block = Block::new()
.borders(Borders::ALL)
.border_type(border_type)
.title(format!("BorderType::{border_type:#?}"));
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_styled_borders(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
let block = Block::new()
.borders(Borders::ALL)
.border_style(Style::new().blue().on_white().bold().italic())
.title("Styled borders");
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_styled_block(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
let block = Block::new()
.borders(Borders::ALL)
.style(Style::new().blue().on_white().bold().italic())
.title("Styled block");
frame.render_widget(paragraph.clone().block(block), area);
@@ -181,7 +184,8 @@ fn render_styled_block(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
// Note: this currently renders incorrectly, see https://github.com/ratatui-org/ratatui/issues/349
fn render_styled_title(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
let block = Block::new()
.borders(Borders::ALL)
.title("Styled title")
.title_style(Style::new().blue().on_white().bold().italic());
frame.render_widget(paragraph.clone().block(block), area);
@@ -192,19 +196,21 @@ fn render_styled_title_content(paragraph: &Paragraph, frame: &mut Frame, area: R
"Styled ".blue().on_white().bold().italic(),
"title content".red().on_white().bold().italic(),
]);
let block = Block::bordered().title(title);
let block = Block::new().borders(Borders::ALL).title(title);
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_multiple_titles(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
let block = Block::new()
.borders(Borders::ALL)
.title("Multiple".blue().on_white().bold().italic())
.title("Titles".red().on_white().bold().italic());
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_multiple_title_positions(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
let block = Block::new()
.borders(Borders::ALL)
.title(
Title::from("top left")
.position(Position::Top)
@@ -239,15 +245,16 @@ fn render_multiple_title_positions(paragraph: &Paragraph, frame: &mut Frame, are
}
fn render_padding(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
.padding(Padding::new(5, 10, 1, 2))
.title("Padding");
let block = Block::new()
.borders(Borders::ALL)
.title("Padding")
.padding(Padding::new(5, 10, 1, 2));
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_nested_blocks(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let outer_block = Block::bordered().title("Outer block");
let inner_block = Block::bordered().title("Inner block");
let outer_block = Block::new().borders(Borders::ALL).title("Outer block");
let inner_block = Block::new().borders(Borders::ALL).title("Inner block");
let inner = outer_block.inner(area);
frame.render_widget(outer_block, area);
frame.render_widget(paragraph.clone().block(inner_block), inner);

View File

@@ -137,7 +137,7 @@ impl App {
fn map_canvas(&self) -> impl Widget + '_ {
Canvas::default()
.block(Block::bordered().title("World"))
.block(Block::default().borders(Borders::ALL).title("World"))
.marker(self.marker)
.paint(|ctx| {
ctx.draw(&Map {
@@ -152,7 +152,7 @@ impl App {
fn pong_canvas(&self) -> impl Widget + '_ {
Canvas::default()
.block(Block::bordered().title("Pong"))
.block(Block::default().borders(Borders::ALL).title("Pong"))
.marker(self.marker)
.paint(|ctx| {
ctx.draw(&self.ball);
@@ -167,7 +167,7 @@ impl App {
let bottom = 0.0;
let top = f64::from(area.height).mul_add(2.0, -4.0);
Canvas::default()
.block(Block::bordered().title("Rects"))
.block(Block::default().borders(Borders::ALL).title("Rects"))
.marker(self.marker)
.x_bounds([left, right])
.y_bounds([bottom, top])

View File

@@ -26,7 +26,7 @@ use crossterm::{
};
use ratatui::{
prelude::*,
widgets::{block::Title, Axis, Block, Chart, Dataset, GraphType, LegendPosition},
widgets::{block::Title, Axis, Block, Borders, Chart, Dataset, GraphType, LegendPosition},
};
#[derive(Clone)]
@@ -184,7 +184,11 @@ fn render_chart1(f: &mut Frame, area: Rect, app: &App) {
];
let chart = Chart::new(datasets)
.block(Block::bordered().title("Chart 1".cyan().bold()))
.block(
Block::default()
.title("Chart 1".cyan().bold())
.borders(Borders::ALL),
)
.x_axis(
Axis::default()
.title("X Axis")
@@ -213,11 +217,13 @@ fn render_line_chart(f: &mut Frame, area: Rect) {
let chart = Chart::new(datasets)
.block(
Block::bordered().title(
Title::default()
.content("Line chart".cyan().bold())
.alignment(Alignment::Center),
),
Block::default()
.title(
Title::default()
.content("Line chart".cyan().bold())
.alignment(Alignment::Center),
)
.borders(Borders::ALL),
)
.x_axis(
Axis::default()
@@ -263,7 +269,7 @@ fn render_scatter(f: &mut Frame, area: Rect) {
let chart = Chart::new(datasets)
.block(
Block::bordered().title(
Block::new().borders(Borders::all()).title(
Title::default()
.content("Scatter chart".cyan().bold())
.alignment(Alignment::Center),

View File

@@ -230,12 +230,12 @@ fn render_indexed_colors(frame: &mut Frame, area: Rect) {
}
fn title_block(title: String) -> Block<'static> {
Block::new()
Block::default()
.borders(Borders::TOP)
.title_alignment(Alignment::Center)
.border_style(Style::new().dark_gray())
.title_style(Style::new().reset())
.title(title)
.title_alignment(Alignment::Center)
.title_style(Style::new().reset())
}
fn render_indexed_grayscale(frame: &mut Frame, area: Rect) {

View File

@@ -436,7 +436,7 @@ impl ConstraintBlock {
} else {
main_color
};
Block::new()
Block::default()
.fg(Self::TEXT_COLOR)
.bg(selected_color)
.render(area, buf);

View File

@@ -19,7 +19,7 @@ use std::io::{self, stdout};
use color_eyre::{config::HookBuilder, Result};
use crossterm::{
event::{self, Event, KeyCode, KeyEventKind},
event::{self, Event, KeyCode},
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
};
@@ -109,9 +109,6 @@ impl App {
fn handle_events(&mut self) -> Result<()> {
if let Event::Key(key) = event::read()? {
use KeyCode::*;
if key.kind != KeyEventKind::Press {
return Ok(());
}
match key.code {
Char('q') | Esc => self.quit(),
Char('l') | Right => self.next(),
@@ -197,7 +194,7 @@ impl App {
);
Paragraph::new(width_bar.dark_gray())
.centered()
.block(Block::new().padding(Padding {
.block(Block::default().padding(Padding {
left: 0,
right: 0,
top: 1,

View File

@@ -14,7 +14,7 @@ pub fn draw(f: &mut Frame, app: &mut App) {
.iter()
.map(|t| text::Line::from(Span::styled(*t, Style::default().fg(Color::Green))))
.collect::<Tabs>()
.block(Block::bordered().title(app.title))
.block(Block::default().borders(Borders::ALL).title(app.title))
.highlight_style(Style::default().fg(Color::Yellow))
.select(app.tabs.index);
f.render_widget(tabs, chunks[0]);
@@ -46,12 +46,12 @@ fn draw_gauges(f: &mut Frame, app: &mut App, area: Rect) {
])
.margin(1)
.split(area);
let block = Block::bordered().title("Graphs");
let block = Block::default().borders(Borders::ALL).title("Graphs");
f.render_widget(block, area);
let label = format!("{:.2}%", app.progress * 100.0);
let gauge = Gauge::default()
.block(Block::new().title("Gauge:"))
.block(Block::default().title("Gauge:"))
.gauge_style(
Style::default()
.fg(Color::Magenta)
@@ -64,7 +64,7 @@ fn draw_gauges(f: &mut Frame, app: &mut App, area: Rect) {
f.render_widget(gauge, chunks[0]);
let sparkline = Sparkline::default()
.block(Block::new().title("Sparkline:"))
.block(Block::default().title("Sparkline:"))
.style(Style::default().fg(Color::Green))
.data(&app.sparkline.points)
.bar_set(if app.enhanced_graphics {
@@ -75,7 +75,7 @@ fn draw_gauges(f: &mut Frame, app: &mut App, area: Rect) {
f.render_widget(sparkline, chunks[1]);
let line_gauge = LineGauge::default()
.block(Block::new().title("LineGauge:"))
.block(Block::default().title("LineGauge:"))
.gauge_style(Style::default().fg(Color::Magenta))
.line_set(if app.enhanced_graphics {
symbols::line::THICK
@@ -110,7 +110,7 @@ fn draw_charts(f: &mut Frame, app: &mut App, area: Rect) {
.map(|i| ListItem::new(vec![text::Line::from(Span::raw(*i))]))
.collect();
let tasks = List::new(tasks)
.block(Block::bordered().title("List"))
.block(Block::default().borders(Borders::ALL).title("List"))
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
.highlight_symbol("> ");
f.render_stateful_widget(tasks, chunks[0], &mut app.tasks.state);
@@ -138,12 +138,12 @@ fn draw_charts(f: &mut Frame, app: &mut App, area: Rect) {
ListItem::new(content)
})
.collect();
let logs = List::new(logs).block(Block::bordered().title("List"));
let logs = List::new(logs).block(Block::default().borders(Borders::ALL).title("List"));
f.render_stateful_widget(logs, chunks[1], &mut app.logs.state);
}
let barchart = BarChart::default()
.block(Block::bordered().title("Bar chart"))
.block(Block::default().borders(Borders::ALL).title("Bar chart"))
.data(&app.barchart)
.bar_width(3)
.bar_gap(2)
@@ -195,12 +195,14 @@ fn draw_charts(f: &mut Frame, app: &mut App, area: Rect) {
];
let chart = Chart::new(datasets)
.block(
Block::bordered().title(Span::styled(
"Chart",
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
)),
Block::default()
.title(Span::styled(
"Chart",
Style::default()
.fg(Color::Cyan)
.add_modifier(Modifier::BOLD),
))
.borders(Borders::ALL),
)
.x_axis(
Axis::default()
@@ -252,7 +254,7 @@ fn draw_text(f: &mut Frame, area: Rect) {
"One more thing is that it should display unicode characters: 10€"
),
];
let block = Block::bordered().title(Span::styled(
let block = Block::default().borders(Borders::ALL).title(Span::styled(
"Footer",
Style::default()
.fg(Color::Magenta)
@@ -290,11 +292,11 @@ fn draw_second_tab(f: &mut Frame, app: &mut App, area: Rect) {
.style(Style::default().fg(Color::Yellow))
.bottom_margin(1),
)
.block(Block::bordered().title("Servers"));
.block(Block::default().title("Servers").borders(Borders::ALL));
f.render_widget(table, chunks[0]);
let map = Canvas::default()
.block(Block::bordered().title("World"))
.block(Block::default().title("World").borders(Borders::ALL))
.paint(|ctx| {
ctx.draw(&Map {
color: Color::White,
@@ -388,6 +390,6 @@ fn draw_third_tab(f: &mut Frame, _app: &mut App, area: Rect) {
Constraint::Ratio(1, 3),
],
)
.block(Block::bordered().title("Colors"));
.block(Block::default().title("Colors").borders(Borders::ALL));
f.render_widget(table, chunks[0]);
}

View File

@@ -38,10 +38,14 @@ enum Tab {
}
pub fn run(terminal: &mut Terminal<impl Backend>) -> Result<()> {
App::default().run(terminal)
App::new().run(terminal)
}
impl App {
pub fn new() -> Self {
Self::default()
}
/// Run the app until the user quits.
pub fn run(&mut self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
while self.is_running() {

View File

@@ -39,7 +39,7 @@ pub use theme::*;
fn main() -> Result<()> {
errors::init_hooks()?;
let terminal = &mut term::init()?;
App::default().run(terminal)?;
App::new().run(terminal)?;
term::restore()?;
Ok(())
}

View File

@@ -49,10 +49,10 @@ fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
.iter()
.map(|hop| Row::new(vec![hop.host, hop.address]))
.collect_vec();
let block = Block::new()
.padding(Padding::new(1, 1, 1, 1))
let block = Block::default()
.title("Traceroute bad.horse".bold().white())
.title_alignment(Alignment::Center)
.title("Traceroute bad.horse".bold().white());
.padding(Padding::new(1, 1, 1, 1));
StatefulWidget::render(
Table::new(rows, [Constraint::Max(100), Constraint::Length(15)])
.header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header))

View File

@@ -51,7 +51,8 @@ fn main() -> io::Result<()> {
fn hello_world(frame: &mut Frame) {
frame.render_widget(
Paragraph::new("Hello World!").block(Block::bordered().title("Greeting")),
Paragraph::new("Hello World!")
.block(Block::default().title("Greeting").borders(Borders::ALL)),
frame.size(),
);
}
@@ -86,8 +87,8 @@ fn layout(frame: &mut Frame) {
Block::new().borders(Borders::TOP).title("Status Bar"),
status_bar,
);
frame.render_widget(Block::bordered().title("Left"), left);
frame.render_widget(Block::bordered().title("Right"), right);
frame.render_widget(Block::default().borders(Borders::ALL).title("Left"), left);
frame.render_widget(Block::default().borders(Borders::ALL).title("Right"), right);
}
fn styling(frame: &mut Frame) {

View File

@@ -206,11 +206,11 @@ impl App {
fn title_block(title: &str) -> Block {
let title = Title::from(title).alignment(Alignment::Center);
Block::new()
.borders(Borders::NONE)
.padding(Padding::vertical(1))
Block::default()
.title(title)
.borders(Borders::NONE)
.fg(CUSTOM_LABEL_COLOR)
.padding(Padding::vertical(1))
}
fn init_error_hooks() -> color_eyre::Result<()> {

View File

@@ -236,7 +236,7 @@ fn run_app<B: Backend>(
fn ui(f: &mut Frame, downloads: &Downloads) {
let area = f.size();
let block = Block::new().title(block::Title::from("Progress").alignment(Alignment::Center));
let block = Block::default().title(block::Title::from("Progress").alignment(Alignment::Center));
f.render_widget(block, area);
let vertical = Layout::vertical([Constraint::Length(2), Constraint::Length(4)]).margin(1);

View File

@@ -26,7 +26,7 @@ use itertools::Itertools;
use ratatui::{
layout::Constraint::*,
prelude::*,
widgets::{Block, Paragraph},
widgets::{Block, Borders, Paragraph},
};
fn main() -> Result<(), Box<dyn Error>> {
@@ -190,9 +190,10 @@ fn render_example_combination(
title: &str,
constraints: Vec<(Constraint, Constraint)>,
) {
let block = Block::bordered()
let block = Block::default()
.title(title.gray())
.style(Style::reset())
.borders(Borders::ALL)
.border_style(Style::default().fg(Color::DarkGray));
let inner = block.inner(area);
frame.render_widget(block, area);

View File

@@ -189,13 +189,13 @@ impl Widget for &mut App<'_> {
impl App<'_> {
fn render_todo(&mut self, area: Rect, buf: &mut Buffer) {
// We create two blocks, one is for the header (outer) and the other is for list (inner).
let outer_block = Block::new()
let outer_block = Block::default()
.borders(Borders::NONE)
.title_alignment(Alignment::Center)
.title("TODO List")
.fg(TEXT_COLOR)
.bg(TODO_HEADER_BG);
let inner_block = Block::new()
.bg(TODO_HEADER_BG)
.title("TODO List")
.title_alignment(Alignment::Center);
let inner_block = Block::default()
.borders(Borders::NONE)
.fg(TEXT_COLOR)
.bg(NORMAL_ROW_COLOR);
@@ -246,16 +246,16 @@ impl App<'_> {
};
// We show the list item's info under the list in this paragraph
let outer_info_block = Block::new()
let outer_info_block = Block::default()
.borders(Borders::NONE)
.title_alignment(Alignment::Center)
.title("TODO Info")
.fg(TEXT_COLOR)
.bg(TODO_HEADER_BG);
let inner_info_block = Block::new()
.bg(TODO_HEADER_BG)
.title("TODO Info")
.title_alignment(Alignment::Center);
let inner_info_block = Block::default()
.borders(Borders::NONE)
.padding(Padding::horizontal(1))
.bg(NORMAL_ROW_COLOR);
.bg(NORMAL_ROW_COLOR)
.padding(Padding::horizontal(1));
// This is a similar process to what we did for list. outer_info_area will be used for
// header inner_info_area will be used for the list info.

View File

@@ -37,7 +37,7 @@ use crossterm::{
};
use ratatui::{
prelude::*,
widgets::{Block, Paragraph},
widgets::{Block, Borders, Paragraph},
};
type Result<T> = std::result::Result<T, Box<dyn Error>>;
@@ -142,9 +142,11 @@ fn ui(f: &mut Frame, app: &App) {
Line::from("try first without the panic handler to see the difference"),
];
let paragraph = Paragraph::new(text)
.block(Block::bordered().title("Panic Handler Demo"))
.centered();
let b = Block::default()
.title("Panic Handler Demo")
.borders(Borders::ALL);
f.render_widget(paragraph, f.size());
let p = Paragraph::new(text).block(b).centered();
f.render_widget(p, f.size());
}

View File

@@ -26,7 +26,7 @@ use crossterm::{
};
use ratatui::{
prelude::*,
widgets::{Block, Paragraph, Wrap},
widgets::{Block, Borders, Paragraph, Wrap},
};
struct App {
@@ -105,7 +105,7 @@ fn ui(f: &mut Frame, app: &App) {
let mut long_line = s.repeat(usize::from(size.width) / s.len() + 4);
long_line.push('\n');
let block = Block::new().black();
let block = Block::default().black();
f.render_widget(block, size);
let layout = Layout::vertical([Constraint::Ratio(1, 4); 4]).split(size);
@@ -127,7 +127,8 @@ fn ui(f: &mut Frame, app: &App) {
];
let create_block = |title| {
Block::bordered()
Block::default()
.borders(Borders::ALL)
.style(Style::default().fg(Color::Gray))
.title(Span::styled(
title,

View File

@@ -25,7 +25,7 @@ use crossterm::{
};
use ratatui::{
prelude::*,
widgets::{Block, Clear, Paragraph, Wrap},
widgets::{Block, Borders, Clear, Paragraph, Wrap},
};
struct App {
@@ -98,11 +98,14 @@ fn ui(f: &mut Frame, app: &App) {
.wrap(Wrap { trim: true });
f.render_widget(paragraph, instructions);
let block = Block::bordered().title("Content").on_blue();
let block = Block::default()
.title("Content")
.borders(Borders::ALL)
.on_blue();
f.render_widget(block, content);
if app.show_popup {
let block = Block::bordered().title("Popup");
let block = Block::default().title("Popup").borders(Borders::ALL);
let area = centered_rect(60, 20, area);
f.render_widget(Clear, area); //this clears out the background
f.render_widget(block, area);

View File

@@ -151,18 +151,18 @@ fn ui(f: &mut Frame, app: &App) {
.split(f.size());
let sparkline = Sparkline::default()
.block(
Block::new()
.borders(Borders::LEFT | Borders::RIGHT)
.title("Data1"),
Block::default()
.title("Data1")
.borders(Borders::LEFT | Borders::RIGHT),
)
.data(&app.data1)
.style(Style::default().fg(Color::Yellow));
f.render_widget(sparkline, chunks[0]);
let sparkline = Sparkline::default()
.block(
Block::new()
.borders(Borders::LEFT | Borders::RIGHT)
.title("Data2"),
Block::default()
.title("Data2")
.borders(Borders::LEFT | Borders::RIGHT),
)
.data(&app.data2)
.style(Style::default().bg(Color::Green));
@@ -170,9 +170,9 @@ fn ui(f: &mut Frame, app: &App) {
// Multiline
let sparkline = Sparkline::default()
.block(
Block::new()
.borders(Borders::LEFT | Borders::RIGHT)
.title("Data3"),
Block::default()
.title("Data3")
.borders(Borders::LEFT | Borders::RIGHT),
)
.data(&app.data3)
.style(Style::default().fg(Color::Red));

View File

@@ -331,9 +331,10 @@ fn render_footer(f: &mut Frame, app: &App, area: Rect) {
.style(Style::new().fg(app.colors.row_fg).bg(app.colors.buffer_bg))
.centered()
.block(
Block::bordered()
.border_type(BorderType::Double)
.border_style(Style::new().fg(app.colors.footer_border_color)),
Block::default()
.borders(Borders::ALL)
.border_style(Style::new().fg(app.colors.footer_border_color))
.border_type(BorderType::Double),
);
f.render_widget(info_footer, area);
}

View File

@@ -205,7 +205,8 @@ impl SelectedTab {
/// A block surrounding the tab's content
fn block(self) -> Block<'static> {
Block::bordered()
Block::default()
.borders(Borders::ALL)
.border_set(symbols::border::PROPORTIONAL_TALL)
.padding(Padding::horizontal(1))
.border_style(self.palette().c700)

View File

@@ -36,7 +36,7 @@ use crossterm::{
};
use ratatui::{
prelude::*,
widgets::{Block, List, ListItem, Paragraph},
widgets::{Block, Borders, List, ListItem, Paragraph},
};
enum InputMode {
@@ -49,7 +49,7 @@ struct App {
/// Current value of the input box
input: String,
/// Position of cursor in the editor area.
character_index: usize,
cursor_position: usize,
/// Current input mode
input_mode: InputMode,
/// History of recorded messages
@@ -62,46 +62,34 @@ impl App {
input: String::new(),
input_mode: InputMode::Normal,
messages: Vec::new(),
character_index: 0,
cursor_position: 0,
}
}
fn move_cursor_left(&mut self) {
let cursor_moved_left = self.character_index.saturating_sub(1);
self.character_index = self.clamp_cursor(cursor_moved_left);
let cursor_moved_left = self.cursor_position.saturating_sub(1);
self.cursor_position = self.clamp_cursor(cursor_moved_left);
}
fn move_cursor_right(&mut self) {
let cursor_moved_right = self.character_index.saturating_add(1);
self.character_index = self.clamp_cursor(cursor_moved_right);
let cursor_moved_right = self.cursor_position.saturating_add(1);
self.cursor_position = self.clamp_cursor(cursor_moved_right);
}
fn enter_char(&mut self, new_char: char) {
let index = self.byte_index();
self.input.insert(index, new_char);
self.input.insert(self.cursor_position, new_char);
self.move_cursor_right();
}
/// Returns the byte index based on the character position.
///
/// Since each character in a string can be contain multiple bytes, it's necessary to calculate
/// the byte index based on the index of the character.
fn byte_index(&mut self) -> usize {
self.input
.char_indices()
.map(|(i, _)| i)
.nth(self.character_index)
.unwrap_or(self.input.len())
}
fn delete_char(&mut self) {
let is_not_cursor_leftmost = self.character_index != 0;
let is_not_cursor_leftmost = self.cursor_position != 0;
if is_not_cursor_leftmost {
// Method "remove" is not used on the saved text for deleting the selected char.
// Reason: Using remove on String works on bytes instead of the chars.
// Using remove would require special care because of char boundaries.
let current_index = self.character_index;
let current_index = self.cursor_position;
let from_left_to_current_index = current_index - 1;
// Getting all characters before the selected character.
@@ -117,11 +105,11 @@ impl App {
}
fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
new_cursor_pos.clamp(0, self.input.chars().count())
new_cursor_pos.clamp(0, self.input.len())
}
fn reset_cursor(&mut self) {
self.character_index = 0;
self.cursor_position = 0;
}
fn submit_message(&mut self) {
@@ -238,7 +226,7 @@ fn ui(f: &mut Frame, app: &App) {
InputMode::Normal => Style::default(),
InputMode::Editing => Style::default().fg(Color::Yellow),
})
.block(Block::bordered().title("Input"));
.block(Block::default().borders(Borders::ALL).title("Input"));
f.render_widget(input, input_area);
match app.input_mode {
InputMode::Normal =>
@@ -252,7 +240,7 @@ fn ui(f: &mut Frame, app: &App) {
f.set_cursor(
// Draw the cursor at the current position in the input field.
// This position is can be controlled via the left and right arrow key
input_area.x + app.character_index as u16 + 1,
input_area.x + app.cursor_position as u16 + 1,
// Move one line down, from the border to the input line
input_area.y + 1,
);
@@ -268,6 +256,7 @@ fn ui(f: &mut Frame, app: &App) {
ListItem::new(content)
})
.collect();
let messages = List::new(messages).block(Block::bordered().title("Messages"));
let messages =
List::new(messages).block(Block::default().borders(Borders::ALL).title("Messages"));
f.render_widget(messages, messages_area);
}

View File

@@ -10,8 +10,8 @@ use crossterm::{
cursor::{Hide, MoveTo, Show},
execute, queue,
style::{
Attribute as CAttribute, Attributes as CAttributes, Color as CColor, Colors, ContentStyle,
Print, SetAttribute, SetBackgroundColor, SetColors, SetForegroundColor,
Attribute as CAttribute, Attributes as CAttributes, Color as CColor, ContentStyle, Print,
SetAttribute, SetBackgroundColor, SetForegroundColor,
},
terminal::{self, Clear},
};
@@ -145,12 +145,14 @@ where
diff.queue(&mut self.writer)?;
modifier = cell.modifier;
}
if cell.fg != fg || cell.bg != bg {
queue!(
self.writer,
SetColors(Colors::new(cell.fg.into(), cell.bg.into()))
)?;
if cell.fg != fg {
let color = CColor::from(cell.fg);
queue!(self.writer, SetForegroundColor(color))?;
fg = cell.fg;
}
if cell.bg != bg {
let color = CColor::from(cell.bg);
queue!(self.writer, SetBackgroundColor(color))?;
bg = cell.bg;
}
#[cfg(feature = "underline-color")]
@@ -226,7 +228,7 @@ where
Ok(Rect::new(0, 0, width, height))
}
fn window_size(&mut self) -> io::Result<WindowSize> {
fn window_size(&mut self) -> Result<WindowSize, io::Error> {
let crossterm::terminal::WindowSize {
columns,
rows,

View File

@@ -198,7 +198,7 @@ where
Ok(Rect::new(0, 0, terminal.0, terminal.1))
}
fn window_size(&mut self) -> io::Result<WindowSize> {
fn window_size(&mut self) -> Result<WindowSize, io::Error> {
Ok(WindowSize {
columns_rows: termion::terminal_size()?.into(),
pixels: termion::terminal_size_pixels()?.into(),

View File

@@ -111,7 +111,7 @@ impl TermwizBackend {
}
impl Backend for TermwizBackend {
fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
where
I: Iterator<Item = (u16, u16, &'a Cell)>,
{
@@ -181,13 +181,13 @@ impl Backend for TermwizBackend {
Ok(())
}
fn hide_cursor(&mut self) -> io::Result<()> {
fn hide_cursor(&mut self) -> Result<(), io::Error> {
self.buffered_terminal
.add_change(Change::CursorVisibility(CursorVisibility::Hidden));
Ok(())
}
fn show_cursor(&mut self) -> io::Result<()> {
fn show_cursor(&mut self) -> Result<(), io::Error> {
self.buffered_terminal
.add_change(Change::CursorVisibility(CursorVisibility::Visible));
Ok(())
@@ -207,18 +207,18 @@ impl Backend for TermwizBackend {
Ok(())
}
fn clear(&mut self) -> io::Result<()> {
fn clear(&mut self) -> Result<(), io::Error> {
self.buffered_terminal
.add_change(Change::ClearScreen(termwiz::color::ColorAttribute::Default));
Ok(())
}
fn size(&self) -> io::Result<Rect> {
fn size(&self) -> Result<Rect, io::Error> {
let (cols, rows) = self.buffered_terminal.dimensions();
Ok(Rect::new(0, 0, u16_max(cols), u16_max(rows)))
}
fn window_size(&mut self) -> io::Result<WindowSize> {
fn window_size(&mut self) -> Result<WindowSize, io::Error> {
let ScreenSize {
cols,
rows,
@@ -241,7 +241,7 @@ impl Backend for TermwizBackend {
})
}
fn flush(&mut self) -> io::Result<()> {
fn flush(&mut self) -> Result<(), io::Error> {
self.buffered_terminal
.flush()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
@@ -271,7 +271,7 @@ impl From<CellAttributes> for Style {
style.fg = Some(value.foreground().into());
style.bg = Some(value.background().into());
#[cfg(feature = "underline-color")]
#[cfg(feature = "underline_color")]
{
style.underline_color = Some(value.underline_color().into());
}
@@ -600,7 +600,7 @@ mod tests {
Style::new().fg(Color::Reset).bg(Color::Indexed(31))
);
// underline color
#[cfg(feature = "underline-color")]
#[cfg(feature = "underline_color")]
assert_eq!(
Style::from(
CellAttributes::default()

View File

@@ -2,7 +2,7 @@
//! It is used in the integration tests to verify the correctness of the library.
use std::{
fmt::{self, Write},
fmt::{Display, Write},
io,
};
@@ -105,16 +105,16 @@ impl TestBackend {
}
}
impl fmt::Display for TestBackend {
impl Display for TestBackend {
/// Formats the `TestBackend` for display by calling the `buffer_view` function
/// on its internal buffer.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", buffer_view(&self.buffer))
}
}
impl Backend for TestBackend {
fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
where
I: Iterator<Item = (u16, u16, &'a Cell)>,
{
@@ -125,26 +125,26 @@ impl Backend for TestBackend {
Ok(())
}
fn hide_cursor(&mut self) -> io::Result<()> {
fn hide_cursor(&mut self) -> Result<(), io::Error> {
self.cursor = false;
Ok(())
}
fn show_cursor(&mut self) -> io::Result<()> {
fn show_cursor(&mut self) -> Result<(), io::Error> {
self.cursor = true;
Ok(())
}
fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
fn get_cursor(&mut self) -> Result<(u16, u16), io::Error> {
Ok(self.pos)
}
fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error> {
self.pos = (x, y);
Ok(())
}
fn clear(&mut self) -> io::Result<()> {
fn clear(&mut self) -> Result<(), io::Error> {
self.buffer.reset();
Ok(())
}
@@ -214,11 +214,11 @@ impl Backend for TestBackend {
Ok(())
}
fn size(&self) -> io::Result<Rect> {
fn size(&self) -> Result<Rect, io::Error> {
Ok(Rect::new(0, 0, self.width, self.height))
}
fn window_size(&mut self) -> io::Result<WindowSize> {
fn window_size(&mut self) -> Result<WindowSize, io::Error> {
// Some arbitrary window pixel size, probably doesn't need much testing.
static WINDOW_PIXEL_SIZE: Size = Size {
width: 640,
@@ -230,7 +230,7 @@ impl Backend for TestBackend {
})
}
fn flush(&mut self) -> io::Result<()> {
fn flush(&mut self) -> Result<(), io::Error> {
Ok(())
}
}

View File

@@ -1,4 +1,7 @@
use std::fmt;
use std::{
cmp::min,
fmt::{Debug, Formatter, Result},
};
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
@@ -52,21 +55,22 @@ pub struct Buffer {
impl Buffer {
/// Returns a Buffer with all cells set to the default one
#[must_use]
pub fn empty(area: Rect) -> Self {
Self::filled(area, &Cell::default())
let cell = Cell::default();
Self::filled(area, &cell)
}
/// Returns a Buffer with all cells initialized with the attributes of the given Cell
#[must_use]
pub fn filled(area: Rect, cell: &Cell) -> Self {
let size = area.area() as usize;
let content = vec![cell.clone(); size];
let mut content = Vec::with_capacity(size);
for _ in 0..size {
content.push(cell.clone());
}
Self { area, content }
}
/// Returns a Buffer containing the given lines
#[must_use]
pub fn with_lines<'a, Iter>(lines: Iter) -> Self
where
Iter: IntoIterator,
@@ -93,14 +97,12 @@ impl Buffer {
}
/// Returns a reference to Cell at the given coordinates
#[track_caller]
pub fn get(&self, x: u16, y: u16) -> &Cell {
let i = self.index_of(x, y);
&self.content[i]
}
/// Returns a mutable reference to Cell at the given coordinates
#[track_caller]
pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
let i = self.index_of(x, y);
&mut self.content[i]
@@ -132,7 +134,6 @@ impl Buffer {
/// // starts at (200, 100).
/// buffer.index_of(0, 0); // Panics
/// ```
#[track_caller]
pub fn index_of(&self, x: u16, y: u16) -> usize {
debug_assert!(
x >= self.area.left()
@@ -183,56 +184,65 @@ impl Buffer {
}
/// Print a string, starting at the position (x, y)
///
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
/// your own type that implements [`Into<Style>`]).
pub fn set_string<T, S>(&mut self, x: u16, y: u16, string: T, style: S)
where
T: AsRef<str>,
S: Into<Style>,
{
self.set_stringn(x, y, string, usize::MAX, style);
self.set_stringn(x, y, string, usize::MAX, style.into());
}
/// Print at most the first n characters of a string if enough space is available
/// until the end of the line.
/// until the end of the line
///
/// Use [`Buffer::set_string`] when the maximum amount of characters can be printed.
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
/// your own type that implements [`Into<Style>`]).
pub fn set_stringn<T, S>(
&mut self,
mut x: u16,
x: u16,
y: u16,
string: T,
max_width: usize,
width: usize,
style: S,
) -> (u16, u16)
where
T: AsRef<str>,
S: Into<Style>,
{
let max_width = max_width.try_into().unwrap_or(u16::MAX);
let mut remaining_width = self.area.right().saturating_sub(x).min(max_width);
let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true)
.map(|symbol| (symbol, symbol.width() as u16))
.filter(|(_symbol, width)| *width > 0)
.map_while(|(symbol, width)| {
remaining_width = remaining_width.checked_sub(width)?;
Some((symbol, width))
});
let style = style.into();
for (symbol, width) in graphemes {
self.get_mut(x, y).set_symbol(symbol).set_style(style);
let next_symbol = x + width;
x += 1;
// Reset following cells if multi-width (they would be hidden by the grapheme),
while x < next_symbol {
self.get_mut(x, y).reset();
x += 1;
let mut index = self.index_of(x, y);
let mut x_offset = x as usize;
let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true);
let max_offset = min(self.area.right() as usize, width.saturating_add(x as usize));
for s in graphemes {
let width = s.width();
if width == 0 {
continue;
}
// `x_offset + width > max_offset` could be integer overflow on 32-bit machines if we
// change dimensions to usize or u32 and someone resizes the terminal to 1x2^32.
if width > max_offset.saturating_sub(x_offset) {
break;
}
self.content[index].set_symbol(s);
self.content[index].set_style(style);
// Reset following cells if multi-width (they would be hidden by the grapheme),
for i in index + 1..index + width {
self.content[i].reset();
}
index += width;
x_offset += width;
}
(x, y)
(x_offset as u16, y)
}
/// Print a line, starting at the position (x, y)
pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, max_width: u16) -> (u16, u16) {
let mut remaining_width = max_width;
pub fn set_line(&mut self, x: u16, y: u16, line: &Line<'_>, width: u16) -> (u16, u16) {
let mut remaining_width = width;
let mut x = x;
for span in line {
if remaining_width == 0 {
@@ -253,8 +263,8 @@ impl Buffer {
}
/// Print a span, starting at the position (x, y)
pub fn set_span(&mut self, x: u16, y: u16, span: &Span<'_>, max_width: u16) -> (u16, u16) {
self.set_stringn(x, y, &span.content, max_width as usize, span.style)
pub fn set_span(&mut self, x: u16, y: u16, span: &Span<'_>, width: u16) -> (u16, u16) {
self.set_stringn(x, y, span.content.as_ref(), width as usize, span.style)
}
/// Set the style of all cells in the given area.
@@ -293,7 +303,8 @@ impl Buffer {
/// Merge an other buffer into this one
pub fn merge(&mut self, other: &Self) {
let area = self.area.union(other.area);
self.content.resize(area.area() as usize, Cell::default());
let cell = Cell::default();
self.content.resize(area.area() as usize, cell.clone());
// Move original content to the appropriate space
let size = self.area.area() as usize;
@@ -303,7 +314,7 @@ impl Buffer {
let k = ((y - area.y) * area.width + x - area.x) as usize;
if i != k {
self.content[k] = self.content[i].clone();
self.content[i] = Cell::default();
self.content[i] = cell.clone();
}
}
@@ -372,7 +383,7 @@ impl Buffer {
}
}
impl fmt::Debug for Buffer {
impl Debug for Buffer {
/// Writes a debug representation of the buffer to the given formatter.
///
/// The format is like a pretty printed struct, with the following fields:
@@ -380,7 +391,7 @@ impl fmt::Debug for Buffer {
/// * `content`: displayed as a list of strings representing the content of the buffer
/// * `styles`: displayed as a list of: `{ x: 1, y: 2, fg: Color::Red, bg: Color::Blue,
/// modifier: Modifier::BOLD }` only showing a value when there is a change in style.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.write_fmt(format_args!(
"Buffer {{\n area: {:?},\n content: [\n",
&self.area

View File

@@ -1,4 +1,4 @@
use std::fmt;
use std::fmt::{self, Display};
use itertools::Itertools;
use strum::EnumIs;
@@ -362,7 +362,7 @@ impl Default for Constraint {
}
}
impl fmt::Display for Constraint {
impl Display for Constraint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Percentage(p) => write!(f, "Percentage({p})"),

View File

@@ -1,4 +1,4 @@
use std::fmt;
use std::fmt::{self, Display};
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
pub struct Margin {
@@ -15,7 +15,7 @@ impl Margin {
}
}
impl fmt::Display for Margin {
impl Display for Margin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}x{}", self.horizontal, self.vertical)
}

View File

@@ -40,6 +40,16 @@ pub struct Offset {
pub y: i32,
}
impl<X: Into<i32>, Y: Into<i32>> From<(X, Y)> for Offset {
/// Creates a new `Offset` from a tuple of (x, y).
fn from((x, y): (X, Y)) -> Self {
Self {
x: x.into(),
y: y.into(),
}
}
}
impl fmt::Display for Rect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}x{}+{}+{}", self.width, self.height, self.x, self.y)
@@ -135,9 +145,20 @@ impl Rect {
/// - Positive `x` moves the whole `Rect` to the right, negative to the left.
/// - Positive `y` moves the whole `Rect` to the bottom, negative to the top.
///
/// See [`Offset`] for details.
/// # Examples
///
/// ```rust
/// # use ratatui::{prelude::*, layout::Offset};
/// let rect = Rect::new(1, 2, 3, 4);
/// let rect = rect.offset(Offset { x: 10, y: 20 });
/// assert_eq!(rect, Rect::new(11, 22, 3, 4));
///
/// // offset can also be called with a tuple of (x, y)
/// let rect = rect.offset((10, 20));
/// ```
#[must_use = "method returns the modified value"]
pub fn offset(self, offset: Offset) -> Self {
pub fn offset<T: Into<Offset>>(self, offset: T) -> Self {
let offset = offset.into();
Self {
x: i32::from(self.x)
.saturating_add(offset.x)
@@ -423,6 +444,11 @@ mod tests {
);
}
#[test]
fn offset_from_tuple() {
assert_eq!(Rect::new(1, 2, 3, 4).offset((5, 6)), Rect::new(6, 8, 3, 4));
}
#[test]
fn union() {
assert_eq!(

View File

@@ -139,7 +139,8 @@
//!
//! fn ui(frame: &mut Frame) {
//! frame.render_widget(
//! Paragraph::new("Hello World!").block(Block::bordered().title("Greeting")),
//! Paragraph::new("Hello World!")
//! .block(Block::default().title("Greeting").borders(Borders::ALL)),
//! frame.size(),
//! );
//! }
@@ -183,8 +184,14 @@
//! [Constraint::Percentage(50), Constraint::Percentage(50)],
//! )
//! .split(main_layout[1]);
//! frame.render_widget(Block::bordered().title("Left"), inner_layout[0]);
//! frame.render_widget(Block::bordered().title("Right"), inner_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],
//! );
//! }
//! ```
//!

View File

@@ -68,14 +68,14 @@
//! [`prelude`]: crate::prelude
//! [`Span`]: crate::text::Span
use std::fmt;
use std::fmt::{self, Debug};
use bitflags::bitflags;
mod color;
mod stylize;
pub use color::{Color, ParseColorError};
pub use color::Color;
pub use stylize::{Styled, Stylize};
pub mod palette;
@@ -119,7 +119,7 @@ impl fmt::Debug for Modifier {
if self.is_empty() {
return write!(f, "NONE");
}
write!(f, "{}", self.0)
fmt::Debug::fmt(&self.0, f)
}
}
@@ -552,26 +552,32 @@ impl From<(Color, Color, Modifier, Modifier)> for Style {
mod tests {
use super::*;
fn styles() -> Vec<Style> {
vec![
Style::default(),
Style::default().fg(Color::Yellow),
Style::default().bg(Color::Yellow),
Style::default().add_modifier(Modifier::BOLD),
Style::default().remove_modifier(Modifier::BOLD),
Style::default().add_modifier(Modifier::ITALIC),
Style::default().remove_modifier(Modifier::ITALIC),
Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD),
Style::default().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
]
}
#[test]
fn combined_patch_gives_same_result_as_individual_patch() {
let styles = [
Style::new(),
Style::new().fg(Color::Yellow),
Style::new().bg(Color::Yellow),
Style::new().add_modifier(Modifier::BOLD),
Style::new().remove_modifier(Modifier::BOLD),
Style::new().add_modifier(Modifier::ITALIC),
Style::new().remove_modifier(Modifier::ITALIC),
Style::new().add_modifier(Modifier::ITALIC | Modifier::BOLD),
Style::new().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
];
let styles = styles();
for &a in &styles {
for &b in &styles {
for &c in &styles {
for &d in &styles {
let combined = a.patch(b.patch(c.patch(d)));
assert_eq!(
Style::new().patch(a).patch(b).patch(c).patch(d),
Style::new().patch(a.patch(b.patch(c.patch(d))))
Style::default().patch(a).patch(b).patch(c).patch(d),
Style::default().patch(combined)
);
}
}
@@ -583,7 +589,7 @@ mod tests {
fn combine_individual_modifiers() {
use crate::{buffer::Buffer, layout::Rect};
let mods = [
let mods = vec![
Modifier::BOLD,
Modifier::DIM,
Modifier::ITALIC,
@@ -597,12 +603,14 @@ mod tests {
let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
for m in mods {
for m in &mods {
buffer.get_mut(0, 0).set_style(Style::reset());
buffer.get_mut(0, 0).set_style(Style::new().add_modifier(m));
buffer
.get_mut(0, 0)
.set_style(Style::default().add_modifier(*m));
let style = buffer.get(0, 0).style();
assert!(style.add_modifier.contains(m));
assert!(!style.sub_modifier.contains(m));
assert!(style.add_modifier.contains(*m));
assert!(!style.sub_modifier.contains(*m));
}
}

View File

@@ -1,6 +1,9 @@
#![allow(clippy::unreadable_literal)]
use std::{fmt, str::FromStr};
use std::{
fmt::{self, Debug, Display},
str::FromStr,
};
/// ANSI Color
///
@@ -63,6 +66,7 @@ use std::{fmt, str::FromStr};
///
/// [ANSI color table]: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum Color {
/// Resets the foreground or background color
#[default]
@@ -137,102 +141,14 @@ impl Color {
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Color {
/// This utilises the [`fmt::Display`] implementation for serialization.
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Color {
/// This is used to deserialize a value into Color via serde.
///
/// This implementation uses the `FromStr` trait to deserialize strings, so named colours, RGB,
/// and indexed values are able to be deserialized. In addition, values that were produced by
/// the the older serialization implementation of Color are also able to be deserialized.
///
/// Prior to v0.26.0, Ratatui would be serialized using a map for indexed and RGB values, for
/// examples in json `{"Indexed": 10}` and `{"Rgb": [255, 0, 255]}` respectively. Now they are
/// serialized using the string representation of the index and the RGB hex value, for example
/// in json it would now be `"10"` and `"#FF00FF"` respectively.
///
/// See the [`Color`] documentation for more information on color names.
///
/// # Examples
///
/// ```
/// use ratatui::prelude::*;
///
/// #[derive(Debug, serde::Deserialize)]
/// struct Theme {
/// color: Color,
/// }
///
/// # fn get_theme() -> Result<(), serde_json::Error> {
/// let theme: Theme = serde_json::from_str(r#"{"color": "bright-white"}"#)?;
/// assert_eq!(theme.color, Color::White);
///
/// let theme: Theme = serde_json::from_str(r##"{"color": "#00FF00"}"##)?;
/// assert_eq!(theme.color, Color::Rgb(0, 255, 0));
///
/// let theme: Theme = serde_json::from_str(r#"{"color": "42"}"#)?;
/// assert_eq!(theme.color, Color::Indexed(42));
///
/// let err = serde_json::from_str::<Theme>(r#"{"color": "invalid"}"#).unwrap_err();
/// assert!(err.is_data());
/// assert_eq!(
/// err.to_string(),
/// "Failed to parse Colors at line 1 column 20"
/// );
///
/// // Deserializing from the previous serialization implementation
/// let theme: Theme = serde_json::from_str(r#"{"color": {"Rgb":[255,0,255]}}"#)?;
/// assert_eq!(theme.color, Color::Rgb(255, 0, 255));
///
/// let theme: Theme = serde_json::from_str(r#"{"color": {"Indexed":10}}"#)?;
/// assert_eq!(theme.color, Color::Indexed(10));
/// # Ok(())
/// # }
/// ```
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
/// Colors are currently serialized with the `Display` implementation, so
/// RGB values are serialized via hex, for example "#FFFFFF".
///
/// Previously they were serialized using serde derive, which encoded
/// RGB values as a map, for example { "rgb": [255, 255, 255] }.
///
/// The deserialization implementation utilises a `Helper` struct
/// to be able to support both formats for backwards compatibility.
#[derive(serde::Deserialize)]
enum ColorWrapper {
Rgb(u8, u8, u8),
Indexed(u8),
}
#[derive(serde::Deserialize)]
#[serde(untagged)]
enum ColorFormat {
V2(String),
V1(ColorWrapper),
}
let multi_type = ColorFormat::deserialize(deserializer)
.map_err(|err| serde::de::Error::custom(format!("Failed to parse Colors: {err}")))?;
match multi_type {
ColorFormat::V2(s) => FromStr::from_str(&s).map_err(serde::de::Error::custom),
ColorFormat::V1(color_wrapper) => match color_wrapper {
ColorWrapper::Rgb(red, green, blue) => Ok(Self::Rgb(red, green, blue)),
ColorWrapper::Indexed(index) => Ok(Self::Indexed(index)),
},
}
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(serde::de::Error::custom)
}
}
@@ -240,8 +156,8 @@ impl<'de> serde::Deserialize<'de> for Color {
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ParseColorError;
impl fmt::Display for ParseColorError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl std::fmt::Display for ParseColorError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Failed to parse Colors")
}
}
@@ -333,7 +249,7 @@ impl FromStr for Color {
}
}
impl fmt::Display for Color {
impl Display for Color {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Reset => write!(f, "Reset"),
@@ -663,42 +579,4 @@ mod tests {
Color::deserialize("#00000000".into_deserializer());
assert!(color.is_err());
}
#[cfg(feature = "serde")]
#[test]
fn serialize_then_deserialize() -> Result<(), serde_json::Error> {
let json_rgb = serde_json::to_string(&Color::Rgb(255, 0, 255))?;
assert_eq!(json_rgb, r##""#FF00FF""##);
assert_eq!(
serde_json::from_str::<Color>(&json_rgb)?,
Color::Rgb(255, 0, 255)
);
let json_white = serde_json::to_string(&Color::White)?;
assert_eq!(json_white, r#""White""#);
let json_indexed = serde_json::to_string(&Color::Indexed(10))?;
assert_eq!(json_indexed, r#""10""#);
assert_eq!(
serde_json::from_str::<Color>(&json_indexed)?,
Color::Indexed(10)
);
Ok(())
}
#[cfg(feature = "serde")]
#[test]
fn deserialize_with_previous_format() -> Result<(), serde_json::Error> {
assert_eq!(Color::White, serde_json::from_str::<Color>("\"White\"")?);
assert_eq!(
Color::Rgb(255, 0, 255),
serde_json::from_str::<Color>(r#"{"Rgb":[255,0,255]}"#)?
);
assert_eq!(
Color::Indexed(10),
serde_json::from_str::<Color>(r#"{"Indexed":10}"#)?
);
Ok(())
}
}

View File

@@ -133,7 +133,11 @@ macro_rules! modifier {
/// "world".green().on_yellow().not_bold(),
/// ]);
/// let paragraph = Paragraph::new(line).italic().underlined();
/// let block = Block::bordered().title("Title").on_white().bold();
/// let block = Block::default()
/// .title("Title")
/// .borders(Borders::ALL)
/// .on_white()
/// .bold();
/// ```
pub trait Stylize<'a, T>: Sized {
#[must_use = "`bg` returns the modified style without modifying the original"]

View File

@@ -65,7 +65,7 @@ impl Frame<'_> {
/// # let backend = TestBackend::new(5, 5);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// # let mut frame = terminal.get_frame();
/// let block = Block::new();
/// let block = Block::default();
/// let area = Rect::new(0, 0, 5, 5);
/// frame.render_widget(block, area);
/// ```
@@ -87,7 +87,7 @@ impl Frame<'_> {
/// # let backend = TestBackend::new(5, 5);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// # let mut frame = terminal.get_frame();
/// let block = Block::new();
/// let block = Block::default();
/// let area = Rect::new(0, 0, 5, 5);
/// frame.render_widget_ref(block, area);
/// ```

View File

@@ -25,20 +25,21 @@
//! // Converted to Line(vec![
//! // Span { content: Cow::Borrowed("My title"), style: Style { .. } }
//! // ])
//! let block = Block::new().title("My title");
//! let block = Block::default().title("My title");
//!
//! // A simple string with a unique style.
//! // Converted to Line(vec![
//! // Span { content: Cow::Borrowed("My title"), style: Style { fg: Some(Color::Yellow), .. }
//! // ])
//! let block = Block::new().title(Span::styled("My title", Style::default().fg(Color::Yellow)));
//! let block =
//! Block::default().title(Span::styled("My title", Style::default().fg(Color::Yellow)));
//!
//! // A string with multiple styles.
//! // Converted to Line(vec![
//! // Span { content: Cow::Borrowed("My"), style: Style { fg: Some(Color::Yellow), .. } },
//! // Span { content: Cow::Borrowed(" title"), .. }
//! // ])
//! let block = Block::new().title(vec![
//! let block = Block::default().title(vec![
//! Span::styled("My", Style::default().fg(Color::Yellow)),
//! Span::raw(" title"),
//! ]);

View File

@@ -1,5 +1,5 @@
#![deny(missing_docs)]
use std::{borrow::Cow, fmt};
use std::borrow::Cow;
use super::StyledGrapheme;
use crate::prelude::*;
@@ -453,39 +453,6 @@ impl<'a> Line<'a> {
self.spans.iter_mut()
}
/// Returns a line that's truncated corresponding to it's alignment and result width
#[must_use = "method returns the modified value"]
fn truncated(&'a self, result_width: u16) -> Self {
let mut truncated_line = Line::default();
let width = self.width() as u16;
let mut offset = match self.alignment {
Some(Alignment::Center) => (width.saturating_sub(result_width)) / 2,
Some(Alignment::Right) => width.saturating_sub(result_width),
_ => 0,
};
let mut x = 0;
for span in &self.spans {
let span_width = span.width() as u16;
if offset >= span_width {
offset -= span_width;
continue;
}
let mut new_span = span.clone();
let new_span_width = span_width - offset;
if x + new_span_width > result_width {
let span_end = (result_width - x + offset) as usize;
new_span.content = Cow::from(&span.content[offset as usize..span_end]);
truncated_line.spans.push(new_span);
break;
}
new_span.content = Cow::from(&span.content[offset as usize..]);
truncated_line.spans.push(new_span);
x += new_span_width;
offset = 0;
}
truncated_line
}
/// Adds a span to the line.
///
/// `span` can be any type that is convertible into a `Span`. For example, you can pass a
@@ -587,23 +554,17 @@ impl WidgetRef for Line<'_> {
let area = area.intersection(buf.area);
buf.set_style(area, self.style);
let width = self.width() as u16;
let mut x = area.left();
let line = if width > area.width {
self.truncated(area.width)
} else {
let offset = match self.alignment {
Some(Alignment::Center) => (area.width.saturating_sub(width)) / 2,
Some(Alignment::Right) => area.width.saturating_sub(width),
Some(Alignment::Left) | None => 0,
};
x = x.saturating_add(offset);
self.to_owned()
let offset = match self.alignment {
Some(Alignment::Center) => (area.width.saturating_sub(width)) / 2,
Some(Alignment::Right) => area.width.saturating_sub(width),
Some(Alignment::Left) | None => 0,
};
for span in &line.spans {
let mut x = area.left().saturating_add(offset);
for span in &self.spans {
let span_width = span.width() as u16;
let span_area = Rect {
x,
width: span_width.min(area.right().saturating_sub(x)),
width: span_width.min(area.right() - x),
..area
};
span.render(span_area, buf);
@@ -615,8 +576,8 @@ impl WidgetRef for Line<'_> {
}
}
impl fmt::Display for Line<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl std::fmt::Display for Line<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for span in &self.spans {
write!(f, "{span}")?;
}
@@ -643,7 +604,6 @@ mod tests {
use rstest::{fixture, rstest};
use super::*;
use crate::assert_buffer_eq;
#[fixture]
fn small_buf() -> Buffer {
@@ -887,53 +847,6 @@ mod tests {
assert_eq!(format!("{line_from_styled_span}"), "Hello, world!");
}
#[test]
fn render_truncates_left() {
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 1));
Line::from("Hello world")
.left_aligned()
.render(buf.area, &mut buf);
let expected = Buffer::with_lines(vec!["Hello"]);
assert_buffer_eq!(buf, expected);
}
#[test]
fn render_truncates_right() {
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 1));
Line::from("Hello world")
.right_aligned()
.render(buf.area, &mut buf);
let expected = Buffer::with_lines(vec!["world"]);
assert_buffer_eq!(buf, expected);
}
#[test]
fn render_truncates_center() {
let mut buf = Buffer::empty(Rect::new(0, 0, 5, 1));
Line::from("Hello world")
.centered()
.render(buf.area, &mut buf);
let expected = Buffer::with_lines(vec!["lo wo"]);
assert_buffer_eq!(buf, expected);
}
#[test]
fn truncate_line_with_multiple_spans() {
let line = Line::default().spans(vec!["foo", "bar"]);
assert_eq!(
line.right_aligned().truncated(4).to_string(),
String::from("obar")
);
}
#[test]
fn truncation_ignores_useless_spans() {
let line = Line::default().spans(vec!["foo", "bar"]);
assert_eq!(
line.right_aligned().truncated(3),
Line::default().spans(vec!["bar"])
);
}
#[test]
fn left_aligned() {

View File

@@ -1,4 +1,7 @@
use std::{borrow::Cow, fmt};
use std::{
borrow::Cow,
fmt::{self, Debug, Display},
};
use super::Text;
@@ -43,17 +46,17 @@ impl<'a> Masked<'a> {
}
}
impl fmt::Debug for Masked<'_> {
impl Debug for Masked<'_> {
/// Debug representation of a masked string is the underlying string
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.inner)
Display::fmt(&self.inner, f)
}
}
impl fmt::Display for Masked<'_> {
impl Display for Masked<'_> {
/// Display representation of a masked string is the masked string
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.value())
Display::fmt(&self.value(), f)
}
}

View File

@@ -1,4 +1,4 @@
use std::{borrow::Cow, fmt};
use std::{borrow::Cow, fmt::Debug};
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
@@ -395,8 +395,8 @@ impl WidgetRef for Span<'_> {
}
}
impl fmt::Display for Span<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl std::fmt::Display for Span<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.content)
}
}

View File

@@ -1,5 +1,5 @@
#![warn(missing_docs)]
use std::{borrow::Cow, fmt};
use std::borrow::Cow;
use itertools::{Itertools, Position};
@@ -580,8 +580,8 @@ where
}
}
impl fmt::Display for Text<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl std::fmt::Display for Text<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (position, line) in self.iter().with_position() {
if position == Position::Last {
write!(f, "{line}")?;

View File

@@ -45,7 +45,7 @@ pub use bar_group::BarGroup;
/// use ratatui::{prelude::*, widgets::*};
///
/// BarChart::default()
/// .block(Block::bordered().title("BarChart"))
/// .block(Block::default().title("BarChart").borders(Borders::ALL))
/// .bar_width(3)
/// .bar_gap(1)
/// .group_gap(3)
@@ -615,7 +615,10 @@ mod tests {
use itertools::iproduct;
use super::*;
use crate::{assert_buffer_eq, widgets::BorderType};
use crate::{
assert_buffer_eq,
widgets::{BorderType, Borders},
};
#[test]
fn default() {
@@ -643,9 +646,10 @@ mod tests {
#[test]
fn block() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 5));
let block = Block::bordered()
let block = Block::default()
.title("Block")
.border_type(BorderType::Double)
.title("Block");
.borders(Borders::ALL);
let widget = BarChart::default()
.data(&[("foo", 1), ("bar", 2)])
.block(block);

View File

@@ -41,12 +41,12 @@ pub use title::{Position, Title};
/// ```
/// use ratatui::{prelude::*, widgets::*};
///
/// Block::new()
/// .border_type(BorderType::Rounded)
/// Block::default()
/// .title("Block")
/// .borders(Borders::LEFT | Borders::RIGHT)
/// .border_style(Style::default().fg(Color::White))
/// .style(Style::default().bg(Color::Black))
/// .title("Block");
/// .border_type(BorderType::Rounded)
/// .style(Style::default().bg(Color::Black));
/// ```
///
/// You may also use multiple titles like in the following:
@@ -56,7 +56,7 @@ pub use title::{Position, Title};
/// widgets::{block::*, *},
/// };
///
/// Block::new()
/// Block::default()
/// .title("Title 1")
/// .title(Title::from("Title 2").position(Position::Bottom));
/// ```
@@ -173,11 +173,6 @@ impl<'a> Block<'a> {
}
/// Create a new block with [all borders](Borders::ALL) shown
///
/// ```
/// # use ratatui::widgets::{Block, Borders};
/// assert_eq!(Block::bordered(), Block::new().borders(Borders::ALL));
/// ```
pub const fn bordered() -> Self {
let mut block = Self::new();
block.borders = Borders::ALL;
@@ -224,7 +219,7 @@ impl<'a> Block<'a> {
/// widgets::{block::*, *},
/// };
///
/// Block::new()
/// Block::default()
/// .title("Title") // By default in the top left corner
/// .title(Title::from("Left").alignment(Alignment::Left)) // also on the left
/// .title(Title::from("Right").alignment(Alignment::Right))
@@ -330,12 +325,12 @@ impl<'a> Block<'a> {
/// widgets::{block::*, *},
/// };
///
/// Block::new()
/// .title_alignment(Alignment::Center)
/// Block::default()
/// // This title won't be aligned in the center
/// .title(Title::from("right").alignment(Alignment::Right))
/// .title("foo")
/// .title("bar");
/// .title("bar")
/// .title_alignment(Alignment::Center);
/// ```
#[must_use = "method moves the value of self and returns the modified value"]
pub const fn title_alignment(mut self, alignment: Alignment) -> Self {
@@ -357,12 +352,12 @@ impl<'a> Block<'a> {
/// widgets::{block::*, *},
/// };
///
/// Block::new()
/// .title_position(Position::Bottom)
/// Block::default()
/// // This title won't be aligned in the center
/// .title(Title::from("top").position(Position::Top))
/// .title("foo")
/// .title("bar");
/// .title("bar")
/// .title_position(Position::Bottom);
/// ```
#[must_use = "method moves the value of self and returns the modified value"]
pub const fn title_position(mut self, position: Position) -> Self {
@@ -382,7 +377,9 @@ impl<'a> Block<'a> {
/// This example shows a `Block` with blue borders.
/// ```
/// # use ratatui::{prelude::*, widgets::*};
/// Block::bordered().border_style(Style::new().blue());
/// Block::default()
/// .borders(Borders::ALL)
/// .border_style(Style::new().blue());
/// ```
#[must_use = "method moves the value of self and returns the modified value"]
pub fn border_style<S: Into<Style>>(mut self, style: S) -> Self {
@@ -412,13 +409,17 @@ impl<'a> Block<'a> {
///
/// # Examples
///
/// Simply show all borders.
/// ```
/// # use ratatui::{prelude::*, widgets::*};
/// Block::default().borders(Borders::ALL);
/// ```
///
/// Display left and right borders.
/// ```
/// # use ratatui::{prelude::*, widgets::*};
/// Block::new().borders(Borders::LEFT | Borders::RIGHT);
/// Block::default().borders(Borders::LEFT | Borders::RIGHT);
/// ```
///
/// To show all borders you can abbreviate this with [`Block::bordered`]
#[must_use = "method moves the value of self and returns the modified value"]
pub const fn borders(mut self, flag: Borders) -> Self {
self.borders = flag;
@@ -436,9 +437,10 @@ impl<'a> Block<'a> {
///
/// ```
/// # use ratatui::{prelude::*, widgets::*};
/// Block::bordered()
/// .border_type(BorderType::Rounded)
/// .title("Block");
/// Block::default()
/// .title("Block")
/// .borders(Borders::ALL)
/// .border_type(BorderType::Rounded);
/// // Renders
/// // ╭Block╮
/// // │ │
@@ -458,7 +460,7 @@ impl<'a> Block<'a> {
///
/// ```
/// # use ratatui::{prelude::*, widgets::*};
/// Block::bordered().border_set(symbols::border::DOUBLE).title("Block");
/// Block::default().title("Block").borders(Borders::ALL).border_set(symbols::border::DOUBLE);
/// // Renders
/// // ╔Block╗
/// // ║ ║
@@ -477,8 +479,8 @@ impl<'a> Block<'a> {
/// ```
/// # use ratatui::{prelude::*, widgets::*};
/// # fn render_nested_block(frame: &mut Frame) {
/// let outer_block = Block::bordered().title("Outer");
/// let inner_block = Block::bordered().title("Inner");
/// let outer_block = Block::default().title("Outer").borders(Borders::ALL);
/// let inner_block = Block::default().title("Inner").borders(Borders::ALL);
///
/// let outer_area = frame.size();
/// let inner_area = outer_block.inner(outer_area);
@@ -499,14 +501,14 @@ impl<'a> Block<'a> {
inner.x = inner.x.saturating_add(1).min(inner.right());
inner.width = inner.width.saturating_sub(1);
}
if self.borders.intersects(Borders::TOP) || self.has_title_at_position(Position::Top) {
if self.borders.intersects(Borders::TOP) || self.have_title_at_position(Position::Top) {
inner.y = inner.y.saturating_add(1).min(inner.bottom());
inner.height = inner.height.saturating_sub(1);
}
if self.borders.intersects(Borders::RIGHT) {
inner.width = inner.width.saturating_sub(1);
}
if self.borders.intersects(Borders::BOTTOM) || self.has_title_at_position(Position::Bottom)
if self.borders.intersects(Borders::BOTTOM) || self.have_title_at_position(Position::Bottom)
{
inner.height = inner.height.saturating_sub(1);
}
@@ -524,7 +526,7 @@ impl<'a> Block<'a> {
inner
}
fn has_title_at_position(&self, position: Position) -> bool {
fn have_title_at_position(&self, position: Position) -> bool {
self.titles
.iter()
.any(|title| title.position.unwrap_or(self.titles_position) == position)
@@ -539,7 +541,9 @@ impl<'a> Block<'a> {
/// This renders a `Block` with no padding (the default).
/// ```
/// # use ratatui::{prelude::*, widgets::*};
/// Block::bordered().padding(Padding::zero());
/// Block::default()
/// .borders(Borders::ALL)
/// .padding(Padding::zero());
/// // Renders
/// // ┌───────┐
/// // │content│
@@ -550,7 +554,9 @@ impl<'a> Block<'a> {
/// Notice the two spaces before and after the content.
/// ```
/// # use ratatui::{prelude::*, widgets::*};
/// Block::bordered().padding(Padding::horizontal(2));
/// Block::default()
/// .borders(Borders::ALL)
/// .padding(Padding::horizontal(2));
/// // Renders
/// // ┌───────────┐
/// // │ content │
@@ -876,33 +882,33 @@ mod tests {
fn inner_takes_into_account_the_borders() {
// No borders
assert_eq!(
Block::new().inner(Rect::default()),
Block::default().inner(Rect::default()),
Rect::new(0, 0, 0, 0),
"no borders, width=0, height=0"
);
assert_eq!(
Block::new().inner(Rect::new(0, 0, 1, 1)),
Block::default().inner(Rect::new(0, 0, 1, 1)),
Rect::new(0, 0, 1, 1),
"no borders, width=1, height=1"
);
// Left border
assert_eq!(
Block::new()
Block::default()
.borders(Borders::LEFT)
.inner(Rect::new(0, 0, 0, 1)),
Rect::new(0, 0, 0, 1),
"left, width=0"
);
assert_eq!(
Block::new()
Block::default()
.borders(Borders::LEFT)
.inner(Rect::new(0, 0, 1, 1)),
Rect::new(1, 0, 0, 1),
"left, width=1"
);
assert_eq!(
Block::new()
Block::default()
.borders(Borders::LEFT)
.inner(Rect::new(0, 0, 2, 1)),
Rect::new(1, 0, 1, 1),
@@ -911,21 +917,21 @@ mod tests {
// Top border
assert_eq!(
Block::new()
Block::default()
.borders(Borders::TOP)
.inner(Rect::new(0, 0, 1, 0)),
Rect::new(0, 0, 1, 0),
"top, height=0"
);
assert_eq!(
Block::new()
Block::default()
.borders(Borders::TOP)
.inner(Rect::new(0, 0, 1, 1)),
Rect::new(0, 1, 1, 0),
"top, height=1"
);
assert_eq!(
Block::new()
Block::default()
.borders(Borders::TOP)
.inner(Rect::new(0, 0, 1, 2)),
Rect::new(0, 1, 1, 1),
@@ -934,21 +940,21 @@ mod tests {
// Right border
assert_eq!(
Block::new()
Block::default()
.borders(Borders::RIGHT)
.inner(Rect::new(0, 0, 0, 1)),
Rect::new(0, 0, 0, 1),
"right, width=0"
);
assert_eq!(
Block::new()
Block::default()
.borders(Borders::RIGHT)
.inner(Rect::new(0, 0, 1, 1)),
Rect::new(0, 0, 0, 1),
"right, width=1"
);
assert_eq!(
Block::new()
Block::default()
.borders(Borders::RIGHT)
.inner(Rect::new(0, 0, 2, 1)),
Rect::new(0, 0, 1, 1),
@@ -957,21 +963,21 @@ mod tests {
// Bottom border
assert_eq!(
Block::new()
Block::default()
.borders(Borders::BOTTOM)
.inner(Rect::new(0, 0, 1, 0)),
Rect::new(0, 0, 1, 0),
"bottom, height=0"
);
assert_eq!(
Block::new()
Block::default()
.borders(Borders::BOTTOM)
.inner(Rect::new(0, 0, 1, 1)),
Rect::new(0, 0, 1, 0),
"bottom, height=1"
);
assert_eq!(
Block::new()
Block::default()
.borders(Borders::BOTTOM)
.inner(Rect::new(0, 0, 1, 2)),
Rect::new(0, 0, 1, 1),
@@ -980,22 +986,30 @@ mod tests {
// All borders
assert_eq!(
Block::bordered().inner(Rect::default()),
Block::default()
.borders(Borders::ALL)
.inner(Rect::default()),
Rect::new(0, 0, 0, 0),
"all borders, width=0, height=0"
);
assert_eq!(
Block::bordered().inner(Rect::new(0, 0, 1, 1)),
Block::default()
.borders(Borders::ALL)
.inner(Rect::new(0, 0, 1, 1)),
Rect::new(1, 1, 0, 0),
"all borders, width=1, height=1"
);
assert_eq!(
Block::bordered().inner(Rect::new(0, 0, 2, 2)),
Block::default()
.borders(Borders::ALL)
.inner(Rect::new(0, 0, 2, 2)),
Rect::new(1, 1, 0, 0),
"all borders, width=2, height=2"
);
assert_eq!(
Block::bordered().inner(Rect::new(0, 0, 3, 3)),
Block::default()
.borders(Borders::ALL)
.inner(Rect::new(0, 0, 3, 3)),
Rect::new(1, 1, 1, 1),
"all borders, width=3, height=3"
);
@@ -1004,17 +1018,17 @@ mod tests {
#[test]
fn inner_takes_into_account_the_title() {
assert_eq!(
Block::new().title("Test").inner(Rect::new(0, 0, 0, 1)),
Block::default().title("Test").inner(Rect::new(0, 0, 0, 1)),
Rect::new(0, 1, 0, 0),
);
assert_eq!(
Block::new()
Block::default()
.title(Title::from("Test").alignment(Alignment::Center))
.inner(Rect::new(0, 0, 0, 1)),
Rect::new(0, 1, 0, 0),
);
assert_eq!(
Block::new()
Block::default()
.title(Title::from("Test").alignment(Alignment::Right))
.inner(Rect::new(0, 0, 0, 1)),
Rect::new(0, 1, 0, 0),
@@ -1025,72 +1039,72 @@ mod tests {
fn inner_takes_into_account_border_and_title() {
let test_rect = Rect::new(0, 0, 0, 2);
let top_top = Block::new()
.borders(Borders::TOP)
.title(Title::from("Test").position(Position::Top));
let top_top = Block::default()
.title(Title::from("Test").position(Position::Top))
.borders(Borders::TOP);
assert_eq!(top_top.inner(test_rect), Rect::new(0, 1, 0, 1));
let top_bot = Block::new()
.borders(Borders::BOTTOM)
.title(Title::from("Test").position(Position::Top));
let top_bot = Block::default()
.title(Title::from("Test").position(Position::Top))
.borders(Borders::BOTTOM);
assert_eq!(top_bot.inner(test_rect), Rect::new(0, 1, 0, 0));
let bot_top = Block::new()
.borders(Borders::TOP)
.title(Title::from("Test").position(Position::Bottom));
let bot_top = Block::default()
.title(Title::from("Test").position(Position::Bottom))
.borders(Borders::TOP);
assert_eq!(bot_top.inner(test_rect), Rect::new(0, 1, 0, 0));
let bot_bot = Block::new()
.borders(Borders::BOTTOM)
.title(Title::from("Test").position(Position::Bottom));
let bot_bot = Block::default()
.title(Title::from("Test").position(Position::Bottom))
.borders(Borders::BOTTOM);
assert_eq!(bot_bot.inner(test_rect), Rect::new(0, 0, 0, 1));
}
#[test]
fn has_title_at_position_takes_into_account_all_positioning_declarations() {
let block = Block::new();
assert!(!block.has_title_at_position(Position::Top));
assert!(!block.has_title_at_position(Position::Bottom));
fn have_title_at_position_takes_into_account_all_positioning_declarations() {
let block = Block::default();
assert!(!block.have_title_at_position(Position::Top));
assert!(!block.have_title_at_position(Position::Bottom));
let block = Block::new().title(Title::from("Test").position(Position::Top));
assert!(block.has_title_at_position(Position::Top));
assert!(!block.has_title_at_position(Position::Bottom));
let block = Block::default().title(Title::from("Test").position(Position::Top));
assert!(block.have_title_at_position(Position::Top));
assert!(!block.have_title_at_position(Position::Bottom));
let block = Block::new().title(Title::from("Test").position(Position::Bottom));
assert!(!block.has_title_at_position(Position::Top));
assert!(block.has_title_at_position(Position::Bottom));
let block = Block::default().title(Title::from("Test").position(Position::Bottom));
assert!(!block.have_title_at_position(Position::Top));
assert!(block.have_title_at_position(Position::Bottom));
let block = Block::new()
let block = Block::default()
.title(Title::from("Test").position(Position::Top))
.title_position(Position::Bottom);
assert!(block.has_title_at_position(Position::Top));
assert!(!block.has_title_at_position(Position::Bottom));
assert!(block.have_title_at_position(Position::Top));
assert!(!block.have_title_at_position(Position::Bottom));
let block = Block::new()
let block = Block::default()
.title(Title::from("Test").position(Position::Bottom))
.title_position(Position::Top);
assert!(!block.has_title_at_position(Position::Top));
assert!(block.has_title_at_position(Position::Bottom));
assert!(!block.have_title_at_position(Position::Top));
assert!(block.have_title_at_position(Position::Bottom));
let block = Block::new()
let block = Block::default()
.title(Title::from("Test").position(Position::Top))
.title(Title::from("Test").position(Position::Bottom));
assert!(block.has_title_at_position(Position::Top));
assert!(block.has_title_at_position(Position::Bottom));
assert!(block.have_title_at_position(Position::Top));
assert!(block.have_title_at_position(Position::Bottom));
let block = Block::new()
let block = Block::default()
.title(Title::from("Test").position(Position::Top))
.title(Title::from("Test"))
.title_position(Position::Bottom);
assert!(block.has_title_at_position(Position::Top));
assert!(block.has_title_at_position(Position::Bottom));
assert!(block.have_title_at_position(Position::Top));
assert!(block.have_title_at_position(Position::Bottom));
let block = Block::new()
let block = Block::default()
.title(Title::from("Test"))
.title(Title::from("Test").position(Position::Bottom))
.title_position(Position::Top);
assert!(block.has_title_at_position(Position::Top));
assert!(block.has_title_at_position(Position::Bottom));
assert!(block.have_title_at_position(Position::Top));
assert!(block.have_title_at_position(Position::Bottom));
}
#[test]
@@ -1120,49 +1134,50 @@ mod tests {
const fn block_can_be_const() {
const _DEFAULT_STYLE: Style = Style::new();
const _DEFAULT_PADDING: Padding = Padding::uniform(1);
const _DEFAULT_BLOCK: Block = Block::bordered()
const _DEFAULT_BLOCK: Block = Block::new()
// the following methods are no longer const because they use Into<Style>
// .style(_DEFAULT_STYLE) // no longer const
// .border_style(_DEFAULT_STYLE) // no longer const
// .title_style(_DEFAULT_STYLE) // no longer const
.title_alignment(Alignment::Left)
.title_position(Position::Top)
.borders(Borders::ALL)
.padding(_DEFAULT_PADDING);
}
/// Ensure Style from/into works the way a user would use it.
/// This test ensures that we have some coverage on the [`Style::from()`] implementations
#[test]
fn style_into_works_from_user_view() {
fn block_style() {
// nominal style
let block = Block::new().style(Style::new().red());
let block = Block::default().style(Style::new().red());
assert_eq!(block.style, Style::new().red());
// auto-convert from Color
let block = Block::new().style(Color::Red);
let block = Block::default().style(Color::Red);
assert_eq!(block.style, Style::new().red());
// auto-convert from (Color, Color)
let block = Block::new().style((Color::Red, Color::Blue));
let block = Block::default().style((Color::Red, Color::Blue));
assert_eq!(block.style, Style::new().red().on_blue());
// auto-convert from Modifier
let block = Block::new().style(Modifier::BOLD | Modifier::ITALIC);
let block = Block::default().style(Modifier::BOLD | Modifier::ITALIC);
assert_eq!(block.style, Style::new().bold().italic());
// auto-convert from (Modifier, Modifier)
let block = Block::new().style((Modifier::BOLD | Modifier::ITALIC, Modifier::DIM));
let block = Block::default().style((Modifier::BOLD | Modifier::ITALIC, Modifier::DIM));
assert_eq!(block.style, Style::new().bold().italic().not_dim());
// auto-convert from (Color, Modifier)
let block = Block::new().style((Color::Red, Modifier::BOLD));
let block = Block::default().style((Color::Red, Modifier::BOLD));
assert_eq!(block.style, Style::new().red().bold());
// auto-convert from (Color, Color, Modifier)
let block = Block::new().style((Color::Red, Color::Blue, Modifier::BOLD));
let block = Block::default().style((Color::Red, Color::Blue, Modifier::BOLD));
assert_eq!(block.style, Style::new().red().on_blue().bold());
// auto-convert from (Color, Color, Modifier, Modifier)
let block = Block::new().style((
let block = Block::default().style((
Color::Red,
Color::Blue,
Modifier::BOLD | Modifier::ITALIC,
@@ -1176,7 +1191,7 @@ mod tests {
#[test]
fn can_be_stylized() {
let block = Block::new().black().on_white().bold().not_dim();
let block = Block::default().black().on_white().bold().not_dim();
assert_eq!(
block.style,
Style::default()
@@ -1240,9 +1255,9 @@ mod tests {
];
for (alignment, expected) in tests {
let mut buffer = Buffer::empty(Rect::new(0, 0, 8, 1));
Block::new()
.title_alignment(alignment)
Block::default()
.title("test")
.title_alignment(alignment)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(buffer, Buffer::with_lines(vec![expected]));
}
@@ -1257,9 +1272,9 @@ mod tests {
];
for (block_title_alignment, alignment, expected) in tests {
let mut buffer = Buffer::empty(Rect::new(0, 0, 8, 1));
Block::new()
.title_alignment(block_title_alignment)
Block::default()
.title(Title::from("test").alignment(alignment))
.title_alignment(block_title_alignment)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(buffer, Buffer::with_lines(vec![expected]));
}
@@ -1269,9 +1284,9 @@ mod tests {
#[test]
fn render_right_aligned_empty_title() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
Block::new()
.title_alignment(Alignment::Right)
Block::default()
.title("")
.title_alignment(Alignment::Right)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(
buffer,
@@ -1286,9 +1301,9 @@ mod tests {
#[test]
fn title_position() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 4, 2));
Block::new()
.title_position(Position::Bottom)
Block::default()
.title("test")
.title_position(Position::Bottom)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(buffer, Buffer::with_lines(vec![" ", "test"]));
}
@@ -1297,9 +1312,9 @@ mod tests {
fn title_content_style() {
for alignment in [Alignment::Left, Alignment::Center, Alignment::Right] {
let mut buffer = Buffer::empty(Rect::new(0, 0, 4, 1));
Block::new()
.title_alignment(alignment)
Block::default()
.title("test".yellow())
.title_alignment(alignment)
.render(buffer.area, &mut buffer);
let mut expected_buffer = Buffer::with_lines(vec!["test"]);
@@ -1313,10 +1328,10 @@ mod tests {
fn block_title_style() {
for alignment in [Alignment::Left, Alignment::Center, Alignment::Right] {
let mut buffer = Buffer::empty(Rect::new(0, 0, 4, 1));
Block::new()
.title_alignment(alignment)
.title_style(Style::new().yellow())
Block::default()
.title("test")
.title_style(Style::new().yellow())
.title_alignment(alignment)
.render(buffer.area, &mut buffer);
let mut expected_buffer = Buffer::with_lines(vec!["test"]);
@@ -1330,10 +1345,10 @@ mod tests {
fn title_style_overrides_block_title_style() {
for alignment in [Alignment::Left, Alignment::Center, Alignment::Right] {
let mut buffer = Buffer::empty(Rect::new(0, 0, 4, 1));
Block::new()
.title_alignment(alignment)
.title_style(Style::new().green().on_red())
Block::default()
.title("test".yellow())
.title_style(Style::new().green().on_red())
.title_alignment(alignment)
.render(buffer.area, &mut buffer);
let mut expected_buffer = Buffer::with_lines(vec!["test"]);
@@ -1346,8 +1361,9 @@ mod tests {
#[test]
fn title_border_style() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
Block::bordered()
Block::default()
.title("test")
.borders(Borders::ALL)
.border_style(Style::new().yellow())
.render(buffer.area, &mut buffer);
@@ -1382,7 +1398,8 @@ mod tests {
#[test]
fn render_plain_border() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
Block::bordered()
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Plain)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(
@@ -1398,7 +1415,8 @@ mod tests {
#[test]
fn render_rounded_border() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
Block::bordered()
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(
@@ -1414,7 +1432,8 @@ mod tests {
#[test]
fn render_double_border() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
Block::bordered()
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Double)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(
@@ -1430,7 +1449,8 @@ mod tests {
#[test]
fn render_quadrant_inside() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
Block::bordered()
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::QuadrantInside)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(
@@ -1446,7 +1466,8 @@ mod tests {
#[test]
fn render_border_quadrant_outside() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
Block::bordered()
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::QuadrantOutside)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(
@@ -1462,7 +1483,8 @@ mod tests {
#[test]
fn render_solid_border() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
Block::bordered()
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Thick)
.render(buffer.area, &mut buffer);
assert_buffer_eq!(
@@ -1478,7 +1500,8 @@ mod tests {
#[test]
fn render_custom_border_set() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
Block::bordered()
Block::default()
.borders(Borders::ALL)
.border_set(border::Set {
top_left: "1",
top_right: "2",

View File

@@ -1,4 +1,4 @@
use std::fmt;
use std::fmt::{self, Debug};
use bitflags::bitflags;
@@ -24,7 +24,7 @@ bitflags! {
/// Implement the `Debug` trait for the `Borders` bitflags. This is a manual implementation to
/// display the flags in a more readable way. The default implementation would display the
/// flags as 'Border(0x0)' for `Borders::NONE` for example.
impl fmt::Debug for Borders {
impl Debug for Borders {
/// Display the Borders bitflags as a list of names. For example, `Borders::NONE` will be
/// displayed as `NONE` and `Borders::ALL` will be displayed as `ALL`. If multiple flags are
/// set, they will be displayed separated by a pipe character.

View File

@@ -28,14 +28,14 @@ pub struct Monthly<'a, DS: DateStyler> {
impl<'a, DS: DateStyler> Monthly<'a, DS> {
/// Construct a calendar for the `display_date` and highlight the `events`
pub const fn new(display_date: Date, events: DS) -> Self {
pub fn new(display_date: Date, events: DS) -> Self {
Self {
display_date,
events,
show_surrounding: None,
show_weekday: None,
show_month: None,
default_style: Style::new(),
default_style: Style::default(),
block: None,
}
}
@@ -90,10 +90,10 @@ impl<'a, DS: DateStyler> Monthly<'a, DS> {
}
/// Return a style with only the background from the default style
const fn default_bg(&self) -> Style {
fn default_bg(&self) -> Style {
match self.default_style.bg {
None => Style::new(),
Some(c) => Style::new().bg(c),
None => Style::default(),
Some(c) => Style::default().bg(c),
}
}
@@ -164,7 +164,7 @@ impl<DS: DateStyler> Monthly<'_, DS> {
let mut y = days_area.y;
// go through all the weeks containing a day in the target month.
while curr_day.month() != self.display_date.month().next() {
while curr_day.month() as u8 != self.display_date.month().next() as u8 {
let mut spans = Vec::with_capacity(14);
for i in 0..7 {
// Draw the gutter. Do it here so we can avoid worrying about

View File

@@ -19,7 +19,7 @@ mod points;
mod rectangle;
mod world;
use std::{fmt, iter::zip};
use std::{fmt::Debug, iter::zip};
use itertools::Itertools;
@@ -70,7 +70,7 @@ struct Layer {
/// resolution of the grid might exceed the number of rows and columns. For example, a grid of
/// Braille patterns will have a resolution of 2x4 dots per cell. This means that a grid of 10x10
/// cells will have a resolution of 20x40 dots.
trait Grid: fmt::Debug {
trait Grid: Debug {
/// Get the resolution of the grid in number of dots.
///
/// This doesn't have to be the same as the number of rows and columns of the grid. For example,
@@ -567,7 +567,7 @@ impl<'a> Context<'a> {
/// };
///
/// Canvas::default()
/// .block(Block::bordered().title("Canvas"))
/// .block(Block::default().title("Canvas").borders(Borders::ALL))
/// .x_bounds([-180.0, 180.0])
/// .y_bounds([-90.0, 90.0])
/// .paint(|ctx| {

View File

@@ -27,7 +27,7 @@ pub enum MapResolution {
}
impl MapResolution {
const fn data(self) -> &'static [(f64, f64)] {
fn data(self) -> &'static [(f64, f64)] {
match self {
Self::Low => &WORLD_LOW_RESOLUTION,
Self::High => &WORLD_HIGH_RESOLUTION,

View File

@@ -1,5 +1,5 @@
/// [Source data](http://www.gnuplotting.org/plotting-the-world-revisited)
pub const WORLD_HIGH_RESOLUTION: [(f64, f64); 5125] = [
pub static WORLD_HIGH_RESOLUTION: [(f64, f64); 5125] = [
(-163.7128, -78.5956),
(-163.1058, -78.2233),
(-161.2451, -78.3801),
@@ -5127,7 +5127,7 @@ pub const WORLD_HIGH_RESOLUTION: [(f64, f64); 5125] = [
(180.0, -84.71338),
];
pub const WORLD_LOW_RESOLUTION: [(f64, f64); 1166] = [
pub static WORLD_LOW_RESOLUTION: [(f64, f64); 1166] = [
(-92.32, 48.24),
(-88.13, 48.92),
(-83.11, 46.27),

View File

@@ -8,7 +8,7 @@ use crate::{
prelude::*,
widgets::{
canvas::{Canvas, Line as CanvasLine, Points},
Block,
Block, Borders,
},
};
@@ -475,7 +475,7 @@ struct ChartLayout {
///
/// // Create the chart and link all the parts together
/// let chart = Chart::new(datasets)
/// .block(Block::new().title("Chart"))
/// .block(Block::default().title("Chart"))
/// .x_axis(x_axis)
/// .y_axis(y_axis);
/// ```
@@ -1050,7 +1050,9 @@ impl WidgetRef for Chart<'_> {
if let Some(legend_area) = layout.legend_area {
buf.set_style(legend_area, original_style);
Block::bordered().render(legend_area, buf);
Block::default()
.borders(Borders::ALL)
.render(legend_area, buf);
for (i, (dataset_name, dataset_style)) in self
.datasets

View File

@@ -11,7 +11,7 @@ use crate::prelude::*;
/// use ratatui::{prelude::*, widgets::*};
///
/// fn draw_on_clear(f: &mut Frame, area: Rect) {
/// let block = Block::bordered().title("Block");
/// let block = Block::default().title("Block").borders(Borders::ALL);
/// f.render_widget(Clear, area); // <- this will clear/reset the area first
/// f.render_widget(block, area); // now render the block widget
/// }

View File

@@ -17,7 +17,7 @@ use crate::{prelude::*, widgets::Block};
/// use ratatui::{prelude::*, widgets::*};
///
/// Gauge::default()
/// .block(Block::bordered().title("Progress"))
/// .block(Block::default().borders(Borders::ALL).title("Progress"))
/// .gauge_style(
/// Style::default()
/// .fg(Color::White)
@@ -164,12 +164,12 @@ impl WidgetRef for Gauge<'_> {
buf.set_style(area, self.style);
self.block.render_ref(area, buf);
let inner = self.block.inner_if_some(area);
self.render_gauge(inner, buf);
self.render_gague(inner, buf);
}
}
impl Gauge<'_> {
fn render_gauge(&self, gauge_area: Rect, buf: &mut Buffer) {
fn render_gague(&self, gauge_area: Rect, buf: &mut Buffer) {
if gauge_area.is_empty() {
return;
}
@@ -249,7 +249,7 @@ fn get_unicode_block<'a>(frac: f64) -> &'a str {
/// use ratatui::{prelude::*, widgets::*};
///
/// LineGauge::default()
/// .block(Block::bordered().title("Progress"))
/// .block(Block::default().borders(Borders::ALL).title("Progress"))
/// .gauge_style(
/// Style::default()
/// .fg(Color::White)

View File

@@ -384,7 +384,7 @@ where
/// # let area = Rect::default();
/// let items = ["Item 1", "Item 2", "Item 3"];
/// let list = List::new(items)
/// .block(Block::bordered().title("List"))
/// .block(Block::default().title("List").borders(Borders::ALL))
/// .style(Style::default().fg(Color::White))
/// .highlight_style(Style::default().add_modifier(Modifier::ITALIC))
/// .highlight_symbol(">>")
@@ -405,7 +405,7 @@ where
/// let mut state = ListState::default();
/// let items = ["Item 1", "Item 2", "Item 3"];
/// let list = List::new(items)
/// .block(Block::bordered().title("List"))
/// .block(Block::default().title("List").borders(Borders::ALL))
/// .highlight_style(Style::new().add_modifier(Modifier::REVERSED))
/// .highlight_symbol(">>")
/// .repeat_highlight_symbol(true);
@@ -537,7 +537,7 @@ impl<'a> List<'a> {
/// ```rust
/// # use ratatui::{prelude::*, widgets::*};
/// # let items = vec!["Item 1"];
/// let block = Block::bordered().title("List");
/// let block = Block::default().title("List").borders(Borders::ALL);
/// let list = List::new(items).block(block);
/// ```
#[must_use = "method moves the value of self and returns the modified value"]
@@ -1049,7 +1049,7 @@ mod tests {
use rstest::{fixture, rstest};
use super::*;
use crate::assert_buffer_eq;
use crate::{assert_buffer_eq, widgets::Borders};
#[test]
fn test_list_state_selected() {
@@ -1229,7 +1229,7 @@ mod tests {
let list = List::new(items)
.highlight_symbol(">>")
.block(Block::bordered());
.block(Block::default().borders(Borders::all()));
// attempt to render into an area of the buffer with zero height after
// setting the block borders
Widget::render(list, Rect::new(0, 0, 15, 2), &mut buffer);
@@ -1474,7 +1474,7 @@ mod tests {
#[test]
fn test_list_with_empty_strings() {
let items = list_items(vec!["Item 0", "", "", "Item 1", "Item 2"]);
let list = List::new(items).block(Block::bordered().title("List"));
let list = List::new(items).block(Block::default().title("List").borders(Borders::ALL));
let buffer = render_widget(list, 10, 7);
let expected = Buffer::with_lines(vec![
@@ -1499,7 +1499,7 @@ mod tests {
#[test]
fn test_list_block() {
let items = list_items(vec!["Item 0", "Item 1", "Item 2"]);
let list = List::new(items).block(Block::bordered().title("List"));
let list = List::new(items).block(Block::default().title("List").borders(Borders::ALL));
let buffer = render_widget(list, 10, 7);
let expected = Buffer::with_lines(vec![

View File

@@ -31,7 +31,7 @@ const fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Align
/// "Third line".into(),
/// ];
/// Paragraph::new(text)
/// .block(Block::bordered().title("Paragraph"))
/// .block(Block::new().title("Paragraph").borders(Borders::ALL))
/// .style(Style::new().white().on_black())
/// .alignment(Alignment::Center)
/// .wrap(Wrap { trim: true });
@@ -126,7 +126,8 @@ impl<'a> Paragraph<'a> {
///
/// ```rust
/// # use ratatui::{prelude::*, widgets::*};
/// let paragraph = Paragraph::new("Hello, world!").block(Block::bordered().title("Paragraph"));
/// let paragraph = Paragraph::new("Hello, world!")
/// .block(Block::default().title("Paragraph").borders(Borders::ALL));
/// ```
#[must_use = "method moves the value of self and returns the modified value"]
pub fn block(mut self, block: Block<'a>) -> Self {
@@ -255,8 +256,6 @@ impl<'a> Paragraph<'a> {
/// need in order to be fully rendered. For paragraphs that do not use wrapping, this count is
/// simply the number of lines present in the paragraph.
///
/// Note: The design for text wrapping is not stable and might affect this API.
///
/// # Example
///
/// ```ignore
@@ -268,6 +267,7 @@ impl<'a> Paragraph<'a> {
/// ```
#[stability::unstable(
feature = "rendered-line-info",
reason = "The design for text wrapping is not stable and might affect this API.",
issue = "https://github.com/ratatui-org/ratatui/issues/293"
)]
pub fn line_count(&self, width: u16) -> usize {
@@ -297,8 +297,6 @@ impl<'a> Paragraph<'a> {
/// Calculates the shortest line width needed to avoid any word being wrapped or truncated.
///
/// Note: The design for text wrapping is not stable and might affect this API.
///
/// # Example
///
/// ```ignore
@@ -311,6 +309,7 @@ impl<'a> Paragraph<'a> {
/// ```
#[stability::unstable(
feature = "rendered-line-info",
reason = "The design for text wrapping is not stable and might affect this API.",
issue = "https://github.com/ratatui-org/ratatui/issues/293"
)]
pub fn line_width(&self) -> usize {
@@ -526,7 +525,8 @@ mod test {
// We use the slightly unconventional "worlds" instead of "world" here to make sure when we
// can truncate this without triggering the typos linter.
let text = "Hello, worlds!";
let truncated_paragraph = Paragraph::new(text).block(Block::bordered().title("Title"));
let truncated_paragraph =
Paragraph::new(text).block(Block::default().title("Title").borders(Borders::ALL));
let wrapped_paragraph = truncated_paragraph.clone().wrap(Wrap { trim: false });
let trimmed_paragraph = truncated_paragraph.clone().wrap(Wrap { trim: true });
@@ -625,10 +625,10 @@ mod test {
#[test]
fn test_render_paragraph_with_block_with_bottom_title_and_border() {
let block = Block::new()
.borders(Borders::BOTTOM)
let block = Block::default()
.title("Title")
.title_position(Position::Bottom)
.title("Title");
.borders(Borders::BOTTOM);
let paragraph = Paragraph::new("Hello, world!").block(block);
test_case(

View File

@@ -6,7 +6,6 @@ use unicode_width::UnicodeWidthStr;
use crate::{layout::Alignment, text::StyledGrapheme};
const NBSP: &str = "\u{00a0}";
const ZWSP: &str = "\u{200b}";
/// A state machine to pack styled symbols into lines.
/// Cannot implement it as Iterator since it yields slices of the internal buffer (need streaming
@@ -105,8 +104,8 @@ where
let mut has_seen_non_whitespace = false;
for StyledGrapheme { symbol, style } in line_symbols {
let symbol_whitespace = symbol == ZWSP
|| (symbol.chars().all(&char::is_whitespace) && symbol != NBSP);
let symbol_whitespace =
symbol.chars().all(&char::is_whitespace) && symbol != NBSP;
let symbol_width = symbol.width() as u16;
// Ignore characters wider than the total max width
if symbol_width > self.max_line_width {
@@ -707,12 +706,4 @@ mod test {
vec![Alignment::Left, Alignment::Right, Alignment::Center]
);
}
#[test]
fn line_composer_zero_width_white_space() {
let width = 3;
let line = "foo\u{200b}bar";
let (word_wrapper, _, _) = run_composer(Composer::WordWrapper { trim: true }, line, width);
assert_eq!(word_wrapper, vec!["foo", "bar"]);
}
}

View File

@@ -29,11 +29,6 @@ use crate::{prelude::*, symbols::scrollbar::*};
/// └─────────── begin
/// ```
///
/// # Important
///
/// You must specify the [`ScrollbarState::content_length`] before rendering the `Scrollbar`, or
/// else the `Scrollbar` will render blank.
///
/// # Examples
///
/// ```rust

View File

@@ -24,7 +24,7 @@ use crate::{prelude::*, widgets::Block};
/// use ratatui::{prelude::*, widgets::*};
///
/// Sparkline::default()
/// .block(Block::bordered().title("Sparkline"))
/// .block(Block::default().title("Sparkline").borders(Borders::ALL))
/// .data(&[0, 2, 3, 4, 1, 4, 10])
/// .max(5)
/// .direction(RenderDirection::RightToLeft)

View File

@@ -75,7 +75,7 @@ use crate::{layout::Flex, prelude::*, widgets::Block};
/// // It has an optional footer, which is simply a Row always visible at the bottom.
/// .footer(Row::new(vec!["Updated on Dec 28"]))
/// // As any other widget, a Table can be wrapped in a Block.
/// .block(Block::new().title("Table"))
/// .block(Block::default().title("Table"))
/// // The selected row and its content can also be styled.
/// .highlight_style(Style::new().reversed())
/// // ...and potentially show a symbol in front of the selection.
@@ -178,7 +178,7 @@ use crate::{layout::Flex, prelude::*, widgets::Block};
/// ];
/// let widths = [Constraint::Length(5), Constraint::Length(5), Constraint::Length(10)];
/// let table = Table::new(rows, widths)
/// .block(Block::new().title("Table"))
/// .block(Block::default().title("Table"))
/// .highlight_style(Style::new().add_modifier(Modifier::REVERSED))
/// .highlight_symbol(">>");
///
@@ -418,7 +418,7 @@ impl<'a> Table<'a> {
/// # use ratatui::{prelude::*, widgets::*};
/// # let rows = [Row::new(vec!["Cell1", "Cell2"])];
/// # let widths = [Constraint::Length(5), Constraint::Length(5)];
/// let block = Block::bordered().title("Table");
/// let block = Block::default().title("Table").borders(Borders::ALL);
/// let table = Table::new(rows, widths).block(block);
/// ```
#[must_use = "method moves the value of self and returns the modified value"]
@@ -842,7 +842,7 @@ mod tests {
use std::vec;
use super::*;
use crate::{layout::Constraint::*, style::Style, text::Line};
use crate::{layout::Constraint::*, style::Style, text::Line, widgets::Borders};
#[test]
fn new() {
@@ -930,7 +930,7 @@ mod tests {
#[test]
fn block() {
let block = Block::bordered().title("Table");
let block = Block::default().title("Table").borders(Borders::ALL);
let table = Table::default().block(block.clone());
assert_eq!(table.block, Some(block));
}
@@ -1027,7 +1027,7 @@ mod tests {
Row::new(vec!["Cell1", "Cell2"]),
Row::new(vec!["Cell3", "Cell4"]),
];
let block = Block::bordered().title("Block");
let block = Block::new().borders(Borders::ALL).title("Block");
let table = Table::new(rows, vec![Constraint::Length(5); 2]).block(block);
Widget::render(table, Rect::new(0, 0, 15, 3), &mut buf);
let expected = Buffer::with_lines(vec![

View File

@@ -60,11 +60,8 @@ impl TableState {
/// # use ratatui::{prelude::*, widgets::*};
/// let state = TableState::new();
/// ```
pub const fn new() -> Self {
Self {
offset: 0,
selected: None,
}
pub fn new() -> Self {
Self::default()
}
/// Sets the index of the first row to be displayed

View File

@@ -17,7 +17,7 @@ const DEFAULT_HIGHLIGHT_STYLE: Style = Style::new().add_modifier(Modifier::REVER
/// use ratatui::{prelude::*, widgets::*};
///
/// Tabs::new(vec!["Tab1", "Tab2", "Tab3", "Tab4"])
/// .block(Block::bordered().title("Tabs"))
/// .block(Block::default().title("Tabs").borders(Borders::ALL))
/// .style(Style::default().white())
/// .highlight_style(Style::default().yellow())
/// .select(2)
@@ -333,7 +333,7 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::assert_buffer_eq;
use crate::{assert_buffer_eq, widgets::Borders};
#[test]
fn new() {
@@ -414,8 +414,8 @@ mod tests {
#[test]
fn render_with_block() {
let tabs =
Tabs::new(vec!["Tab1", "Tab2", "Tab3", "Tab4"]).block(Block::bordered().title("Tabs"));
let tabs = Tabs::new(vec!["Tab1", "Tab2", "Tab3", "Tab4"])
.block(Block::default().title("Tabs").borders(Borders::ALL));
let mut expected = Buffer::with_lines(vec![
"┌Tabs────────────────────────┐",
"│ Tab1 │ Tab2 │ Tab3 │ Tab4 │",

View File

@@ -62,7 +62,7 @@ fn assert_buffer(state: &mut AppState, expected: &Buffer) {
.split(f.size());
let list = List::new(items)
.highlight_symbol(">>")
.block(Block::new().borders(Borders::RIGHT));
.block(Block::default().borders(Borders::RIGHT));
f.render_stateful_widget(list, layout[0], &mut state.list);
let table = Table::new(

View File

@@ -5,7 +5,7 @@ use ratatui::{
buffer::Buffer,
layout::Rect,
style::{Color, Style, Stylize},
widgets::{BarChart, Block, Paragraph},
widgets::{BarChart, Block, Borders, Paragraph},
Terminal,
};
@@ -61,10 +61,11 @@ fn barchart_can_be_stylized() {
#[test]
fn block_can_be_stylized() -> io::Result<()> {
let block = Block::bordered()
let block = Block::default()
.title("Title".light_blue())
.on_cyan()
.cyan();
.cyan()
.borders(Borders::ALL);
let area = Rect::new(0, 0, 8, 3);
let mut terminal = Terminal::new(TestBackend::new(11, 4))?;

View File

@@ -2,7 +2,7 @@ use ratatui::{
backend::TestBackend,
buffer::Buffer,
style::{Color, Style},
widgets::{Bar, BarChart, BarGroup, Block},
widgets::{Bar, BarChart, BarGroup, Block, Borders},
Terminal,
};
@@ -16,7 +16,7 @@ fn widgets_barchart_not_full_below_max_value() {
.draw(|f| {
let size = f.size();
let barchart = BarChart::default()
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.data(&[("empty", 0), ("half", 50), ("almost", 99), ("full", 100)])
.max(100)
.bar_width(7)
@@ -53,7 +53,7 @@ fn widgets_barchart_group() {
.draw(|f| {
let size = f.size();
let barchart = BarChart::default()
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.data(
BarGroup::default().label("Mar".into()).bars(&[
Bar::default()

View File

@@ -15,8 +15,9 @@ use ratatui::{
fn widgets_block_renders() {
let backend = TestBackend::new(10, 10);
let mut terminal = Terminal::new(backend).unwrap();
let block =
Block::bordered().title(Span::styled("Title", Style::default().fg(Color::LightBlue)));
let block = Block::default()
.title(Span::styled("Title", Style::default().fg(Color::LightBlue)))
.borders(Borders::ALL);
terminal
.draw(|frame| frame.render_widget(block, Rect::new(0, 0, 8, 8)))
.unwrap();
@@ -53,7 +54,7 @@ fn widgets_block_titles_overlap() {
// Left overrides the center
test_case(
Block::new()
Block::default()
.title(Title::from("aaaaa").alignment(Alignment::Left))
.title(Title::from("bbb").alignment(Alignment::Center))
.title(Title::from("ccc").alignment(Alignment::Right)),
@@ -63,7 +64,7 @@ fn widgets_block_titles_overlap() {
// Left alignment overrides the center alignment which overrides the right alignment
test_case(
Block::new()
Block::default()
.title(Title::from("aaaaa").alignment(Alignment::Left))
.title(Title::from("bbbbb").alignment(Alignment::Center))
.title(Title::from("ccccc").alignment(Alignment::Right)),
@@ -73,7 +74,7 @@ fn widgets_block_titles_overlap() {
// Multiple left alignment overrides the center alignment and the right alignment
test_case(
Block::new()
Block::default()
.title(Title::from("aaaaa").alignment(Alignment::Left))
.title(Title::from("aaaaa").alignment(Alignment::Left))
.title(Title::from("bbbbb").alignment(Alignment::Center))
@@ -84,7 +85,7 @@ fn widgets_block_titles_overlap() {
// The right alignment doesn't override the center alignment, but pierces through it
test_case(
Block::new()
Block::default()
.title(Title::from("bbbbb").alignment(Alignment::Center))
.title(Title::from("ccccccccccc").alignment(Alignment::Right)),
Rect::new(0, 0, 11, 1),
@@ -115,69 +116,69 @@ fn widgets_block_renders_on_small_areas() {
];
for (borders, symbol) in one_cell_test_cases {
test_case(
Block::new().borders(borders).title("Test"),
Block::default().title("Test").borders(borders),
Rect::new(0, 0, 0, 0),
Buffer::empty(Rect::new(0, 0, 0, 0)),
);
test_case(
Block::new().borders(borders).title("Test"),
Block::default().title("Test").borders(borders),
Rect::new(0, 0, 1, 0),
Buffer::empty(Rect::new(0, 0, 1, 0)),
);
test_case(
Block::new().borders(borders).title("Test"),
Block::default().title("Test").borders(borders),
Rect::new(0, 0, 0, 1),
Buffer::empty(Rect::new(0, 0, 0, 1)),
);
test_case(
Block::new().borders(borders).title("Test"),
Block::default().title("Test").borders(borders),
Rect::new(0, 0, 1, 1),
Buffer::with_lines(vec![symbol]),
);
}
test_case(
Block::new().borders(Borders::LEFT).title("Test"),
Block::default().title("Test").borders(Borders::LEFT),
Rect::new(0, 0, 4, 1),
Buffer::with_lines(vec!["│Tes"]),
);
test_case(
Block::new().borders(Borders::RIGHT).title("Test"),
Block::default().title("Test").borders(Borders::RIGHT),
Rect::new(0, 0, 4, 1),
Buffer::with_lines(vec!["Tes│"]),
);
test_case(
Block::new().borders(Borders::RIGHT).title("Test"),
Block::default().title("Test").borders(Borders::RIGHT),
Rect::new(0, 0, 4, 1),
Buffer::with_lines(vec!["Tes│"]),
);
test_case(
Block::new()
.borders(Borders::LEFT | Borders::RIGHT)
.title("Test"),
Block::default()
.title("Test")
.borders(Borders::LEFT | Borders::RIGHT),
Rect::new(0, 0, 4, 1),
Buffer::with_lines(vec!["│Te│"]),
);
test_case(
Block::new().borders(Borders::TOP).title("Test"),
Block::default().title("Test").borders(Borders::TOP),
Rect::new(0, 0, 4, 1),
Buffer::with_lines(vec!["Test"]),
);
test_case(
Block::new().borders(Borders::TOP).title("Test"),
Block::default().title("Test").borders(Borders::TOP),
Rect::new(0, 0, 5, 1),
Buffer::with_lines(vec!["Test─"]),
);
test_case(
Block::new()
.borders(Borders::LEFT | Borders::TOP)
.title("Test"),
Block::default()
.title("Test")
.borders(Borders::LEFT | Borders::TOP),
Rect::new(0, 0, 5, 1),
Buffer::with_lines(vec!["┌Test"]),
);
test_case(
Block::new()
.borders(Borders::LEFT | Borders::TOP)
.title("Test"),
Block::default()
.title("Test")
.borders(Borders::LEFT | Borders::TOP),
Rect::new(0, 0, 6, 1),
Buffer::with_lines(vec!["┌Test─"]),
);
@@ -192,14 +193,14 @@ fn widgets_block_title_alignment() {
let backend = TestBackend::new(15, 3);
let mut terminal = Terminal::new(backend).unwrap();
let block1 = Block::new()
.borders(borders)
.title(Title::from(Span::styled("Title", Style::default())).alignment(alignment));
let block1 = Block::default()
.title(Title::from(Span::styled("Title", Style::default())).alignment(alignment))
.borders(borders);
let block2 = Block::new()
.borders(borders)
let block2 = Block::default()
.title("Title")
.title_alignment(alignment)
.title("Title");
.borders(borders);
let area = Rect::new(1, 0, 13, 3);
@@ -389,7 +390,7 @@ fn widgets_block_title_alignment_bottom() {
let title = Title::from(Span::styled("Title", Style::default()))
.alignment(alignment)
.position(Position::Bottom);
let block = Block::new().borders(borders).title(title);
let block = Block::default().title(title).borders(borders);
let area = Rect::new(1, 0, 13, 3);
terminal
.draw(|frame| frame.render_widget(block, area))
@@ -572,7 +573,10 @@ fn widgets_block_multiple_titles() {
let backend = TestBackend::new(15, 3);
let mut terminal = Terminal::new(backend).unwrap();
let block = Block::new().borders(borders).title(title_a).title(title_b);
let block = Block::default()
.title(title_a)
.title(title_b)
.borders(borders);
let area = Rect::new(1, 0, 13, 3);

View File

@@ -5,7 +5,7 @@ use ratatui::{
style::{Color, Style},
symbols,
text::{self, Span},
widgets::{Axis, Block, Chart, Dataset, GraphType::Line},
widgets::{Axis, Block, Borders, Chart, Dataset, GraphType::Line},
Terminal,
};
use rstest::rstest;
@@ -46,7 +46,7 @@ fn widgets_chart_can_render_on_small_areas(#[case] width: u16, #[case] height: u
.style(Style::default().fg(Color::Magenta))
.data(&[(0.0, 0.0)])];
let chart = Chart::new(datasets)
.block(Block::bordered().title("Plot"))
.block(Block::default().title("Plot").borders(Borders::ALL))
.x_axis(
Axis::default()
.bounds([0.0, 0.0])
@@ -265,7 +265,7 @@ fn widgets_chart_can_have_axis_with_zero_length_bounds() {
.style(Style::default().fg(Color::Magenta))
.data(&[(0.0, 0.0)])];
let chart = Chart::new(datasets)
.block(Block::bordered().title("Plot"))
.block(Block::default().title("Plot").borders(Borders::ALL))
.x_axis(
Axis::default()
.bounds([0.0, 0.0])
@@ -305,7 +305,7 @@ fn widgets_chart_handles_overflows() {
(1_588_298_496.0, 1.0),
])];
let chart = Chart::new(datasets)
.block(Block::bordered().title("Plot"))
.block(Block::default().title("Plot").borders(Borders::ALL))
.x_axis(
Axis::default()
.bounds([1_588_298_471.0, 1_588_992_600.0])
@@ -338,7 +338,11 @@ fn widgets_chart_can_have_empty_datasets() {
.draw(|f| {
let datasets = vec![Dataset::default().data(&[]).graph_type(Line)];
let chart = Chart::new(datasets)
.block(Block::bordered().title("Empty Dataset With Line"))
.block(
Block::default()
.title("Empty Dataset With Line")
.borders(Borders::ALL),
)
.x_axis(
Axis::default()
.bounds([0.0, 0.0])
@@ -407,7 +411,7 @@ fn widgets_chart_can_have_a_legend() {
];
let chart = Chart::new(datasets)
.style(Style::default().bg(Color::White))
.block(Block::bordered().title("Chart Test"))
.block(Block::default().title("Chart Test").borders(Borders::ALL))
.x_axis(
Axis::default()
.bounds([0.0, 100.0])

View File

@@ -5,7 +5,7 @@ use ratatui::{
style::{Color, Modifier, Style, Stylize},
symbols,
text::Span,
widgets::{Block, Gauge, LineGauge},
widgets::{Block, Borders, Gauge, LineGauge},
Terminal,
};
@@ -22,13 +22,13 @@ fn widgets_gauge_renders() {
.split(f.size());
let gauge = Gauge::default()
.block(Block::bordered().title("Percentage"))
.block(Block::default().title("Percentage").borders(Borders::ALL))
.gauge_style(Style::default().bg(Color::Blue).fg(Color::Red))
.use_unicode(true)
.percent(43);
f.render_widget(gauge, chunks[0]);
let gauge = Gauge::default()
.block(Block::bordered().title("Ratio"))
.block(Block::default().title("Ratio").borders(Borders::ALL))
.gauge_style(Style::default().bg(Color::Blue).fg(Color::Red))
.use_unicode(true)
.ratio(0.511_313_934_313_1);
@@ -71,12 +71,12 @@ fn widgets_gauge_renders_no_unicode() {
.split(f.size());
let gauge = Gauge::default()
.block(Block::bordered().title("Percentage"))
.block(Block::default().title("Percentage").borders(Borders::ALL))
.percent(43)
.use_unicode(false);
f.render_widget(gauge, chunks[0]);
let gauge = Gauge::default()
.block(Block::bordered().title("Ratio"))
.block(Block::default().title("Ratio").borders(Borders::ALL))
.ratio(0.211_313_934_313_1)
.use_unicode(false);
f.render_widget(gauge, chunks[1]);
@@ -106,7 +106,9 @@ fn widgets_gauge_applies_styles() {
.draw(|f| {
let gauge = Gauge::default()
.block(
Block::bordered().title(Span::styled("Test", Style::default().fg(Color::Red))),
Block::default()
.title(Span::styled("Test", Style::default().fg(Color::Red)))
.borders(Borders::ALL),
)
.gauge_style(Style::default().fg(Color::Blue).bg(Color::Red))
.percent(43)
@@ -194,7 +196,7 @@ fn widgets_line_gauge_renders() {
},
);
let gauge = LineGauge::default()
.block(Block::bordered().title("Gauge 2"))
.block(Block::default().title("Gauge 2").borders(Borders::ALL))
.gauge_style(Style::default().fg(Color::Green))
.line_set(symbols::line::THICK)
.ratio(0.211_313_934_313_1);

View File

@@ -127,7 +127,7 @@ fn widgets_list_should_truncate_items() {
terminal
.draw(|f| {
let list = List::new(case.items.clone())
.block(Block::new().borders(Borders::RIGHT))
.block(Block::default().borders(Borders::RIGHT))
.highlight_symbol(">> ");
f.render_stateful_widget(list, Rect::new(0, 0, 8, 2), &mut state);
})
@@ -288,7 +288,7 @@ fn widgets_list_enable_always_highlight_spacing() {
ListItem::new(vec![Line::from("Item 2"), Line::from("Item 2b")]),
ListItem::new(vec![Line::from("Item 3"), Line::from("Item 3c")]),
])
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.highlight_spacing(space);
f.render_stateful_widget(table, size, state);

View File

@@ -3,7 +3,7 @@ use ratatui::{
buffer::Buffer,
layout::Alignment,
text::{Line, Span, Text},
widgets::{Block, Padding, Paragraph, Wrap},
widgets::{Block, Borders, Padding, Paragraph, Wrap},
Terminal,
};
@@ -30,7 +30,7 @@ fn widgets_paragraph_renders_double_width_graphemes() {
let text = vec![Line::from(s)];
let paragraph = Paragraph::new(text)
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.wrap(Wrap { trim: true });
test_case(
@@ -61,7 +61,7 @@ fn widgets_paragraph_renders_mixed_width_graphemes() {
let size = f.size();
let text = vec![Line::from(s)];
let paragraph = Paragraph::new(text)
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.wrap(Wrap { trim: true });
f.render_widget(paragraph, size);
})
@@ -84,7 +84,7 @@ fn widgets_paragraph_renders_mixed_width_graphemes() {
fn widgets_paragraph_can_wrap_with_a_trailing_nbsp() {
let nbsp = "\u{00a0}";
let line = Line::from(vec![Span::raw("NBSP"), Span::raw(nbsp)]);
let paragraph = Paragraph::new(line).block(Block::bordered());
let paragraph = Paragraph::new(line).block(Block::default().borders(Borders::ALL));
test_case(
paragraph,
@@ -99,8 +99,8 @@ fn widgets_paragraph_can_wrap_with_a_trailing_nbsp() {
#[test]
fn widgets_paragraph_can_scroll_horizontally() {
let text =
Text::from("段落现在可以水平滚动了!\nParagraph can scroll horizontally!\nLittle line");
let paragraph = Paragraph::new(text).block(Block::bordered());
Text::from("段落现在可以水平滚动了!\nParagraph can scroll horizontally!\nShort line");
let paragraph = Paragraph::new(text).block(Block::default().borders(Borders::ALL));
test_case(
paragraph.clone().alignment(Alignment::Left).scroll((0, 7)),
@@ -108,7 +108,7 @@ fn widgets_paragraph_can_scroll_horizontally() {
"┌──────────────────┐",
"│在可以水平滚动了!│",
"│ph can scroll hori│",
"line │",
"│ine ",
"│ │",
"│ │",
"│ │",
@@ -124,7 +124,7 @@ fn widgets_paragraph_can_scroll_horizontally() {
"┌──────────────────┐",
"│段落现在可以水平滚│",
"│Paragraph can scro│",
"Little line│",
" Short line│",
"│ │",
"│ │",
"│ │",
@@ -144,7 +144,7 @@ const SAMPLE_STRING: &str = "The library is based on the principle of immediate
fn widgets_paragraph_can_wrap_its_content() {
let text = vec![Line::from(SAMPLE_STRING)];
let paragraph = Paragraph::new(text)
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.wrap(Wrap { trim: true });
test_case(
@@ -196,14 +196,14 @@ fn widgets_paragraph_can_wrap_its_content() {
#[test]
fn widgets_paragraph_works_with_padding() {
let block = Block::bordered().padding(Padding {
left: 2,
right: 2,
top: 1,
bottom: 1,
});
let paragraph = Paragraph::new(vec![Line::from(SAMPLE_STRING)])
.block(block.clone())
let text = vec![Line::from(SAMPLE_STRING)];
let paragraph = Paragraph::new(text)
.block(Block::default().borders(Borders::ALL).padding(Padding {
left: 2,
right: 2,
top: 1,
bottom: 1,
}))
.wrap(Wrap { trim: true });
test_case(
@@ -224,7 +224,7 @@ fn widgets_paragraph_works_with_padding() {
]),
);
test_case(
paragraph.alignment(Alignment::Right),
paragraph.clone().alignment(Alignment::Right),
Buffer::with_lines(vec![
"┌────────────────────┐",
"│ │",
@@ -241,12 +241,16 @@ fn widgets_paragraph_works_with_padding() {
]),
);
let paragraph = Paragraph::new(vec![
Line::from("This is always centered.").alignment(Alignment::Center),
Line::from(SAMPLE_STRING),
])
.block(block)
.wrap(Wrap { trim: true });
let mut text = vec![Line::from("This is always centered.").alignment(Alignment::Center)];
text.push(Line::from(SAMPLE_STRING));
let paragraph = Paragraph::new(text)
.block(Block::default().borders(Borders::ALL).padding(Padding {
left: 2,
right: 2,
top: 1,
bottom: 1,
}))
.wrap(Wrap { trim: true });
test_case(
paragraph.alignment(Alignment::Right),
@@ -279,7 +283,7 @@ fn widgets_paragraph_can_align_spans() {
Line::from(default_s),
];
let paragraph = Paragraph::new(text)
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.wrap(Wrap { trim: true });
test_case(
@@ -329,7 +333,7 @@ fn widgets_paragraph_can_align_spans() {
let mut text = left_lines.clone();
text.append(&mut lines);
let paragraph = Paragraph::new(text).block(Block::bordered());
let paragraph = Paragraph::new(text).block(Block::default().borders(Borders::ALL));
test_case(
paragraph.clone().alignment(Alignment::Right),

View File

@@ -33,7 +33,7 @@ fn widgets_table_column_spacing_can_be_changed() {
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.column_spacing(column_spacing);
f.render_widget(table, size);
})
@@ -129,7 +129,7 @@ fn widgets_table_columns_widths_can_use_fixed_length_constraints() {
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::bordered());
.block(Block::default().borders(Borders::ALL));
f.render_widget(table, size);
})
.unwrap();
@@ -221,7 +221,7 @@ fn widgets_table_columns_widths_can_use_percentage_constraints() {
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.column_spacing(0);
f.render_widget(table, size);
})
@@ -331,7 +331,7 @@ fn widgets_table_columns_widths_can_use_mixed_constraints() {
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::bordered());
.block(Block::default().borders(Borders::ALL));
f.render_widget(table, size);
})
.unwrap();
@@ -444,7 +444,7 @@ fn widgets_table_columns_widths_can_use_ratio_constraints() {
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.column_spacing(0);
f.render_widget(table, size);
})
@@ -555,7 +555,7 @@ fn widgets_table_can_have_rows_with_multi_lines() {
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.column_spacing(1);
f.render_stateful_widget(table, size, state);
@@ -652,7 +652,7 @@ fn widgets_table_enable_always_highlight_spacing() {
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.highlight_spacing(space)
.column_spacing(1);
@@ -796,7 +796,7 @@ fn widgets_table_can_have_elements_styled_individually() {
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::new().borders(Borders::LEFT | Borders::RIGHT))
.block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
.highlight_symbol(">> ")
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
.column_spacing(1);
@@ -861,7 +861,7 @@ fn widgets_table_should_render_even_if_empty() {
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]))
.block(Block::new().borders(Borders::LEFT | Borders::RIGHT))
.block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
.column_spacing(1);
f.render_widget(table, size);
})
@@ -902,7 +902,7 @@ fn widgets_table_columns_dont_panic() {
],
)
.header(Row::new(vec!["h1", "h2", "h3", "h4"]))
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.column_spacing(1);
@@ -940,7 +940,7 @@ fn widgets_table_should_clamp_offset_if_rows_are_removed() {
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.column_spacing(1);
f.render_stateful_widget(table, size, &mut state);
})
@@ -971,7 +971,7 @@ fn widgets_table_should_clamp_offset_if_rows_are_removed() {
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::bordered())
.block(Block::default().borders(Borders::ALL))
.column_spacing(1);
f.render_stateful_widget(table, size, &mut state);
})