Compare commits

..

19 Commits

Author SHA1 Message Date
Josh McKinney
48924e9cb9 fix: add ansi_buffer example 2024-04-25 20:01:22 -07:00
Josh McKinney
ee85bcfb43 fix: flip order of formatter and value to write 2024-04-25 19:52:22 -07:00
Josh McKinney
9f9a3047c4 fix: add area param to AnsiStringBuffer::render_ref 2024-04-25 19:41:29 -07:00
Josh McKinney
0d54ca06f8 feat: add WidgetExt trait for debugging widgets
Importing the `WidgetExt` trait allows users to easily get a string
representation of a widget with ANSI escape sequences for the terminal.
This is useful for debugging and testing widgets.

```rust
use ratatui::{prelude::*, widgets::widget_ext::WidgetExt};

fn main() {
    let greeting = Text::from(vec![
        Line::styled("Hello", Color::Blue),
        Line::styled("World ", Color::Green),
    ]);
    println!("{}", greeting.to_ansi_string(5, 2));
}
```

Fixes: https://github.com/ratatui-org/ratatui/issues/1045
2024-04-25 19:20:45 -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
22 changed files with 926 additions and 55 deletions

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"
@@ -145,6 +148,9 @@ unstable-rendered-line-info = []
## the future.
unstable-widget-ref = []
## Enables the `WidgetExt` trait which is experimental and may change in the future.
unstable-widget-ext = []
[package.metadata.docs.rs]
all-features = true
# see https://doc.rust-lang.org/nightly/rustdoc/scraped-examples.html
@@ -176,8 +182,8 @@ harness = false
[[example]]
name = "barchart"
required-features = ["crossterm"]
name = "ansi_buffer"
required-features = ["unstable-widget-ext", "unstable-widget-ref"]
doc-scrape-examples = true
[[example]]
@@ -322,6 +328,11 @@ name = "inline"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "widget_ext"
required-features = ["unstable-widget-ext"]
doc-scrape-examples = true
[[test]]
name = "state_serde"
required-features = ["serde"]

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"]

8
examples/ansi_buffer.rs Normal file
View File

@@ -0,0 +1,8 @@
use ratatui::{prelude::*, widgets::ansi_string_buffer::AnsiStringBuffer};
fn main() {
let mut buf = AnsiStringBuffer::new(5, 2);
buf.render_ref(&Line::styled("Hello", Color::Blue), Rect::new(0, 0, 5, 1));
buf.render_ref(&Line::styled("World", Color::Green), Rect::new(0, 1, 5, 1));
println!("{buf}");
}

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

@@ -0,0 +1,12 @@
# This is a vhs script. See https://github.com/charmbracelet/vhs for more info.
# To run this script, install vhs and run `vhs ./examples/hello_world.tape`
Output "target/widget_ext.gif"
Set Theme "Aardvark Blue"
Set Width 1200
Set Height 300
Set TypingSpeed 10ms
Type "cargo run --example=widget_ext --features=unstable-widget-ext"
Enter
Sleep 2s
Screenshot "target/widget_ext.png"
Sleep 1s

9
examples/widget_ext.rs Normal file
View File

@@ -0,0 +1,9 @@
use ratatui::{prelude::*, widgets::widget_ext::WidgetExt};
fn main() {
let greeting = Text::from(vec![
Line::styled("Hello", Color::Blue),
Line::styled("World ", Color::Green),
]);
println!("{}", greeting.to_ansi_string(5, 2));
}

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

@@ -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

@@ -21,6 +21,7 @@
//! - [`Tabs`]: displays a tab bar and allows selection.
//!
//! [`Canvas`]: crate::widgets::canvas::Canvas
pub mod ansi_string_buffer;
mod barchart;
pub mod block;
mod borders;
@@ -37,6 +38,7 @@ mod scrollbar;
mod sparkline;
mod table;
mod tabs;
pub mod widget_ext;
pub use self::{
barchart::{Bar, BarChart, BarGroup},

View File

@@ -0,0 +1,169 @@
//! A buffer that allows widgets to easily be rendered to a string with ANSI escape sequences.
use std::fmt::{self, Display, Formatter};
use crate::prelude::*;
/// A buffer that allows widgets to easily be rendered to a string with ANSI escape sequences.
#[stability::unstable(
feature = "widget-ext",
issue = "https://github.com/ratatui-org/ratatui/issues/1045"
)]
pub struct AnsiStringBuffer {
/// The area of the buffer.
pub area: Rect,
buf: Buffer,
}
impl AnsiStringBuffer {
/// Creates a new `AnsiStringBuffer` with the given width and height.
pub fn new(width: u16, height: u16) -> Self {
Self {
area: Rect::new(0, 0, width, height),
buf: Buffer::empty(Rect::new(0, 0, width, height)),
}
}
/// Renders the given widget to the buffer.
pub fn render_ref(&mut self, widget: &impl WidgetRef, area: Rect) {
widget.render_ref(area, &mut self.buf);
}
}
impl Display for AnsiStringBuffer {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let mut last_style = None;
for y in 0..self.area.height {
if y > 0 {
f.write_str("\n")?;
}
for x in 0..self.area.width {
let cell = self.buf.get(x, y);
let style = (cell.fg, cell.bg, cell.modifier);
if last_style.is_none() || last_style != Some(style) {
write_cell_style(f, cell)?;
last_style = Some(style);
}
f.write_str(cell.symbol())?;
}
}
f.write_str("\u{1b}[0m")
}
}
fn write_cell_style(f: &mut Formatter, cell: &buffer::Cell) -> fmt::Result {
f.write_str("\u{1b}[")?;
write_modifier(f, cell.modifier)?;
write_fg(f, cell.fg)?;
write_bg(f, cell.bg)?;
f.write_str("m")
}
fn write_modifier(f: &mut Formatter, modifier: Modifier) -> fmt::Result {
if modifier.contains(Modifier::BOLD) {
f.write_str("1;")?;
}
if modifier.contains(Modifier::DIM) {
f.write_str("2;")?;
}
if modifier.contains(Modifier::ITALIC) {
f.write_str("3;")?;
}
if modifier.contains(Modifier::UNDERLINED) {
f.write_str("4;")?;
}
if modifier.contains(Modifier::SLOW_BLINK) {
f.write_str("5;")?;
}
if modifier.contains(Modifier::RAPID_BLINK) {
f.write_str("6;")?;
}
if modifier.contains(Modifier::REVERSED) {
f.write_str("7;")?;
}
if modifier.contains(Modifier::HIDDEN) {
f.write_str("8;")?;
}
if modifier.contains(Modifier::CROSSED_OUT) {
f.write_str("9;")?;
}
Ok(())
}
fn write_fg(f: &mut Formatter, color: Color) -> fmt::Result {
f.write_str(match color {
Color::Reset => "39",
Color::Black => "30",
Color::Red => "31",
Color::Green => "32",
Color::Yellow => "33",
Color::Blue => "34",
Color::Magenta => "35",
Color::Cyan => "36",
Color::Gray => "37",
Color::DarkGray => "90",
Color::LightRed => "91",
Color::LightGreen => "92",
Color::LightYellow => "93",
Color::LightBlue => "94",
Color::LightMagenta => "95",
Color::LightCyan => "96",
Color::White => "97",
_ => "",
})?;
if let Color::Rgb(red, green, blue) = color {
f.write_fmt(format_args!("38;2;{red};{green};{blue}"))?;
}
if let Color::Indexed(i) = color {
f.write_fmt(format_args!("38;5;{i}"))?;
}
f.write_str(";")
}
fn write_bg(f: &mut Formatter, color: Color) -> fmt::Result {
f.write_str(match color {
Color::Reset => "49",
Color::Black => "40",
Color::Red => "41",
Color::Green => "42",
Color::Yellow => "43",
Color::Blue => "44",
Color::Magenta => "45",
Color::Cyan => "46",
Color::Gray => "47",
Color::DarkGray => "100",
Color::LightRed => "101",
Color::LightGreen => "102",
Color::LightYellow => "103",
Color::LightBlue => "104",
Color::LightMagenta => "105",
Color::LightCyan => "106",
Color::White => "107",
_ => "",
})?;
if let Color::Rgb(red, green, blue) = color {
f.write_fmt(format_args!("48;2;{red};{green};{blue}"))?;
}
if let Color::Indexed(i) = color {
f.write_fmt(format_args!("48;5;{i}"))?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_string() {
let mut buf = AnsiStringBuffer::new(5, 2);
buf.render_ref(&Line::styled("Hello", Color::Blue), Rect::new(0, 0, 5, 1));
buf.render_ref(&Line::styled("World", Color::Green), Rect::new(0, 1, 5, 1));
let ansi_string = buf.to_string();
// println!("{ansi_string}"); // uncomment to visually inspect the output
assert_eq!(
ansi_string,
"\u{1b}[34;49mHello\n\u{1b}[32;49mWorld\u{1b}[0m"
);
}
}

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

50
src/widgets/widget_ext.rs Normal file
View File

@@ -0,0 +1,50 @@
//! A module that provides an extension trait for widgets that provides methods that are useful for
//! debugging.
use super::ansi_string_buffer::AnsiStringBuffer;
use crate::prelude::*;
/// An extension trait for widgets that provides methods that are useful for debugging.
#[stability::unstable(
feature = "widget-ext",
issue = "https://github.com/ratatui-org/ratatui/issues/1045"
)]
pub trait WidgetExt {
/// Returns a string representation of the widget with ANSI escape sequences for the terminal.
fn to_ansi_string(&self, width: u16, height: u16) -> String;
}
impl<W: WidgetRef> WidgetExt for W {
fn to_ansi_string(&self, width: u16, height: u16) -> String {
let mut buf = AnsiStringBuffer::new(width, height);
buf.render_ref(self, buf.area);
buf.to_string()
}
}
#[cfg(test)]
mod widget_ext_tests {
use super::*;
struct Greeting;
impl WidgetRef for Greeting {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
let text = Text::from(vec![
Line::styled("Hello", Color::Blue),
Line::styled("World ", Color::Green),
]);
text.render(area, buf);
}
}
#[test]
fn widget_ext_to_ansi_string() {
let ansi_string = Greeting.to_ansi_string(5, 2);
// println!("{ansi_string}"); // uncomment to visually inspect the output
assert_eq!(
ansi_string,
"\u{1b}[34;49mHello\n\u{1b}[32;49mWorld\u{1b}[0m"
);
}
}

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│",
"│ │",
"│ │",
"│ │",