Compare commits

..

20 Commits

Author SHA1 Message Date
Tadeas Uradnik
5fbb77ad20 chore(README): use terminal theme for badges (#1026)
The badges in the readme were all the default theme. Giving them
prettier colors that match the terminal gif is better. I've used the
colors from the VHS repo.
2024-04-25 20:27:28 -07:00
EdJoPaTo
97ee102f17 feat(buffer): track_caller for index_of (#1046)
The caller put in the wrong x/y -> the caller is the cause.
2024-04-25 16:23:09 -07:00
EdJoPaTo
c442dfd1ad perf(canvas): change map data to const instead of static (#1037) 2024-04-24 01:49:57 -07:00
Ibby
0a164965ea fix: use to_string to serialize Color (#934)
Since deserialize now uses `FromStr` to deserialize color, serializing
`Color` RGB values, as well as index values, would produce an output
that would no longer be able to be deserialized without causing an
error.


Color::Rgb will now be serialized as the hex representation of their
value.
For example, with serde_json, `Color::Rgb(255, 0, 255)` would be
serialized as `"#FF00FF"` rather than `{"Rgb": [255, 0, 255]}`.

Color::Indexed will now be serialized as just the string of the index.
For example, with serde_json, `Color::Indexed(10)` would be serialized
as `"10"` rather than `{"Indexed": 10}`.

Other color variants remain the same.
2024-04-23 23:30:00 -07:00
EdJoPaTo
bf0923473c feat(table): make TableState::new const (#1040) 2024-04-21 23:23:56 -06:00
EdJoPaTo
81b96338ea perf(calendar): use const fn (#1039)
Also, do the comparison without `as u8`. Stays the same at runtime and
is cleaner code.
2024-04-21 11:34:17 -06:00
EdJoPaTo
2e71c1874e perf(buffer): simplify Buffer::filled with macro (#1036)
The `vec![]` macro is highly optimized by the Rust team and shorter.
Don't do it manually.

This change is mainly cleaner code. The only production code that uses
this is `Terminal::with_options` and `Terminal::insert_before` so it's
not performance relevant on every render.
2024-04-21 11:29:37 -06:00
Josh McKinney
c75aa1990f build: add clippy::cargo lint (#1053)
Followup to https://github.com/ratatui-org/ratatui/pull/1035 and
https://github.com/ratatui-org/ratatui/discussions/1034

It's reasonable to enable this and deal with breakage by fixing any
specific issues that arise.
2024-04-21 10:43:46 -06:00
mcskware
326a461f9a chore: Add package categories field (#1035)
Add the package categories field in Cargo.toml, with value
`["command-line-interface"]`. This fixes the (currently non-default)
clippy cargo group lint
[`clippy::cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#/cargo_common_metadata).

As per discussion in [Cargo package categories
suggestions](https://github.com/ratatui-org/ratatui/discussions/1034),
this lint is not suggested to be run by default in CI, but rather as an
occasional one-off as part of the larger
[`clippy::cargo`](https://doc.rust-lang.org/stable/clippy/lints.html#cargo)
lint group.
2024-04-19 12:55:12 -07:00
EdJoPaTo
f3172c59d4 refactor(gauge): fix internal typo (#1048) 2024-04-18 13:47:52 +03:00
EdJoPaTo
bef5bcf750 refactor(example): remove pointless new method (#1038)
Use `App::default()` directly.
2024-04-16 21:02:39 +03:00
Marcin Puc
11264787d0 chore(changelog): fix changelog release links (#1033)
<!-- Please read CONTRIBUTING.md before submitting any pull request. -->

Minor oopsie
2024-04-15 23:11:27 +03:00
dependabot[bot]
9b3c260b76 chore(deps): update rstest requirement from 0.18.2 to 0.19.0 (#1031)
Updates the requirements on [rstest](https://github.com/la10736/rstest)
to permit the latest version.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/la10736/rstest/releases">rstest's
releases</a>.</em></p>
<blockquote>
<p>Introduce MSRV and minor fixes</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/la10736/rstest/blob/master/CHANGELOG.md">rstest's
changelog</a>.</em></p>
<blockquote>
<h2>[0.19.0] 2024/4/9</h2>
<h3>Changed</h3>
<ul>
<li>Defined <code>rust-version</code> for each crate (see <a
href="https://redirect.github.com/la10736/rstest/issues/227">#227</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>
<p><code>#[once]</code> fixtures now require the returned type to be
<a
href="https://doc.rust-lang.org/std/marker/trait.Sync.html"><code>Sync</code></a>
to prevent UB
when tests are executed in parallel. (see <a
href="https://redirect.github.com/la10736/rstest/issues/235">#235</a>
for more details)</p>
</li>
<li>
<p><code>#[future(awt)]</code> and <code>#[awt]</code> now properly
handle mutable (<code>mut</code>) parameters by treating futures as
immutable and
treating the awaited rebinding as mutable.</p>
</li>
</ul>
<h2>[0.18.2] 2023/8/13</h2>
<h3>Changed</h3>
<ul>
<li>Now <code>#[files]</code> accept also parent folders (see <a
href="https://redirect.github.com/la10736/rstest/issues/205">#205</a>
for more details).</li>
</ul>
<h2>[0.18.1] 2023/7/5</h2>
<h3>Fixed</h3>
<ul>
<li>Wrong doc test</li>
<li>Docs</li>
</ul>
<h2>[0.18.0] 2023/7/4</h2>
<h3>Add</h3>
<ul>
<li>Add support for <code>RSTEST_TIMEOUT</code> environment variable to
define a max timeout
for each function (see <a
href="https://redirect.github.com/la10736/rstest/issues/190">#190</a>
for details).
Thanks to <a
href="https://github.com/aviramha"><code>@​aviramha</code></a> for idea
and PR</li>
<li><code>#[files(&quot;glob path&quot;)]</code> attribute to generate
tests based on files that
satisfy the given glob path (see <a
href="https://redirect.github.com/la10736/rstest/issues/163">#163</a>
for details).</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Switch to <code>syn</code> 2.0 and edition 2021 : minimal Rust
version now is 1.56.0
both for <code>rstest</code> and <code>rstest_reuse</code> (see <a
href="https://redirect.github.com/la10736/rstest/issues/187">#187</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Fixed wired behavior on extraction <code>#[awt]</code> function
attrs (See
<a
href="https://redirect.github.com/la10736/rstest/issues/189">#189</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3ffd682568"><code>3ffd682</code></a>
Fix license links</li>
<li><a
href="36ab06d68e"><code>36ab06d</code></a>
Fix license link</li>
<li><a
href="941d8ac7ae"><code>941d8ac</code></a>
Update changelog</li>
<li><a
href="cdff674d16"><code>cdff674</code></a>
Bump version</li>
<li><a
href="e0624fe198"><code>e0624fe</code></a>
Fix clippy warning</li>
<li><a
href="f7b4b57922"><code>f7b4b57</code></a>
Shutup warning on nightly (tests)</li>
<li><a
href="49a7d3816e"><code>49a7d38</code></a>
Shutup warning in night</li>
<li><a
href="b58ce22ef1"><code>b58ce22</code></a>
Set resolver in virtual manifest</li>
<li><a
href="3c2fb9c33c"><code>3c2fb9c</code></a>
Properly handle mutability for awaited futures (<a
href="https://redirect.github.com/la10736/rstest/issues/239">#239</a>)</li>
<li><a
href="61a7007f66"><code>61a7007</code></a>
We're not interested about msrv for tests</li>
<li>Additional commits viewable in <a
href="https://github.com/la10736/rstest/compare/v0.18.2...v0.19.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-15 18:55:45 +03:00
Orhun Parmaksız
363c4c54e8 chore(release): prepare for 0.26.2 (#1029) 2024-04-15 13:38:55 +03:00
Josh McKinney
b7778e5cd1 fix(paragraph): unit test typo (#1022) 2024-04-08 17:33:05 -07:00
dependabot[bot]
b5061c5250 chore(deps): update stability requirement from 0.1.1 to 0.2.0 (#1021)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-04-08 22:43:04 +03:00
EdJoPaTo
359204c929 refactor: simplify to io::Result (#1016)
Simplifies the code, logic stays exactly the same.
2024-04-03 02:08:42 -07:00
EdJoPaTo
14461c3a35 docs(breaking-changes): typos and markdownlint (#1009) 2024-04-03 02:07:39 -07:00
Josh McKinney
3b002fdcab docs: update incompatible code warning in examples readme (#1013)
Co-authored-by: EdJoPaTo <rfc-conform-git-commit-email@funny-long-domain-label-everyone-hates-as-it-is-too-long.edjopato.de>
2024-04-01 13:34:03 -07:00
Tadeas Uradnik
0207160784 fix(line): line truncation respects alignment (#987)
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
2024-03-30 18:10:33 -07:00
25 changed files with 808 additions and 135 deletions

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`
### `Flex::Start` is the new default flex mode for `Layout` ([#881])
[#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 did
A new type `Masked` was introduced that implements `From<Text<'a>>`. This causes any code that
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,7 +2,457 @@
All notable changes to this project will be documented in this file.
## [0.26.1](https://github.com/ratatui-org/ratatui/releases/tag/0.26.1) - 2024-02-12
## [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
This is a patch release that fixes bugs and adds enhancements, including new iterators, title options for blocks, and various rendering improvements. ✨
@@ -161,7 +611,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/0.26.0) - 2024-02-02
## [0.26.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.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 🐭
@@ -1818,7 +2268,7 @@ Shout out to our new sponsors!
* @atuinsh
* @JeftavanderHorst!
## [0.25.0](https://github.com/ratatui-org/ratatui/releases/tag/0.25.0) - 2023-12-18
## [0.25.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.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 🐭
@@ -2303,7 +2753,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/0.24.0) - 2023-10-23
## [0.24.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.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,10 +1,11 @@
[package]
name = "ratatui"
version = "0.26.1" # crate version
version = "0.26.2" # 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/"
keywords = ["tui", "terminal", "dashboard"]
categories = ["command-line-interface"]
repository = "https://github.com/ratatui-org/ratatui"
readme = "README.md"
license = "MIT"
@@ -39,7 +40,7 @@ unicode-segmentation = "1.10"
unicode-width = "0.1"
document-features = { version = "0.2.7", optional = true }
lru = "0.12.0"
stability = "0.1.1"
stability = "0.2.0"
compact_str = "0.7.1"
[dev-dependencies]
@@ -58,12 +59,14 @@ palette = "0.7.3"
pretty_assertions = "1.4.0"
rand = "0.8.5"
rand_chacha = "0.3.1"
rstest = "0.18.2"
rstest = "0.19.0"
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

@@ -327,24 +327,23 @@ 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
[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
[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
[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
https://img.shields.io/codecov/c/github/ratatui-org/ratatui?logo=codecov&style=flat-square&token=BAQ8SOKEST&color=C43AC3&logoColor=C43AC3
[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
https://img.shields.io/discord/1070692720437383208?label=discord&logo=discord&style=flat-square&color=1370D3&logoColor=1370D3
[Discord Server]: https://discord.gg/pMCEU9hNEj
[Docs Badge]: https://img.shields.io/docsrs/ratatui?logo=rust&style=flat-square
[Docs Badge]: https://img.shields.io/docsrs/ratatui?logo=rust&style=flat-square&logoColor=E05D44
[Matrix Badge]:
https://img.shields.io/matrix/ratatui-general%3Amatrix.org?style=flat-square&logo=matrix&label=Matrix
https://img.shields.io/matrix/ratatui-general%3Amatrix.org?style=flat-square&logo=matrix&label=Matrix&color=C43AC3
[Matrix]: https://matrix.to/#/#ratatui:matrix.org
[Sponsors Badge]: https://img.shields.io/github/sponsors/ratatui-org?logo=github&style=flat-square
[Sponsors Badge]: https://img.shields.io/github/sponsors/ratatui-org?logo=github&style=flat-square&color=1370D3
<!-- cargo-rdme end -->

View File

@@ -1,8 +1,7 @@
use criterion::{criterion_group, criterion_main, BatchSize, Bencher, BenchmarkId, Criterion};
use criterion::{criterion_group, criterion_main, BatchSize, Bencher, Criterion};
use ratatui::{
buffer::Buffer,
layout::Rect,
prelude::Alignment,
layout::{Alignment, Rect},
widgets::{
block::{Position, Title},
Block, Borders, Padding, Widget,
@@ -13,23 +12,23 @@ use ratatui::{
fn block(c: &mut Criterion) {
let mut group = c.benchmark_group("block");
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
for (width, height) in [
(100, 50), // vertically split screen
(200, 50), // 1080p fullscreen with medium font
(256, 256), // Max sized area
] {
let buffer_area = buffer_size.area();
let buffer_size = Rect::new(0, 0, width, height);
// Render an empty block
group.bench_with_input(
BenchmarkId::new("render_empty", buffer_area),
format!("render_empty/{width}x{height}"),
&Block::new(),
|b, block| render(b, block, buffer_size),
);
// Render with all features
group.bench_with_input(
BenchmarkId::new("render_all_feature", buffer_area),
format!("render_all_feature/{width}x{height}"),
&Block::new()
.borders(Borders::ALL)
.title("test title")

View File

@@ -1,4 +1,9 @@
# configuration for https://github.com/orhun/git-cliff
# git-cliff ~ configuration file
# https://git-cliff.org/docs/configuration
[remote.github]
owner = "ratatui-org"
repo = "ratatui"
[changelog]
# changelog header
@@ -22,7 +27,9 @@ 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 }}
*({{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 %}\
{%- if commit.breaking %} [**breaking**]{% endif %}
{%- if commit.body %}
@@ -49,6 +56,28 @@ 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 %}
"""
@@ -68,7 +97,7 @@ filter_unconventional = true
split_commits = false
# regex for preprocessing the commit messages
commit_preprocessors = [
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/ratatui-org/ratatui/issues/${2}))" },
{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" },
{ 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}" },

4
clippy.toml Normal file
View File

@@ -0,0 +1,4 @@
# 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
allowed-duplicate-crates = ["bitflags"]

View File

@@ -1,20 +1,20 @@
# Examples
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.
This folder might use unreleased code. View the examples for the latest release instead.
> [!WARNING]
>
> There are backwards incompatible changes in these examples, as they are designed to compile
> There may be 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.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.
> 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.
> - 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

@@ -38,14 +38,10 @@ enum Tab {
}
pub fn run(terminal: &mut Terminal<impl Backend>) -> Result<()> {
App::new().run(terminal)
App::default().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::new().run(terminal)?;
App::default().run(terminal)?;
term::restore()?;
Ok(())
}

View File

@@ -228,7 +228,7 @@ where
Ok(Rect::new(0, 0, width, height))
}
fn window_size(&mut self) -> Result<WindowSize, io::Error> {
fn window_size(&mut self) -> io::Result<WindowSize> {
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) -> Result<WindowSize, io::Error> {
fn window_size(&mut self) -> io::Result<WindowSize> {
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) -> Result<(), io::Error>
fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
where
I: Iterator<Item = (u16, u16, &'a Cell)>,
{
@@ -181,13 +181,13 @@ impl Backend for TermwizBackend {
Ok(())
}
fn hide_cursor(&mut self) -> Result<(), io::Error> {
fn hide_cursor(&mut self) -> io::Result<()> {
self.buffered_terminal
.add_change(Change::CursorVisibility(CursorVisibility::Hidden));
Ok(())
}
fn show_cursor(&mut self) -> Result<(), io::Error> {
fn show_cursor(&mut self) -> io::Result<()> {
self.buffered_terminal
.add_change(Change::CursorVisibility(CursorVisibility::Visible));
Ok(())
@@ -207,18 +207,18 @@ impl Backend for TermwizBackend {
Ok(())
}
fn clear(&mut self) -> Result<(), io::Error> {
fn clear(&mut self) -> io::Result<()> {
self.buffered_terminal
.add_change(Change::ClearScreen(termwiz::color::ColorAttribute::Default));
Ok(())
}
fn size(&self) -> Result<Rect, io::Error> {
fn size(&self) -> io::Result<Rect> {
let (cols, rows) = self.buffered_terminal.dimensions();
Ok(Rect::new(0, 0, u16_max(cols), u16_max(rows)))
}
fn window_size(&mut self) -> Result<WindowSize, io::Error> {
fn window_size(&mut self) -> io::Result<WindowSize> {
let ScreenSize {
cols,
rows,
@@ -241,7 +241,7 @@ impl Backend for TermwizBackend {
})
}
fn flush(&mut self) -> Result<(), io::Error> {
fn flush(&mut self) -> io::Result<()> {
self.buffered_terminal
.flush()
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

View File

@@ -114,7 +114,7 @@ impl Display for TestBackend {
}
impl Backend for TestBackend {
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
where
I: Iterator<Item = (u16, u16, &'a Cell)>,
{
@@ -125,26 +125,26 @@ impl Backend for TestBackend {
Ok(())
}
fn hide_cursor(&mut self) -> Result<(), io::Error> {
fn hide_cursor(&mut self) -> io::Result<()> {
self.cursor = false;
Ok(())
}
fn show_cursor(&mut self) -> Result<(), io::Error> {
fn show_cursor(&mut self) -> io::Result<()> {
self.cursor = true;
Ok(())
}
fn get_cursor(&mut self) -> Result<(u16, u16), io::Error> {
fn get_cursor(&mut self) -> io::Result<(u16, u16)> {
Ok(self.pos)
}
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error> {
fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> {
self.pos = (x, y);
Ok(())
}
fn clear(&mut self) -> Result<(), io::Error> {
fn clear(&mut self) -> io::Result<()> {
self.buffer.reset();
Ok(())
}
@@ -214,11 +214,11 @@ impl Backend for TestBackend {
Ok(())
}
fn size(&self) -> Result<Rect, io::Error> {
fn size(&self) -> io::Result<Rect> {
Ok(Rect::new(0, 0, self.width, self.height))
}
fn window_size(&mut self) -> Result<WindowSize, io::Error> {
fn window_size(&mut self) -> io::Result<WindowSize> {
// 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) -> Result<(), io::Error> {
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}

View File

@@ -55,22 +55,21 @@ pub struct Buffer {
impl Buffer {
/// Returns a Buffer with all cells set to the default one
#[must_use]
pub fn empty(area: Rect) -> Self {
let cell = Cell::default();
Self::filled(area, &cell)
Self::filled(area, &Cell::default())
}
/// 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 mut content = Vec::with_capacity(size);
for _ in 0..size {
content.push(cell.clone());
}
let content = vec![cell.clone(); size];
Self { area, content }
}
/// Returns a Buffer containing the given lines
#[must_use]
pub fn with_lines<'a, Iter>(lines: Iter) -> Self
where
Iter: IntoIterator,
@@ -97,12 +96,14 @@ 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]
@@ -134,6 +135,7 @@ 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()

View File

@@ -40,16 +40,6 @@ 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)
@@ -145,20 +135,9 @@ 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.
///
/// # 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));
/// ```
/// See [`Offset`] for details.
#[must_use = "method returns the modified value"]
pub fn offset<T: Into<Offset>>(self, offset: T) -> Self {
let offset = offset.into();
pub fn offset(self, offset: Offset) -> Self {
Self {
x: i32::from(self.x)
.saturating_add(offset.x)
@@ -444,11 +423,6 @@ 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

@@ -66,7 +66,6 @@ use std::{
///
/// [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]
@@ -141,14 +140,102 @@ impl Color {
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Color {
/// This utilises the [`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>,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&s).map_err(serde::de::Error::custom)
/// 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)),
},
}
}
}
@@ -579,4 +666,42 @@ 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

@@ -453,6 +453,39 @@ 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
@@ -554,17 +587,23 @@ impl WidgetRef for Line<'_> {
let area = area.intersection(buf.area);
buf.set_style(area, self.style);
let width = self.width() as u16;
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,
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 mut x = area.left().saturating_add(offset);
for span in &self.spans {
for span in &line.spans {
let span_width = span.width() as u16;
let span_area = Rect {
x,
width: span_width.min(area.right() - x),
width: span_width.min(area.right().saturating_sub(x)),
..area
};
span.render(span_area, buf);
@@ -604,6 +643,7 @@ mod tests {
use rstest::{fixture, rstest};
use super::*;
use crate::assert_buffer_eq;
#[fixture]
fn small_buf() -> Buffer {
@@ -847,6 +887,53 @@ 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

@@ -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 fn new(display_date: Date, events: DS) -> Self {
pub const fn new(display_date: Date, events: DS) -> Self {
Self {
display_date,
events,
show_surrounding: None,
show_weekday: None,
show_month: None,
default_style: Style::default(),
default_style: Style::new(),
block: None,
}
}
@@ -90,10 +90,10 @@ impl<'a, DS: DateStyler> Monthly<'a, DS> {
}
/// Return a style with only the background from the default style
fn default_bg(&self) -> Style {
const fn default_bg(&self) -> Style {
match self.default_style.bg {
None => Style::default(),
Some(c) => Style::default().bg(c),
None => Style::new(),
Some(c) => Style::new().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() as u8 != self.display_date.month().next() as u8 {
while curr_day.month() != self.display_date.month().next() {
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

@@ -27,7 +27,7 @@ pub enum MapResolution {
}
impl MapResolution {
fn data(self) -> &'static [(f64, f64)] {
const 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 static WORLD_HIGH_RESOLUTION: [(f64, f64); 5125] = [
pub const WORLD_HIGH_RESOLUTION: [(f64, f64); 5125] = [
(-163.7128, -78.5956),
(-163.1058, -78.2233),
(-161.2451, -78.3801),
@@ -5127,7 +5127,7 @@ pub static WORLD_HIGH_RESOLUTION: [(f64, f64); 5125] = [
(180.0, -84.71338),
];
pub static WORLD_LOW_RESOLUTION: [(f64, f64); 1166] = [
pub const WORLD_LOW_RESOLUTION: [(f64, f64); 1166] = [
(-92.32, 48.24),
(-88.13, 48.92),
(-83.11, 46.27),

View File

@@ -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_gague(inner, buf);
self.render_gauge(inner, buf);
}
}
impl Gauge<'_> {
fn render_gague(&self, gauge_area: Rect, buf: &mut Buffer) {
fn render_gauge(&self, gauge_area: Rect, buf: &mut Buffer) {
if gauge_area.is_empty() {
return;
}

View File

@@ -256,6 +256,8 @@ 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
@@ -267,7 +269,6 @@ 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,6 +298,8 @@ 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
@@ -309,7 +312,6 @@ 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 {

View File

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

View File

@@ -99,7 +99,7 @@ fn widgets_paragraph_can_wrap_with_a_trailing_nbsp() {
#[test]
fn widgets_paragraph_can_scroll_horizontally() {
let text =
Text::from("段落现在可以水平滚动了!\nParagraph can scroll horizontally!\nShort line");
Text::from("段落现在可以水平滚动了!\nParagraph can scroll horizontally!\nLittle line");
let paragraph = Paragraph::new(text).block(Block::default().borders(Borders::ALL));
test_case(
@@ -108,7 +108,7 @@ fn widgets_paragraph_can_scroll_horizontally() {
"┌──────────────────┐",
"│在可以水平滚动了!│",
"│ph can scroll hori│",
"│ine ",
"line │",
"│ │",
"│ │",
"│ │",
@@ -124,7 +124,7 @@ fn widgets_paragraph_can_scroll_horizontally() {
"┌──────────────────┐",
"│段落现在可以水平滚│",
"│Paragraph can scro│",
" Short line│",
"Little line│",
"│ │",
"│ │",
"│ │",