Compare commits

..

27 Commits

Author SHA1 Message Date
EdJoPaTo
baedc39494 refactor(buffer): simplify set_stringn logic (#1083) 2024-05-02 16:32:52 -07:00
EdJoPaTo
366c2a0e6d perf(block): use Block::bordered (#1041)
`Block::bordered()` is shorter than
`Block::new().borders(Borders::ALL)`, requires one less import
(`Borders`) and in case `Block::default()` was used before can even be
`const`.
2024-05-02 03:09:48 -07:00
Josh McKinney
bef2bc1e7c chore(cargo): add homepage to Cargo.toml (#1080) 2024-05-02 12:47:10 +03:00
Josh McKinney
64eb3913a4 chore: fixup cargo lint for windows targets (#1071)
Crossterm brings in multiple versions of the same dep
2024-05-01 05:59:09 -07:00
May
e95230beda docs: add note about scrollbar state content length (#1077) 2024-04-30 23:37:43 -07:00
Levi Zim
f4637d40c3 fix(reflow): allow wrapping at zero width whitespace (#1074) 2024-04-28 01:58:28 -07:00
Orhun Parmaksız
da1ade7b2e docs(github): update code owners about past maintainers (#1073)
As per suggestion in
https://github.com/ratatui-org/ratatui/pull/1067#issuecomment-2079766990

It's good for historical purposes!
2024-04-27 16:06:29 -07:00
Josh McKinney
1706b0a3e4 perf(crossterm): Speed up combined fg and bg color changes by up to 20% (#1072) 2024-04-27 14:34:52 -07:00
Eiko Thomas
4392759501 fix(examples): changed user_input example to work with multi-byte unicode chars (#1069)
This is the proposed solution for issue #1068. It solves the bug in the
user_input example with multi-byte UTF-8 characters as input.

Fixes: #1068

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-04-26 22:36:36 -07:00
Paul Sobolik
20fc0ddfca fix(examples): fix key handling in constraints (#1066)
Add check for `KeyEventKind::Press` to constraints example's event
handler to eliminate double keys
on Windows.

Fixes: #1062

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-04-26 21:20:30 -07:00
Orhun Parmaksız
3687f78f6a docs(github): update code owners (#1067)
Removes the team members that are not able to review PRs recently (with
their approval ofc)
2024-04-26 20:51:06 -07:00
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
64 changed files with 1159 additions and 615 deletions

5
.github/CODEOWNERS vendored
View File

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

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,11 +1,13 @@
[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"]
repository = "https://github.com/ratatui-org/ratatui"
homepage = "https://ratatui.rs"
keywords = ["tui", "terminal", "dashboard"]
categories = ["command-line-interface"]
readme = "README.md"
license = "MIT"
exclude = [
@@ -39,7 +41,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 +60,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

@@ -162,7 +162,7 @@ fn handle_events() -> io::Result<bool> {
fn ui(frame: &mut Frame) {
frame.render_widget(
Paragraph::new("Hello World!")
.block(Block::default().title("Greeting").borders(Borders::ALL)),
.block(Block::bordered().title("Greeting")),
frame.size(),
);
}
@@ -207,11 +207,11 @@ fn ui(frame: &mut Frame) {
)
.split(main_layout[1]);
frame.render_widget(
Block::default().borders(Borders::ALL).title("Left"),
Block::bordered().title("Left"),
inner_layout[0],
);
frame.render_widget(
Block::default().borders(Borders::ALL).title("Right"),
Block::bordered().title("Right"),
inner_layout[1],
);
}
@@ -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,11 +1,10 @@
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,
Block, Padding, Widget,
},
};
@@ -13,32 +12,31 @@ 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),
&Block::new()
.borders(Borders::ALL)
format!("render_all_feature/{width}x{height}"),
&Block::bordered()
.padding(Padding::new(5, 5, 2, 2))
.title("test title")
.title(
Title::from("bottom left title")
.alignment(Alignment::Right)
.position(Position::Bottom),
)
.padding(Padding::new(5, 5, 2, 2)),
),
|b, block| render(b, block, buffer_size),
);
}

View File

@@ -1,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}" },

15
clippy.toml Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,8 +10,8 @@ use crossterm::{
cursor::{Hide, MoveTo, Show},
execute, queue,
style::{
Attribute as CAttribute, Attributes as CAttributes, Color as CColor, ContentStyle, Print,
SetAttribute, SetBackgroundColor, SetForegroundColor,
Attribute as CAttribute, Attributes as CAttributes, Color as CColor, Colors, ContentStyle,
Print, SetAttribute, SetBackgroundColor, SetColors, SetForegroundColor,
},
terminal::{self, Clear},
};
@@ -145,14 +145,12 @@ where
diff.queue(&mut self.writer)?;
modifier = cell.modifier;
}
if cell.fg != fg {
let color = CColor::from(cell.fg);
queue!(self.writer, SetForegroundColor(color))?;
if cell.fg != fg || cell.bg != bg {
queue!(
self.writer,
SetColors(Colors::new(cell.fg.into(), cell.bg.into()))
)?;
fg = cell.fg;
}
if cell.bg != bg {
let color = CColor::from(cell.bg);
queue!(self.writer, SetBackgroundColor(color))?;
bg = cell.bg;
}
#[cfg(feature = "underline-color")]

View File

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

View File

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

View File

@@ -552,32 +552,26 @@ impl From<(Color, Color, Modifier, Modifier)> for Style {
mod tests {
use super::*;
fn styles() -> Vec<Style> {
vec![
Style::default(),
Style::default().fg(Color::Yellow),
Style::default().bg(Color::Yellow),
Style::default().add_modifier(Modifier::BOLD),
Style::default().remove_modifier(Modifier::BOLD),
Style::default().add_modifier(Modifier::ITALIC),
Style::default().remove_modifier(Modifier::ITALIC),
Style::default().add_modifier(Modifier::ITALIC | Modifier::BOLD),
Style::default().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
]
}
#[test]
fn combined_patch_gives_same_result_as_individual_patch() {
let styles = styles();
let styles = [
Style::new(),
Style::new().fg(Color::Yellow),
Style::new().bg(Color::Yellow),
Style::new().add_modifier(Modifier::BOLD),
Style::new().remove_modifier(Modifier::BOLD),
Style::new().add_modifier(Modifier::ITALIC),
Style::new().remove_modifier(Modifier::ITALIC),
Style::new().add_modifier(Modifier::ITALIC | Modifier::BOLD),
Style::new().remove_modifier(Modifier::ITALIC | Modifier::BOLD),
];
for &a in &styles {
for &b in &styles {
for &c in &styles {
for &d in &styles {
let combined = a.patch(b.patch(c.patch(d)));
assert_eq!(
Style::default().patch(a).patch(b).patch(c).patch(d),
Style::default().patch(combined)
Style::new().patch(a).patch(b).patch(c).patch(d),
Style::new().patch(a.patch(b.patch(c.patch(d))))
);
}
}
@@ -589,7 +583,7 @@ mod tests {
fn combine_individual_modifiers() {
use crate::{buffer::Buffer, layout::Rect};
let mods = vec![
let mods = [
Modifier::BOLD,
Modifier::DIM,
Modifier::ITALIC,
@@ -603,14 +597,12 @@ mod tests {
let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
for m in &mods {
for m in mods {
buffer.get_mut(0, 0).set_style(Style::reset());
buffer
.get_mut(0, 0)
.set_style(Style::default().add_modifier(*m));
buffer.get_mut(0, 0).set_style(Style::new().add_modifier(m));
let style = buffer.get(0, 0).style();
assert!(style.add_modifier.contains(*m));
assert!(!style.sub_modifier.contains(*m));
assert!(style.add_modifier.contains(m));
assert!(!style.sub_modifier.contains(m));
}
}

View File

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

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

View File

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

View File

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

View File

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

View File

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

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

@@ -567,7 +567,7 @@ impl<'a> Context<'a> {
/// };
///
/// Canvas::default()
/// .block(Block::default().title("Canvas").borders(Borders::ALL))
/// .block(Block::bordered().title("Canvas"))
/// .x_bounds([-180.0, 180.0])
/// .y_bounds([-90.0, 90.0])
/// .paint(|ctx| {

View File

@@ -27,7 +27,7 @@ pub enum MapResolution {
}
impl MapResolution {
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

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

View File

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

View File

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

View File

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

View File

@@ -31,7 +31,7 @@ const fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Align
/// "Third line".into(),
/// ];
/// Paragraph::new(text)
/// .block(Block::new().title("Paragraph").borders(Borders::ALL))
/// .block(Block::bordered().title("Paragraph"))
/// .style(Style::new().white().on_black())
/// .alignment(Alignment::Center)
/// .wrap(Wrap { trim: true });
@@ -126,8 +126,7 @@ impl<'a> Paragraph<'a> {
///
/// ```rust
/// # use ratatui::{prelude::*, widgets::*};
/// let paragraph = Paragraph::new("Hello, world!")
/// .block(Block::default().title("Paragraph").borders(Borders::ALL));
/// let paragraph = Paragraph::new("Hello, world!").block(Block::bordered().title("Paragraph"));
/// ```
#[must_use = "method moves the value of self and returns the modified value"]
pub fn block(mut self, block: Block<'a>) -> Self {
@@ -256,6 +255,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 +268,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 +297,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 +311,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 {
@@ -525,8 +526,7 @@ mod test {
// We use the slightly unconventional "worlds" instead of "world" here to make sure when we
// can truncate this without triggering the typos linter.
let text = "Hello, worlds!";
let truncated_paragraph =
Paragraph::new(text).block(Block::default().title("Title").borders(Borders::ALL));
let truncated_paragraph = Paragraph::new(text).block(Block::bordered().title("Title"));
let wrapped_paragraph = truncated_paragraph.clone().wrap(Wrap { trim: false });
let trimmed_paragraph = truncated_paragraph.clone().wrap(Wrap { trim: true });
@@ -625,10 +625,10 @@ mod test {
#[test]
fn test_render_paragraph_with_block_with_bottom_title_and_border() {
let block = Block::default()
.title("Title")
let block = Block::new()
.borders(Borders::BOTTOM)
.title_position(Position::Bottom)
.borders(Borders::BOTTOM);
.title("Title");
let paragraph = Paragraph::new("Hello, world!").block(block);
test_case(

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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