Compare commits
70 Commits
v0.30.0-al
...
v0.30.0-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7831aedd4 | ||
|
|
4a871f993e | ||
|
|
da05957fa0 | ||
|
|
41d883da7a | ||
|
|
0552223511 | ||
|
|
21a561b1b3 | ||
|
|
079d74ce14 | ||
|
|
22ec4f7414 | ||
|
|
6f213191ef | ||
|
|
088aac136d | ||
|
|
a692a6e371 | ||
|
|
1798512e94 | ||
|
|
32f3833a6d | ||
|
|
2ccc40e116 | ||
|
|
019e34e006 | ||
|
|
11cbb2ba87 | ||
|
|
50ba96518f | ||
|
|
904b0aa723 | ||
|
|
b544e394c9 | ||
|
|
1d28c89fe5 | ||
|
|
1d2882636e | ||
|
|
157cb3401b | ||
|
|
3d0c96a838 | ||
|
|
03066d81bf | ||
|
|
5f57d35234 | ||
|
|
5c021bf344 | ||
|
|
9721300a47 | ||
|
|
9a541981b8 | ||
|
|
a6a1368250 | ||
|
|
1fcca6369e | ||
|
|
694c788c24 | ||
|
|
6e436725e4 | ||
|
|
dafb716f9d | ||
|
|
819e92cd44 | ||
|
|
a38066d2d1 | ||
|
|
3646c97840 | ||
|
|
1c3b698b82 | ||
|
|
c767f6bc3c | ||
|
|
08ea837753 | ||
|
|
a8aca0ec12 | ||
|
|
ed5dd73084 | ||
|
|
b5f7e44183 | ||
|
|
fab532171d | ||
|
|
898aef6e2f | ||
|
|
f57b696fdc | ||
|
|
452366aa9e | ||
|
|
ff729b7607 | ||
|
|
6ddde0e8a8 | ||
|
|
93ad6b828c | ||
|
|
e411d9ec3e | ||
|
|
a0979d6871 | ||
|
|
ed071f3723 | ||
|
|
9275d3421c | ||
|
|
15f442a71e | ||
|
|
18e70d3d51 | ||
|
|
17bba14540 | ||
|
|
ce4856a65f | ||
|
|
f2451e7f1e | ||
|
|
7c8573f575 | ||
|
|
4f0a8b21af | ||
|
|
91147c4d75 | ||
|
|
6dd25a3111 | ||
|
|
35eba76b4d | ||
|
|
357ae7e251 | ||
|
|
3b13240728 | ||
|
|
d201b8e5dd | ||
|
|
21e62d84c2 | ||
|
|
d3f01ebf6e | ||
|
|
2892bddce6 | ||
|
|
fbf6050c86 |
22
.cz.toml
22
.cz.toml
@@ -32,17 +32,17 @@ schema_pattern = "(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)
|
||||
type = "list"
|
||||
name = "change_type"
|
||||
choices = [
|
||||
{ value = "build", name = "build: Changes that affect the build system or external dependencies (example scopes: pip, docker, npm)", key = "b" },
|
||||
{ value = "chore", name = "chore: A modification that generally does not fall into any other category", key = "c" },
|
||||
{ value = "ci", name = "ci: Changes to our CI configuration files and scripts (example scopes: GitLabCI)", key = "i" },
|
||||
{ value = "docs", name = "docs: Documentation only changes", key = "d" },
|
||||
{ value = "feat", name = "feat: A new feature.", key = "f" },
|
||||
{ value = "fix", name = "fix: A bug fix.", key = "x" },
|
||||
{ value = "perf", name = "perf: A code change that improves performance", key = "p" },
|
||||
{ value = "refactor", name = "refactor: A code change that neither fixes a bug nor adds a feature", key = "r" },
|
||||
{ value = "revert", name = "revert: Revert previous commits", key = "v" },
|
||||
{ value = "style", name = "style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)", key = "s" },
|
||||
{ value = "test", name = "test: Adding missing or correcting existing tests", key = "t" },
|
||||
{ value = "build", name = "build: Changes that affect the build system or external dependencies (example scopes: pip, docker, npm)", key = "b" },
|
||||
{ value = "chore", name = "chore: A modification that generally does not fall into any other category", key = "c" },
|
||||
{ value = "ci", name = "ci: Changes to our CI configuration files and scripts (example scopes: GitLabCI)", key = "i" },
|
||||
{ value = "docs", name = "docs: Documentation only changes", key = "d" },
|
||||
{ value = "feat", name = "feat: A new feature.", key = "f" },
|
||||
{ value = "fix", name = "fix: A bug fix.", key = "x" },
|
||||
{ value = "perf", name = "perf: A code change that improves performance", key = "p" },
|
||||
{ value = "refactor", name = "refactor: A code change that neither fixes a bug nor adds a feature", key = "r" },
|
||||
{ value = "revert", name = "revert: Revert previous commits", key = "v" },
|
||||
{ value = "style", name = "style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)", key = "s" },
|
||||
{ value = "test", name = "test: Adding missing or correcting existing tests", key = "t" },
|
||||
]
|
||||
message = "Select the type of change you are committing"
|
||||
|
||||
|
||||
4
.github/workflows/bench_track_fork_pr.yml
vendored
4
.github/workflows/bench_track_fork_pr.yml
vendored
@@ -18,12 +18,12 @@ jobs:
|
||||
PR_EVENT: event.json
|
||||
steps:
|
||||
- name: Download Benchmark Results
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
with:
|
||||
name: ${{ env.BENCHMARK_RESULTS }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
- name: Download PR Event
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v7
|
||||
with:
|
||||
name: ${{ env.PR_EVENT }}
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@@ -28,7 +28,11 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with: { components: rustfmt }
|
||||
- run: cargo xtask lint-formatting
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: taplo-cli
|
||||
- run: cargo xtask format --check
|
||||
|
||||
# Check for typos in the codebase.
|
||||
# See <https://github.com/crate-ci/typos/>
|
||||
@@ -55,7 +59,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: bnjbvr/cargo-machete@v0.7.0
|
||||
|
||||
# Run cargo clippy.
|
||||
@@ -67,7 +70,7 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with: { components: clippy }
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo xtask lint-clippy
|
||||
- run: cargo xtask clippy
|
||||
|
||||
# Run markdownlint on all markdown files in the repository.
|
||||
lint-markdown:
|
||||
@@ -114,7 +117,7 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo xtask check
|
||||
- run: cargo xtask check --all-features
|
||||
|
||||
# Check if README.md is up-to-date with the crate's documentation.
|
||||
check-readme:
|
||||
@@ -124,7 +127,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: taiki-e/install-action@cargo-rdme
|
||||
- run: cargo xtask check-readme
|
||||
- run: cargo xtask readme --check
|
||||
|
||||
# Run cargo rustdoc with the same options that would be used by docs.rs, taking into account the
|
||||
# package.metadata.docs.rs configured in Cargo.toml. https://github.com/dtolnay/cargo-docs-rs
|
||||
@@ -138,7 +141,7 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: dtolnay/install@cargo-docs-rs
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo xtask lint-docs
|
||||
- run: cargo xtask docs
|
||||
|
||||
# Run cargo test on the documentation of the crate. This will catch any code examples that don't
|
||||
# compile, or any other issues in the documentation.
|
||||
|
||||
12
.taplo.toml
Normal file
12
.taplo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
include = ["**/*.toml"]
|
||||
|
||||
[formatting]
|
||||
column_width = 100
|
||||
reorder_keys = false
|
||||
|
||||
[[rule]]
|
||||
include = ["**/Cargo.toml"]
|
||||
keys = ["dependencies", "dev-dependencies", "build-dependencies", "workspace.dependencies"]
|
||||
|
||||
[rule.formatting]
|
||||
reorder_keys = true
|
||||
@@ -12,6 +12,7 @@ This is a quick summary of the sections below:
|
||||
|
||||
- [Unreleased](#unreleased)
|
||||
- The `From` impls for backend types are now replaced with more specific traits
|
||||
- `FrameExt` trait for `unstable-widget-ref` feature
|
||||
- [v0.29.0](#v0290)
|
||||
- `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const
|
||||
- Removed public fields from `Rect` iterators
|
||||
@@ -76,6 +77,28 @@ This is a quick summary of the sections below:
|
||||
|
||||
## Unreleased (0.30.0)
|
||||
|
||||
### `FrameExt` trait for `unstable-widget-ref` feature ([#1530])
|
||||
|
||||
[#1530]: https://github.com/ratatui/ratatui/pull/1530
|
||||
|
||||
To call `Frame::render_widget_ref()` or `Frame::render_stateful_widget_ref()` you now need to:
|
||||
|
||||
1. Import the `FrameExt` trait from `ratatui::widgets`.
|
||||
2. Enable the `unstable-widget-ref` feature.
|
||||
|
||||
For example:
|
||||
|
||||
```rust
|
||||
use ratatui::{
|
||||
layout::Rect,
|
||||
widgets::{Block, FrameExt},
|
||||
};
|
||||
|
||||
let block = Block::new();
|
||||
let area = Rect::new(0, 0, 5, 5);
|
||||
frame.render_widget_ref(&block, area);
|
||||
```
|
||||
|
||||
### `WidgetRef` no longer has a blanket implementation of Widget
|
||||
|
||||
Previously there was a blanket implementation of Widget for WidgetRef. This has been reversed to
|
||||
|
||||
238
CHANGELOG.md
238
CHANGELOG.md
@@ -1,8 +1,220 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
<!-- ignore lint rules that are often triggered by content generated from commits / git-cliff -->
|
||||
<!-- markdownlint-disable line-length no-bare-urls ul-style emphasis-style -->
|
||||
|
||||
_"Food will come, Remy. Food always comes to those who love to cook." – Gusteau_
|
||||
## [0.30.0-alpha.1](https://github.com/ratatui/ratatui/releases/tag/0.30.0-alpha.1) - 2025-01-14
|
||||
|
||||
0.30.0-alpha.1 is a pre-release of the upcoming 0.30.0 release. It is intended for testing and
|
||||
feedback. Please report any issues you encounter. The primary change is that we've split the crate
|
||||
into multiple crates. The main crate is now `ratatui-core` and the widgets are in `ratatui-widgets`.
|
||||
This allows for widget crates to depend on a stable core crate, and for the core crate to be used
|
||||
without widgets.
|
||||
|
||||
### Features
|
||||
|
||||
- [56d5e05](https://github.com/ratatui/ratatui/commit/56d5e057625378f1bdf732dabb57208453fb84d6) *(bar)* Update label and text_value to accept Into<> by @Emivvvvv in [#1471](https://github.com/ratatui/ratatui/pull/1471) [**breaking**]
|
||||
|
||||
- [b76ad3b](https://github.com/ratatui/ratatui/commit/b76ad3b02ea1c3381b6434c40e620f28d642948e) *(bar)* Impl Styled for Bar by @Emivvvvv in [#1476](https://github.com/ratatui/ratatui/pull/1476)
|
||||
|
||||
- [369b18e](https://github.com/ratatui/ratatui/commit/369b18eef2e4220147e7c7264ad7f8e023a1d2dd) *(barchart)* Reduce barchart creation verbosity by @Emivvvvv in [#1453](https://github.com/ratatui/ratatui/pull/1453)
|
||||
|
||||
- [9275d34](https://github.com/ratatui/ratatui/commit/9275d3421c088174bcf9de0832340bcbea76367a) *(layout)* Add Offset::new() constructor by @joshka in [#1547](https://github.com/ratatui/ratatui/pull/1547)
|
||||
|
||||
- [ff729b7](https://github.com/ratatui/ratatui/commit/ff729b7607e0099a155f10dfe0ce42320641b74d) *(scrollbar)* Support retrieving the current position of state by @orhun in [#1552](https://github.com/ratatui/ratatui/pull/1552)
|
||||
|
||||
- [ce4856a](https://github.com/ratatui/ratatui/commit/ce4856a65f3c76db714a45338ba3be9b638c6c35) *(widgets)* Add the missing constructor to canvas types by @orhun in [#1538](https://github.com/ratatui/ratatui/pull/1538)
|
||||
|
||||
- [50ba965](https://github.com/ratatui/ratatui/commit/50ba96518f01454cff185784a1e9676093ce06cf) *(uncategorized)* Add a new RatatuiMascot widget by @Its-Just-Nans in [#1584](https://github.com/ratatui/ratatui/pull/1584)
|
||||
|
||||
- [1d28c89](https://github.com/ratatui/ratatui/commit/1d28c89fe50f1a11cf1719233acca1a12d5e2d7b) *(uncategorized)* Add conversions for anstyle by @joshka in [#1581](https://github.com/ratatui/ratatui/pull/1581)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [860e48b](https://github.com/ratatui/ratatui/commit/860e48b0f0ec51c8596bc12985a8b37bad4fba00) *(buffer)* Buffer::get_pos() now correctly handles index > u16::MAX by @joshka in [#1447](https://github.com/ratatui/ratatui/pull/1447)
|
||||
|
||||
- [ec30390](https://github.com/ratatui/ratatui/commit/ec30390446b998cba97a25db63b2e3d27db7a12d) *(canvas)* Round coordinates to nearest grid cell by @joshka in [#1507](https://github.com/ratatui/ratatui/pull/1507)
|
||||
|
||||
- [afd1ce1](https://github.com/ratatui/ratatui/commit/afd1ce179b74f10ea786ed6f9b2999288bc94e7a) *(canvas)* Lines that start outside the visible grid are now drawn by @renesat in [#1501](https://github.com/ratatui/ratatui/pull/1501)
|
||||
|
||||
- [18e70d3](https://github.com/ratatui/ratatui/commit/18e70d3d51b654bb895843224edf0c21a3114dc9) *(crossterm)* Terminal should keep Bold when removing Dim by @MarSik in [#1541](https://github.com/ratatui/ratatui/pull/1541)
|
||||
|
||||
- [a692a6e](https://github.com/ratatui/ratatui/commit/a692a6e37114f39a3eb15b0cdc486dee8a7e91ff) *(lint)* Apply rust 1.84 clippy suggestions by @joshka in [#1612](https://github.com/ratatui/ratatui/pull/1612)
|
||||
|
||||
- [3b13240](https://github.com/ratatui/ratatui/commit/3b13240728597a8c459c4bb5f913372185e9df91) *(scrollbar)* Check for area.is_empty() before rendering by @farmeroy in [#1529](https://github.com/ratatui/ratatui/pull/1529)
|
||||
|
||||
- [f57b696](https://github.com/ratatui/ratatui/commit/f57b696fdc3bac381bccda0414400453f5abecf2) *(span)* Dont render control characters by @EdJoPaTo in [#1312](https://github.com/ratatui/ratatui/pull/1312)
|
||||
|
||||
- [2ce958e](https://github.com/ratatui/ratatui/commit/2ce958e38cf1ebab49b1817b669631dc349a0ebb) *(table)* Allow display of additional table row, if row height > 1 by @Lunderberg in [#1452](https://github.com/ratatui/ratatui/pull/1452)
|
||||
|
||||
- [2892bdd](https://github.com/ratatui/ratatui/commit/2892bddce66daf0b285a6d9f6fb3fdb2581cda2b) *(uncategorized)* Rust 1.83 clippy lints by @joshka in [#1527](https://github.com/ratatui/ratatui/pull/1527)
|
||||
|
||||
- [36e2d1b](https://github.com/ratatui/ratatui/commit/36e2d1bda1d8b4e1d0fbd09ba2894d56bd7eeeca) *(uncategorized)* Add feature(doc_cfg) when generating docs by @joshka in [#1506](https://github.com/ratatui/ratatui/pull/1506)
|
||||
|
||||
- [4d7704f](https://github.com/ratatui/ratatui/commit/4d7704fba5dcebda34c8c04bf4b5ac48e3a22008) *(uncategorized)* Make StatefulWidget and Ref work with unsized State by @thscharler in [#1505](https://github.com/ratatui/ratatui/pull/1505)
|
||||
|
||||
- [7b87509](https://github.com/ratatui/ratatui/commit/7b875091e18b894f53af6331a8ad5b7101a75d1e) *(uncategorized)* Typo by @marcoieni in [#1480](https://github.com/ratatui/ratatui/pull/1480)
|
||||
|
||||
### Refactor
|
||||
|
||||
- [f132fa1](https://github.com/ratatui/ratatui/commit/f132fa1715ea0893c52e35c65d505abadb75cec6) *(table)* Small readability improvements by @joshka in [#1510](https://github.com/ratatui/ratatui/pull/1510)
|
||||
|
||||
- [904b0aa](https://github.com/ratatui/ratatui/commit/904b0aa723e63d3fb68802b797949619506bf3a9) *(uncategorized)* Move symbols to modules by @joshka in [#1594](https://github.com/ratatui/ratatui/pull/1594)
|
||||
|
||||
- [7c8573f](https://github.com/ratatui/ratatui/commit/7c8573f5750ea8cf87101f81314803c834ea4942) *(uncategorized)* Rearrange selection_spacing code by @raylu in [#1540](https://github.com/ratatui/ratatui/pull/1540)
|
||||
|
||||
- [217c57c](https://github.com/ratatui/ratatui/commit/217c57cd60628abde1ca2f0c39b014e22c9edc4e) *(uncategorized)* Modularize backends by @orhun in [#1508](https://github.com/ratatui/ratatui/pull/1508)
|
||||
|
||||
- [e461b72](https://github.com/ratatui/ratatui/commit/e461b724a6b010fe242f9bd6d9746007cbf24219) *(uncategorized)* Move {Stateful,}Widget{,Ref} types into individual files by @joshka in [#1479](https://github.com/ratatui/ratatui/pull/1479)
|
||||
|
||||
### Documentation
|
||||
|
||||
- [d291042](https://github.com/ratatui/ratatui/commit/d291042e69ad930ae0d3c5d1f991d9e68320c00e) *(block)* Revise the block example by @orhun in [#1520](https://github.com/ratatui/ratatui/pull/1520)
|
||||
|
||||
- [fcde9cb](https://github.com/ratatui/ratatui/commit/fcde9cb9c3c5e9752fefbcc7cdeac95e2db9d684) *(changelog)* Fix typo by @orhun in [#1463](https://github.com/ratatui/ratatui/pull/1463)
|
||||
|
||||
- [3ae6bf1](https://github.com/ratatui/ratatui/commit/3ae6bf1d6f24407de400024dcb5924d841a2e1ba) *(contributing)* Use cargo-xtask for instructions by @orhun in [#1509](https://github.com/ratatui/ratatui/pull/1509)
|
||||
|
||||
- [04e1b32](https://github.com/ratatui/ratatui/commit/04e1b32cd2e77653266fa8e7269a2e08d774220e) *(layout)* Rename cassowary-rs references to cassowary by @miroim in [#1448](https://github.com/ratatui/ratatui/pull/1448)
|
||||
|
||||
- [088aac1](https://github.com/ratatui/ratatui/commit/088aac136d8b22557081ab4c657f5c9f5cde80d8) *(readme)* Tweak links and badges by @joshka in [#1598](https://github.com/ratatui/ratatui/pull/1598)
|
||||
|
||||
- [6e43672](https://github.com/ratatui/ratatui/commit/6e436725e4f8c58be527b07c464a0d8b5d306eb6) *(readme)* Reimagine README.md by @orhun in [#1569](https://github.com/ratatui/ratatui/pull/1569)
|
||||
|
||||
- [8f28247](https://github.com/ratatui/ratatui/commit/8f282473b21d7693f20b5f0cdad2f12b9ed209ff) *(readme)* Correct examples links by @HoKim98 in [#1484](https://github.com/ratatui/ratatui/pull/1484)
|
||||
|
||||
- [9f90f74](https://github.com/ratatui/ratatui/commit/9f90f7495fd46b3b7ac34160f094bc1583f82b70) *(readme)* Fix broken link by @nilsmartel in [#1485](https://github.com/ratatui/ratatui/pull/1485)
|
||||
|
||||
- [260af68](https://github.com/ratatui/ratatui/commit/260af68a347b527281265f7bf259eb1336aa49b2) *(readme)* Include iocraft as an alternative by @kdheepak in [#1483](https://github.com/ratatui/ratatui/pull/1483)
|
||||
|
||||
- [dafb716](https://github.com/ratatui/ratatui/commit/dafb716f9d8acb5dda303dc1e155dc90eb54b926) *(widgets)* Add example for grouped barchart by @orhun in [#1566](https://github.com/ratatui/ratatui/pull/1566)
|
||||
|
||||
- [ed5dd73](https://github.com/ratatui/ratatui/commit/ed5dd73084e2c8ccf3d36bc23048d67755a90e6a) *(widgets)* Add example for tabs by @orhun in [#1559](https://github.com/ratatui/ratatui/pull/1559)
|
||||
|
||||
- [fab5321](https://github.com/ratatui/ratatui/commit/fab532171d1c8e9639298550565e4eefb8737275) *(widgets)* Add example for scrollbar by @orhun in [#1545](https://github.com/ratatui/ratatui/pull/1545)
|
||||
|
||||
- [898aef6](https://github.com/ratatui/ratatui/commit/898aef6e2f38655e58684a2d842f03bcde0ebf0c) *(widgets)* Add example for list by @orhun in [#1542](https://github.com/ratatui/ratatui/pull/1542)
|
||||
|
||||
- [452366a](https://github.com/ratatui/ratatui/commit/452366aa9e666f26946ebccf6046a0bed393f5c1) *(widgets)* Add example for sparkline by @orhun in [#1556](https://github.com/ratatui/ratatui/pull/1556)
|
||||
|
||||
- [6ddde0e](https://github.com/ratatui/ratatui/commit/6ddde0e8a84b0909ba5631afac2dfc7878764786) *(widgets)* Add example for table by @orhun in [#1557](https://github.com/ratatui/ratatui/pull/1557)
|
||||
|
||||
- [93ad6b8](https://github.com/ratatui/ratatui/commit/93ad6b828c3a34be982447390d17b7953a9c328e) *(widgets)* Update values in chart example by @orhun in [#1558](https://github.com/ratatui/ratatui/pull/1558)
|
||||
|
||||
- [15f442a](https://github.com/ratatui/ratatui/commit/15f442a71ed4ce3faeaac3b2a3a7798940eb1846) *(widgets)* Add example for paragraph by @orhun in [#1544](https://github.com/ratatui/ratatui/pull/1544)
|
||||
|
||||
- [17bba14](https://github.com/ratatui/ratatui/commit/17bba14540449ae584a9cafbe2a39cc7fa451ef4) *(widgets)* Move the logo example to widgets by @orhun in [#1543](https://github.com/ratatui/ratatui/pull/1543)
|
||||
|
||||
- [f2451e7](https://github.com/ratatui/ratatui/commit/f2451e7f1ed1d5bd38e8901f139bc2916a4b005e) *(widgets)* Add example for gauge by @orhun in [#1539](https://github.com/ratatui/ratatui/pull/1539)
|
||||
|
||||
- [4f0a8b2](https://github.com/ratatui/ratatui/commit/4f0a8b21af49b825debb13695f8c1f368f4f56b5) *(widgets)* Add example for canvas by @orhun in [#1533](https://github.com/ratatui/ratatui/pull/1533)
|
||||
|
||||
- [91147c4](https://github.com/ratatui/ratatui/commit/91147c4d75bee207052b06a7dca4b610df321de1) *(widgets)* Add example for chart by @orhun in [#1536](https://github.com/ratatui/ratatui/pull/1536)
|
||||
|
||||
- [6dd25a3](https://github.com/ratatui/ratatui/commit/6dd25a311152abf3e0938c9a59e54c98e2a327a6) *(widgets)* Add example for calendar by @orhun in [#1532](https://github.com/ratatui/ratatui/pull/1532)
|
||||
|
||||
- [99ac005](https://github.com/ratatui/ratatui/commit/99ac005b06f807c79060dba43b33ec9b1f7c1a3a) *(widgets)* Add simple barchart example by @joshka in [#1511](https://github.com/ratatui/ratatui/pull/1511)
|
||||
|
||||
- [da05957](https://github.com/ratatui/ratatui/commit/da05957fa01fcc420519400d82ef9df6acace0e8) *(uncategorized)* Add widget-ref-container example by @joshka in [#1603](https://github.com/ratatui/ratatui/pull/1603)
|
||||
|
||||
- [1798512](https://github.com/ratatui/ratatui/commit/1798512e94b277fdfafeddb4043f7174ce2ac64a) *(uncategorized)* Fix wording in user_input example by @dawedawe in [#1611](https://github.com/ratatui/ratatui/pull/1611)
|
||||
|
||||
- [03066d8](https://github.com/ratatui/ratatui/commit/03066d81bfc7fb4356e8d188f3d6a15b45509c15) *(uncategorized)* Fix punctuation in canvas.rs documentation by @dawedawe in [#1583](https://github.com/ratatui/ratatui/pull/1583)
|
||||
|
||||
- [e411d9e](https://github.com/ratatui/ratatui/commit/e411d9ec3e921e812b7e8d2836c2ad0b60a0e6f8) *(uncategorized)* Add input form example by @joshka in [#1551](https://github.com/ratatui/ratatui/pull/1551)
|
||||
|
||||
- [ed071f3](https://github.com/ratatui/ratatui/commit/ed071f37232fae47a2193235d57934cc5c678baa) *(uncategorized)* Add mouse-drawing example by @joshka in [#1546](https://github.com/ratatui/ratatui/pull/1546)
|
||||
|
||||
- [46902f5](https://github.com/ratatui/ratatui/commit/46902f5587efe2b27c39b5e3c39109c62a636ba3) *(uncategorized)* Improve docs for workspace crates by @orhun in [#1490](https://github.com/ratatui/ratatui/pull/1490)
|
||||
|
||||
- [a6b5792](https://github.com/ratatui/ratatui/commit/a6b579223fd83c36e024428df49a3027cd1c21bc) *(uncategorized)* Fix example link in readme by @thomas-tacquet in [#1462](https://github.com/ratatui/ratatui/pull/1462)
|
||||
|
||||
### Miscellaneous Tasks
|
||||
|
||||
- [abe2f27](https://github.com/ratatui/ratatui/commit/abe2f273289d3798968a645a38a7a38571530065) *(backend)* Change From<T> impls to new backend specific IntoBackend and FromBackend traits by @joshka in [#1464](https://github.com/ratatui/ratatui/pull/1464) [**breaking**]
|
||||
|
||||
- [0a47ebd](https://github.com/ratatui/ratatui/commit/0a47ebd94bbc7a89c9e2aa893cf13a988756ec19) *(bencher)* Update bencher CLI usage by @epompeii in [#1470](https://github.com/ratatui/ratatui/pull/1470)
|
||||
|
||||
- [a0979d6](https://github.com/ratatui/ratatui/commit/a0979d68715b2c67b32b8909189bc103f0e81f6e) *(build)* Remove cargo lint by @joshka in [#1549](https://github.com/ratatui/ratatui/pull/1549)
|
||||
|
||||
- [eaa4038](https://github.com/ratatui/ratatui/commit/eaa403856ecb60338619e2e727a0388187ae017c) *(ci)* Install pre-built binaries for cargo-rdme by @orhun in [#1477](https://github.com/ratatui/ratatui/pull/1477)
|
||||
|
||||
- [e5e2316](https://github.com/ratatui/ratatui/commit/e5e2316451fb4c085f205b4884cc82ba8a4930e6) *(ci)* Add check for keeping README.md up-to-date by @orhun in [#1473](https://github.com/ratatui/ratatui/pull/1473)
|
||||
|
||||
- [2ef3583](https://github.com/ratatui/ratatui/commit/2ef3583effdeb6492d76d977177b4363129a8c8c) *(ci)* Replace cargo-make with a custom cargo-xtask by @joshka in [#1461](https://github.com/ratatui/ratatui/pull/1461)
|
||||
|
||||
- [98df774](https://github.com/ratatui/ratatui/commit/98df774d7f9b69a2a474d25adf38e38a428f1b77) *(core)* Move core types to ratatui-core by @joshka in [#1460](https://github.com/ratatui/ratatui/pull/1460)
|
||||
|
||||
- [35eba76](https://github.com/ratatui/ratatui/commit/35eba76b4dff047dde2d1e01bc427eb1b992b490) *(example)* Move demo2 to top level folder by @joshka in [#1524](https://github.com/ratatui/ratatui/pull/1524)
|
||||
|
||||
- [5f57d35](https://github.com/ratatui/ratatui/commit/5f57d3523436e944b60ccc7a937d69336c9f82b8) *(examples)* Add colors explorer demo app by @orhun in [#1580](https://github.com/ratatui/ratatui/pull/1580)
|
||||
|
||||
- [5c021bf](https://github.com/ratatui/ratatui/commit/5c021bf344b17fc4075e9663dfb270bead180e25) *(examples)* Add chart demo app by @orhun in [#1579](https://github.com/ratatui/ratatui/pull/1579)
|
||||
|
||||
- [9721300](https://github.com/ratatui/ratatui/commit/9721300a473096daf6b34f6cc7c13643d61a4e00) *(examples)* Add canvas demo app by @orhun in [#1578](https://github.com/ratatui/ratatui/pull/1578)
|
||||
|
||||
- [a6a1368](https://github.com/ratatui/ratatui/commit/a6a13682507846320a79538ffad673a58c1143f0) *(examples)* Add calendar explorer demo app by @orhun in [#1571](https://github.com/ratatui/ratatui/pull/1571)
|
||||
|
||||
- [819e92c](https://github.com/ratatui/ratatui/commit/819e92cd44b6bee7d21115ff465c2f3f8c82ed9b) *(examples)* Add weather demo app by @orhun in [#1567](https://github.com/ratatui/ratatui/pull/1567)
|
||||
|
||||
- [b5f7e44](https://github.com/ratatui/ratatui/commit/b5f7e4418364c9710d14567c73122af67e0a63ae) *(examples)* Move async example to apps by @joshka in [#1503](https://github.com/ratatui/ratatui/pull/1503)
|
||||
|
||||
- [17316ec](https://github.com/ratatui/ratatui/commit/17316ec5d0a4807600dd116736d66938b985e718) *(github)* Enable sponsorship button by @orhun in [#1478](https://github.com/ratatui/ratatui/pull/1478)
|
||||
|
||||
- [d3f01eb](https://github.com/ratatui/ratatui/commit/d3f01ebf6ea97e71bcda8c84b054943e1f24cd4e) *(lint)* Ensure lint config is correct by @joshka in [#1528](https://github.com/ratatui/ratatui/pull/1528)
|
||||
|
||||
- [2b7ec5c](https://github.com/ratatui/ratatui/commit/2b7ec5cb7f34edb65fc81d362d3b512b98d246ac) *(widgets)* Enable calendar widget as default by @orhun in [#1521](https://github.com/ratatui/ratatui/pull/1521)
|
||||
|
||||
- [d201b8e](https://github.com/ratatui/ratatui/commit/d201b8e5ddd98a1887252179dec8f09e1f342b0c) *(xtask)* Check lints for only library targets by @orhun in [#1531](https://github.com/ratatui/ratatui/pull/1531)
|
||||
|
||||
- [6f21319](https://github.com/ratatui/ratatui/commit/6f213191efd528fd7e0d5c99fda3e6d028ee0f98) *(uncategorized)* Rename examples with clashing names by @joshka in [#1597](https://github.com/ratatui/ratatui/pull/1597)
|
||||
|
||||
- [11cbb2b](https://github.com/ratatui/ratatui/commit/11cbb2ba87e557a04674973a39d59124d8683ed1) *(uncategorized)* Use cargo xtask for bacon clippy command by @joshka in [#1592](https://github.com/ratatui/ratatui/pull/1592)
|
||||
|
||||
- [b544e39](https://github.com/ratatui/ratatui/commit/b544e394c97fffdeddaccd44bea2214ebffa1616) *(uncategorized)* Use clap instead of argh for demo example by @joshka in [#1591](https://github.com/ratatui/ratatui/pull/1591)
|
||||
|
||||
- [9a54198](https://github.com/ratatui/ratatui/commit/9a541981b8accd9efe17c0893d18b520bc569b15) *(uncategorized)* Make source files non-executable by @orhun in [#1577](https://github.com/ratatui/ratatui/pull/1577)
|
||||
|
||||
- [357ae7e](https://github.com/ratatui/ratatui/commit/357ae7e251721f2e7fcb539de5e6fc60eaa30e29) *(uncategorized)* Move terminal types to ratatui-core by @joshka in [#1530](https://github.com/ratatui/ratatui/pull/1530) [**breaking**]
|
||||
|
||||
- [21e62d8](https://github.com/ratatui/ratatui/commit/21e62d84c2d2fa2d5563caf0c0974c2514e56ee5) *(uncategorized)* Move the demo example to main folder by @joshka in [#1523](https://github.com/ratatui/ratatui/pull/1523)
|
||||
|
||||
- [fbf6050](https://github.com/ratatui/ratatui/commit/fbf6050c867b63276ae3d6f5bca4d741c2ce355c) *(uncategorized)* Prepare alpha modularization release by @joshka in [#1525](https://github.com/ratatui/ratatui/pull/1525)
|
||||
|
||||
- [e4e95bc](https://github.com/ratatui/ratatui/commit/e4e95bcecf15deb09416a0e53193d261f012222a) *(uncategorized)* Remove --color always flags from bacon.toml by @joshka in [#1502](https://github.com/ratatui/ratatui/pull/1502)
|
||||
|
||||
- [a41c97b](https://github.com/ratatui/ratatui/commit/a41c97b413b28d0db6d1ea09dcc1d5b8556148b1) *(uncategorized)* Move unstable widget refs to ratatui by @joshka in [#1491](https://github.com/ratatui/ratatui/pull/1491) [**breaking**]
|
||||
|
||||
- [e7085e3](https://github.com/ratatui/ratatui/commit/e7085e3a3ec4b3b90a4e69d49add96e7ba65616c) *(uncategorized)* Move widgets into ratatui-widgets crate by @joshka in [#1474](https://github.com/ratatui/ratatui/pull/1474)
|
||||
|
||||
- [f1d0a18](https://github.com/ratatui/ratatui/commit/f1d0a1837564d69f00e4b5d9eb94cc001cd3a3a7) *(uncategorized)* Move ratatui crate into workspace folder by @joshka in [#1459](https://github.com/ratatui/ratatui/pull/1459)
|
||||
|
||||
- [55fb2d2](https://github.com/ratatui/ratatui/commit/55fb2d2e56b492f0f4131fde9d44951b504cf50c) *(uncategorized)* Update repo links to ratatui instead of ratatui-org by @joshka in [#1458](https://github.com/ratatui/ratatui/pull/1458)
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
- [4a871f9](https://github.com/ratatui/ratatui/commit/4a871f993ea38069da513660707a072be299b791) *(uncategorized)* Refactor xtask / toml formatting by @joshka in [#1602](https://github.com/ratatui/ratatui/pull/1602)
|
||||
|
||||
### New Contributors
|
||||
|
||||
* @dawedawe made their first contribution in [#1611](https://github.com/ratatui/ratatui/pull/1611)
|
||||
* @Its-Just-Nans made their first contribution in [#1584](https://github.com/ratatui/ratatui/pull/1584)
|
||||
* @MarSik made their first contribution in [#1541](https://github.com/ratatui/ratatui/pull/1541)
|
||||
* @raylu made their first contribution in [#1540](https://github.com/ratatui/ratatui/pull/1540)
|
||||
* @renesat made their first contribution in [#1501](https://github.com/ratatui/ratatui/pull/1501)
|
||||
* @HoKim98 made their first contribution in [#1484](https://github.com/ratatui/ratatui/pull/1484)
|
||||
* @nilsmartel made their first contribution in [#1485](https://github.com/ratatui/ratatui/pull/1485)
|
||||
* @marcoieni made their first contribution in [#1480](https://github.com/ratatui/ratatui/pull/1480)
|
||||
* @epompeii made their first contribution in [#1470](https://github.com/ratatui/ratatui/pull/1470)
|
||||
* @thomas-tacquet made their first contribution in [#1462](https://github.com/ratatui/ratatui/pull/1462)
|
||||
* @miroim made their first contribution in [#1448](https://github.com/ratatui/ratatui/pull/1448)
|
||||
|
||||
**Full Changelog**: https://github.com/ratatui/ratatui/compare/v0.29.0...0.30.0-alpha.1
|
||||
|
||||
## [v0.29.0](https://github.com/ratatui/ratatui/releases/tag/v0.29.0) - 2024-10-21
|
||||
|
||||
> _"Food will come, Remy. Food always comes to those who love to cook."_ – Gusteau
|
||||
|
||||
We are excited to announce the new version of `ratatui` - a Rust library that's all about cooking up TUIs 👨🍳🐀
|
||||
|
||||
@@ -10,8 +222,6 @@ We are excited to announce the new version of `ratatui` - a Rust library that's
|
||||
|
||||
⚠️ List of breaking changes can be found [here](https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md).
|
||||
|
||||
## [v0.29.0](https://github.com/ratatui/ratatui/releases/tag/v0.29.0) - 2024-10-21
|
||||
|
||||
### Features
|
||||
|
||||
- [3a43274](https://github.com/ratatui/ratatui/commit/3a43274881a79b4e593536c2ca915b509e557215) *(color)* Add hsluv support by @du-ob in [#1333](https://github.com/ratatui/ratatui/pull/1333)
|
||||
@@ -433,6 +643,7 @@ We are excited to announce the new version of `ratatui` - a Rust library that's
|
||||
> <https://github.com/ratatui/ratatui/issues/738>
|
||||
>
|
||||
> To update your code:
|
||||
>
|
||||
> ```rust
|
||||
>
|
||||
> Block::new().title(Title::from("foo"));
|
||||
@@ -483,17 +694,18 @@ We are excited to announce the new version of `ratatui` - a Rust library that's
|
||||
> Given:```rust
|
||||
>
|
||||
> Text::from_iter([
|
||||
> Line::from("without line fields"),
|
||||
> Line::from("with line fields").bold().centered(),
|
||||
> Line::from_iter([
|
||||
> Span::from("without span fields"),
|
||||
> Span::from("with span fields")
|
||||
> .green()
|
||||
> .on_black()
|
||||
> .italic()
|
||||
> .not_dim(),
|
||||
> ]),
|
||||
> Line::from("without line fields"),
|
||||
> Line::from("with line fields").bold().centered(),
|
||||
> Line::from_iter([
|
||||
> Span::from("without span fields"),
|
||||
> Span::from("with span fields")
|
||||
> .green()
|
||||
> .on_black()
|
||||
> .italic()
|
||||
> .not_dim(),
|
||||
> ]),
|
||||
> ])
|
||||
>
|
||||
> ```
|
||||
>
|
||||
> Debug:```
|
||||
|
||||
689
Cargo.lock
generated
689
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
82
Cargo.toml
82
Cargo.toml
@@ -1,14 +1,15 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["ratatui", "ratatui-*", "xtask"]
|
||||
members = ["ratatui", "ratatui-*", "xtask", "examples/apps/*"]
|
||||
default-members = [
|
||||
"ratatui",
|
||||
"ratatui-core",
|
||||
"ratatui-crossterm",
|
||||
# this is not included as it doesn't compile on windows
|
||||
# "ratatui-termion",
|
||||
"ratatui-termwiz",
|
||||
"ratatui-widgets",
|
||||
"ratatui",
|
||||
"ratatui-core",
|
||||
"ratatui-crossterm",
|
||||
# this is not included as it doesn't compile on windows
|
||||
# "ratatui-termion",
|
||||
"ratatui-termwiz",
|
||||
"ratatui-widgets",
|
||||
"examples/apps/*",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
@@ -25,31 +26,72 @@ edition = "2021"
|
||||
rust-version = "1.74.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
bitflags = "2.6.0"
|
||||
bitflags = "2.7.0"
|
||||
color-eyre = "0.6.3"
|
||||
crossterm = "0.28.1"
|
||||
document-features = "0.2.7"
|
||||
indoc = "2.0.5"
|
||||
instability = "0.3.3"
|
||||
instability = "0.3.7"
|
||||
itertools = "0.13.0"
|
||||
pretty_assertions = "1.4.1"
|
||||
ratatui = { path = "ratatui", version = "0.30.0-alpha.0" }
|
||||
ratatui-core = { path = "ratatui-core", version = "0.1.0-alpha.0" }
|
||||
ratatui-crossterm = { path = "ratatui-crossterm", version = "0.1.0-alpha.0" }
|
||||
ratatui-termion = { path = "ratatui-termion", version = "0.1.0-alpha.0" }
|
||||
ratatui-termwiz = { path = "ratatui-termwiz", version = "0.1.0-alpha.0" }
|
||||
ratatui-widgets = { path = "ratatui-widgets", version = "0.3.0-alpha.0" }
|
||||
rstest = "0.23.0"
|
||||
serde = { version = "1.0.215", features = ["derive"] }
|
||||
serde_json = "1.0.133"
|
||||
ratatui = { path = "ratatui", version = "0.30.0-alpha.1" }
|
||||
ratatui-core = { path = "ratatui-core", version = "0.1.0-alpha.2" }
|
||||
ratatui-crossterm = { path = "ratatui-crossterm", version = "0.1.0-alpha.1" }
|
||||
ratatui-termion = { path = "ratatui-termion", version = "0.1.0-alpha.1" }
|
||||
ratatui-termwiz = { path = "ratatui-termwiz", version = "0.1.0-alpha.1" }
|
||||
ratatui-widgets = { path = "ratatui-widgets", version = "0.3.0-alpha.1" }
|
||||
rstest = "0.24.0"
|
||||
serde = { version = "1.0.217", features = ["derive"] }
|
||||
serde_json = "1.0.135"
|
||||
strum = { version = "0.26.3", features = ["derive"] }
|
||||
termwiz = { version = "0.22.0" }
|
||||
unicode-segmentation = "1.12.0"
|
||||
# See <https://github.com/ratatui/ratatui/issues/1271> for information about why we pin unicode-width
|
||||
unicode-width = "=0.2.0"
|
||||
termion = "4.0.0"
|
||||
unicode-width = "=0.2.0"
|
||||
|
||||
# Improve benchmark consistency
|
||||
[profile.bench]
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
|
||||
[workspace.lints.rust]
|
||||
unsafe_code = "forbid"
|
||||
|
||||
[workspace.lints.clippy]
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
cast_possible_truncation = "allow"
|
||||
cast_possible_wrap = "allow"
|
||||
cast_precision_loss = "allow"
|
||||
cast_sign_loss = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
module_name_repetitions = "allow"
|
||||
must_use_candidate = "allow"
|
||||
|
||||
# we often split up a module into multiple files with the main type in a file named after the
|
||||
# module, so we want to allow this pattern
|
||||
module_inception = "allow"
|
||||
|
||||
# nursery or restricted
|
||||
as_underscore = "warn"
|
||||
deref_by_slicing = "warn"
|
||||
else_if_without_else = "warn"
|
||||
empty_line_after_doc_comments = "warn"
|
||||
equatable_if_let = "warn"
|
||||
fn_to_numeric_cast_any = "warn"
|
||||
format_push_string = "warn"
|
||||
map_err_ignore = "warn"
|
||||
missing_const_for_fn = "warn"
|
||||
mixed_read_write_in_expression = "warn"
|
||||
mod_module_files = "warn"
|
||||
needless_pass_by_ref_mut = "warn"
|
||||
needless_raw_strings = "warn"
|
||||
or_fun_call = "warn"
|
||||
redundant_type_annotations = "warn"
|
||||
rest_pat_in_fully_bound_structs = "warn"
|
||||
string_lit_chars_any = "warn"
|
||||
string_slice = "warn"
|
||||
string_to_string = "warn"
|
||||
unnecessary_self_imports = "warn"
|
||||
use_self = "warn"
|
||||
|
||||
444
README.md
444
README.md
@@ -1,380 +1,170 @@
|
||||
<details>
|
||||
<summary>Table of Contents</summary>
|
||||
|
||||
- [Ratatui](#ratatui)
|
||||
- [Quick Start](#quickstart)
|
||||
- [Other documentation](#other-documentation)
|
||||
- [Introduction](#introduction)
|
||||
- [Quickstart](#quickstart)
|
||||
- [Initialize and restore the terminal](#initialize-and-restore-the-terminal)
|
||||
- [Drawing the UI](#drawing-the-ui)
|
||||
- [Handling events](#handling-events)
|
||||
- [Layout](#layout)
|
||||
- [Text and styling](#text-and-styling)
|
||||
- [Status of this fork](#status-of-this-fork)
|
||||
- [Widgets](#widgets)
|
||||
- [Built in](#built-in)
|
||||
- [Third-party libraries, bootstrapping templates and widgets](#third-party-libraries-bootstrapping-templates-and-widgets)
|
||||
- [Apps](#apps)
|
||||
- [Alternatives](#alternatives)
|
||||
- [Acknowledgments](#acknowledgments)
|
||||
- [License](#license)
|
||||
- [Quickstart](#quickstart)
|
||||
- [Documentation](#documentation)
|
||||
- [Templates](#templates)
|
||||
- [Built with Ratatui](#built-with-ratatui)
|
||||
- [Alternatives](#alternatives)
|
||||
- [Contributing](#contributing)
|
||||
- [Acknowledgements](#acknowledgements)
|
||||
- [License](#license)
|
||||
|
||||
</details>
|
||||
|
||||
<!-- cargo-rdme start -->
|
||||
|
||||

|
||||
|
||||
<div align="center">
|
||||
|
||||
[![Crate Badge]][Crate] [![Docs Badge]][API Docs] [![CI Badge]][CI Workflow] [![Deps.rs
|
||||
Badge]][Deps.rs]<br> [![Codecov Badge]][Codecov] [![License Badge]](./LICENSE) [![Sponsors
|
||||
Badge]][GitHub Sponsors]<br> [![Discord Badge]][Discord Server] [![Matrix Badge]][Matrix]
|
||||
[![Forum Badge]][Forum]<br>
|
||||
|
||||
[Ratatui Website] · [API Docs] · [Examples] · [Changelog] · [Breaking Changes]<br>
|
||||
[Contributing] · [Report a bug] · [Request a Feature] · [Create a Pull Request]
|
||||
[![Crate Badge]][Crate] [![Repo Badge]][Repo] [![Docs Badge]][Docs] [![License Badge]][License] \
|
||||
[![CI Badge]][CI] [![Deps Badge]][Deps] [![Codecov Badge]][Codecov] [![Sponsors Badge]][Sponsors] \
|
||||
[Ratatui Website] · [Docs] · [Widget Examples] · [App Examples] · [Changelog] \
|
||||
[Breaking Changes] · [Contributing] · [Report a bug] · [Request a Feature]
|
||||
|
||||
</div>
|
||||
|
||||
# Ratatui
|
||||
|
||||
[Ratatui][Ratatui Website] is a crate for cooking up terminal user interfaces in Rust. It is a
|
||||
lightweight library that provides a set of widgets and utilities to build complex Rust TUIs.
|
||||
Ratatui was forked from the [tui-rs] crate in 2023 in order to continue its development.
|
||||
[Ratatui][Ratatui Website] (_ˌræ.təˈtu.i_) is a Rust crate for cooking up terminal user interfaces
|
||||
(TUIs). It provides a simple and flexible way to create text-based user interfaces in the terminal,
|
||||
which can be used for command-line applications, dashboards, and other interactive console programs.
|
||||
|
||||
## Quickstart
|
||||
|
||||
Add `ratatui` and `crossterm` as dependencies to your cargo.toml:
|
||||
Ratatui has [templates] available to help you get started quickly. You can use the
|
||||
[`cargo-generate`] command to create a new project with Ratatui:
|
||||
|
||||
```shell
|
||||
cargo add ratatui crossterm
|
||||
cargo install --locked cargo-generate
|
||||
cargo generate ratatui/templates
|
||||
```
|
||||
|
||||
Then you can create a simple "Hello World" application:
|
||||
Selecting the Hello World template produces the following application:
|
||||
|
||||
```rust
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{self, Event};
|
||||
use ratatui::{text::Text, Frame};
|
||||
use ratatui::{DefaultTerminal, Frame};
|
||||
|
||||
fn main() {
|
||||
let mut terminal = ratatui::init();
|
||||
loop {
|
||||
terminal.draw(draw).expect("failed to draw frame");
|
||||
if matches!(event::read().expect("failed to read event"), Event::Key(_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ratatui::restore();
|
||||
}
|
||||
|
||||
fn draw(frame: &mut Frame) {
|
||||
let text = Text::raw("Hello World!");
|
||||
frame.render_widget(text, frame.area());
|
||||
}
|
||||
```
|
||||
|
||||
The full code for this example which contains a little more detail is in the [Examples]
|
||||
directory. For more guidance on different ways to structure your application see the
|
||||
[Application Patterns] and [Hello World tutorial] sections in the [Ratatui Website] and the
|
||||
various [Examples]. There are also several starter templates available in the [templates]
|
||||
repository.
|
||||
|
||||
## Other documentation
|
||||
|
||||
- [Ratatui Website] - explains the library's concepts and provides step-by-step tutorials
|
||||
- [Ratatui Forum][Forum] - a place to ask questions and discuss the library
|
||||
- [API Docs] - the full API documentation for the library on docs.rs.
|
||||
- [Examples] - a collection of examples that demonstrate how to use the library.
|
||||
- [Contributing] - Please read this if you are interested in contributing to the project.
|
||||
- [Changelog] - generated by [git-cliff] utilizing [Conventional Commits].
|
||||
- [Breaking Changes] - a list of breaking changes in the library.
|
||||
|
||||
You can also watch the [FOSDEM 2024 talk] about Ratatui which gives a brief introduction to
|
||||
terminal user interfaces and showcases the features of Ratatui, along with a hello world demo.
|
||||
|
||||
## Introduction
|
||||
|
||||
Ratatui is based on the principle of immediate rendering with intermediate buffers. This means
|
||||
that for each frame, your app must render all widgets that are supposed to be part of the UI.
|
||||
This is in contrast to the retained mode style of rendering where widgets are updated and then
|
||||
automatically redrawn on the next frame. See the [Rendering] section of the [Ratatui Website]
|
||||
for more info.
|
||||
|
||||
Ratatui uses [Crossterm] by default as it works on most platforms. See the [Installation]
|
||||
section of the [Ratatui Website] for more details on how to use other backends ([Termion] /
|
||||
[Termwiz]).
|
||||
|
||||
Every application built with `ratatui` needs to implement the following steps:
|
||||
|
||||
- Initialize the terminal
|
||||
- A main loop that:
|
||||
- Draws the UI
|
||||
- Handles input events
|
||||
- Restore the terminal state
|
||||
|
||||
### Initialize and restore the terminal
|
||||
|
||||
The [`Terminal`] type is the main entry point for any Ratatui application. It is generic over a
|
||||
a choice of [`Backend`] implementations that each provide functionality to draw frames, clear
|
||||
the screen, hide the cursor, etc. There are backend implementations for [Crossterm], [Termion]
|
||||
and [Termwiz].
|
||||
|
||||
The simplest way to initialize the terminal is to use the [`init`] function which returns a
|
||||
[`DefaultTerminal`] instance with the default options, enters the Alternate Screen and Raw mode
|
||||
and sets up a panic hook that restores the terminal in case of panic. This instance can then be
|
||||
used to draw frames and interact with the terminal state. (The [`DefaultTerminal`] instance is a
|
||||
type alias for a terminal with the [`crossterm`] backend.) The [`restore`] function restores the
|
||||
terminal to its original state.
|
||||
|
||||
```rust
|
||||
fn main() -> std::io::Result<()> {
|
||||
let mut terminal = ratatui::init();
|
||||
let result = run(&mut terminal);
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
```
|
||||
|
||||
See the [`backend` module] and the [Backends] section of the [Ratatui Website] for more info on
|
||||
the alternate screen and raw mode.
|
||||
|
||||
### Drawing the UI
|
||||
|
||||
Drawing the UI is done by calling the [`Terminal::draw`] method on the terminal instance. This
|
||||
method takes a closure that is called with a [`Frame`] instance. The [`Frame`] provides the size
|
||||
of the area to draw to and allows the app to render any [`Widget`] using the provided
|
||||
[`render_widget`] method. After this closure returns, a diff is performed and only the changes
|
||||
are drawn to the terminal. See the [Widgets] section of the [Ratatui Website] for more info.
|
||||
|
||||
The closure passed to the [`Terminal::draw`] method should handle the rendering of a full frame.
|
||||
|
||||
```rust
|
||||
use ratatui::{widgets::Paragraph, Frame};
|
||||
|
||||
fn run(terminal: &mut ratatui::DefaultTerminal) -> std::io::Result<()> {
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
loop {
|
||||
terminal.draw(|frame| draw(frame))?;
|
||||
if handle_events()? {
|
||||
terminal.draw(render)?;
|
||||
if matches!(event::read()?, Event::Key(_)) {
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(frame: &mut Frame) {
|
||||
let text = Paragraph::new("Hello World!");
|
||||
frame.render_widget(text, frame.area());
|
||||
fn render(frame: &mut Frame) {
|
||||
frame.render_widget("hello world", frame.area());
|
||||
}
|
||||
```
|
||||
|
||||
### Handling events
|
||||
## Documentation
|
||||
|
||||
Ratatui does not include any input handling. Instead event handling can be implemented by
|
||||
calling backend library methods directly. See the [Handling Events] section of the [Ratatui
|
||||
Website] for more info. For example, if you are using [Crossterm], you can use the
|
||||
[`crossterm::event`] module to handle events.
|
||||
- [Docs] - the full API documentation for the library on docs.rs.
|
||||
- [Ratatui Website] - explains the library's concepts and provides step-by-step tutorials.
|
||||
- [Ratatui Forum] - a place to ask questions and discuss the library.
|
||||
- [Widget Examples] - a collection of examples that demonstrate how to use the library.
|
||||
- [App Examples] - a collection of more complex examples that demonstrate how to build apps.
|
||||
- [Changelog] - generated by [git-cliff] utilizing [Conventional Commits].
|
||||
- [Breaking Changes] - a list of breaking changes in the library.
|
||||
|
||||
```rust
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
|
||||
You can also watch the [EuroRust 2024 talk] to learn about common concepts in Ratatui and what's
|
||||
possible to build with it.
|
||||
|
||||
fn handle_events() -> std::io::Result<bool> {
|
||||
match event::read()? {
|
||||
Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
|
||||
KeyCode::Char('q') => return Ok(true),
|
||||
// handle other key events
|
||||
_ => {}
|
||||
},
|
||||
// handle other events
|
||||
_ => {}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
## Templates
|
||||
|
||||
If you're looking to get started quickly, you can use one of the available templates from the
|
||||
[templates] repository using [`cargo-generate`]:
|
||||
|
||||
```shell
|
||||
cargo generate ratatui/templates
|
||||
```
|
||||
|
||||
## Layout
|
||||
|
||||
The library comes with a basic yet useful layout management object called [`Layout`] which
|
||||
allows you to split the available space into multiple areas and then render widgets in each
|
||||
area. This lets you describe a responsive terminal UI by nesting layouts. See the [Layout]
|
||||
section of the [Ratatui Website] for more info.
|
||||
|
||||
```rust
|
||||
use ratatui::{
|
||||
layout::{Constraint, Layout},
|
||||
widgets::Block,
|
||||
Frame,
|
||||
};
|
||||
|
||||
fn draw(frame: &mut Frame) {
|
||||
use Constraint::{Fill, Length, Min};
|
||||
|
||||
let vertical = Layout::vertical([Length(1), Min(0), Length(1)]);
|
||||
let [title_area, main_area, status_area] = vertical.areas(frame.area());
|
||||
let horizontal = Layout::horizontal([Fill(1); 2]);
|
||||
let [left_area, right_area] = horizontal.areas(main_area);
|
||||
|
||||
frame.render_widget(Block::bordered().title("Title Bar"), title_area);
|
||||
frame.render_widget(Block::bordered().title("Status Bar"), status_area);
|
||||
frame.render_widget(Block::bordered().title("Left"), left_area);
|
||||
frame.render_widget(Block::bordered().title("Right"), right_area);
|
||||
}
|
||||
```
|
||||
|
||||
Running this example produces the following output:
|
||||
|
||||
```text
|
||||
Title Bar───────────────────────────────────
|
||||
┌Left────────────────┐┌Right───────────────┐
|
||||
│ ││ │
|
||||
└────────────────────┘└────────────────────┘
|
||||
Status Bar──────────────────────────────────
|
||||
```
|
||||
|
||||
## Text and styling
|
||||
|
||||
The [`Text`], [`Line`] and [`Span`] types are the building blocks of the library and are used in
|
||||
many places. [`Text`] is a list of [`Line`]s and a [`Line`] is a list of [`Span`]s. A [`Span`]
|
||||
is a string with a specific style.
|
||||
|
||||
The [`style` module] provides types that represent the various styling options. The most
|
||||
important one is [`Style`] which represents the foreground and background colors and the text
|
||||
attributes of a [`Span`]. The [`style` module] also provides a [`Stylize`] trait that allows
|
||||
short-hand syntax to apply a style to widgets and text. See the [Styling Text] section of the
|
||||
[Ratatui Website] for more info.
|
||||
|
||||
```rust
|
||||
use ratatui::{
|
||||
layout::{Constraint, Layout},
|
||||
style::{Color, Modifier, Style, Stylize},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
|
||||
fn draw(frame: &mut Frame) {
|
||||
let areas = Layout::vertical([Constraint::Length(1); 4]).split(frame.area());
|
||||
|
||||
let line = Line::from(vec![
|
||||
Span::raw("Hello "),
|
||||
Span::styled(
|
||||
"World",
|
||||
Style::new()
|
||||
.fg(Color::Green)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
"!".red().on_light_yellow().italic(),
|
||||
]);
|
||||
frame.render_widget(line, areas[0]);
|
||||
|
||||
// using the short-hand syntax and implicit conversions
|
||||
let paragraph = Paragraph::new("Hello World!".red().on_white().bold());
|
||||
frame.render_widget(paragraph, areas[1]);
|
||||
|
||||
// style the whole widget instead of just the text
|
||||
let paragraph = Paragraph::new("Hello World!").style(Style::new().red().on_white());
|
||||
frame.render_widget(paragraph, areas[2]);
|
||||
|
||||
// use the simpler short-hand syntax
|
||||
let paragraph = Paragraph::new("Hello World!").blue().on_yellow();
|
||||
frame.render_widget(paragraph, areas[3]);
|
||||
}
|
||||
```
|
||||
|
||||
[Ratatui Website]: https://ratatui.rs/
|
||||
[Installation]: https://ratatui.rs/installation/
|
||||
[Rendering]: https://ratatui.rs/concepts/rendering/
|
||||
[Application Patterns]: https://ratatui.rs/concepts/application-patterns/
|
||||
[Hello World tutorial]: https://ratatui.rs/tutorials/hello-world/
|
||||
[Backends]: https://ratatui.rs/concepts/backends/
|
||||
[Widgets]: https://ratatui.rs/how-to/widgets/
|
||||
[Handling Events]: https://ratatui.rs/concepts/event-handling/
|
||||
[Layout]: https://ratatui.rs/how-to/layout/
|
||||
[Styling Text]: https://ratatui.rs/how-to/render/style-text/
|
||||
[templates]: https://github.com/ratatui/templates/
|
||||
[Examples]: https://github.com/ratatui/ratatui/tree/main/ratatui/examples/README.md
|
||||
[Report a bug]: https://github.com/ratatui/ratatui/issues/new?labels=bug&projects=&template=bug_report.md
|
||||
[Request a Feature]: https://github.com/ratatui/ratatui/issues/new?labels=enhancement&projects=&template=feature_request.md
|
||||
[Create a Pull Request]: https://github.com/ratatui/ratatui/compare
|
||||
[git-cliff]: https://git-cliff.org
|
||||
[Conventional Commits]: https://www.conventionalcommits.org
|
||||
[API Docs]: https://docs.rs/ratatui
|
||||
[Changelog]: https://github.com/ratatui/ratatui/blob/main/CHANGELOG.md
|
||||
[Contributing]: https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md
|
||||
[Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md
|
||||
[FOSDEM 2024 talk]: https://www.youtube.com/watch?v=NU0q6NOLJ20
|
||||
[`Frame`]: terminal::Frame
|
||||
[`render_widget`]: terminal::Frame::render_widget
|
||||
[`Widget`]: widgets::Widget
|
||||
[`Layout`]: layout::Layout
|
||||
[`Text`]: text::Text
|
||||
[`Line`]: text::Line
|
||||
[`Span`]: text::Span
|
||||
[`Style`]: style::Style
|
||||
[`style` module]: style
|
||||
[`Stylize`]: style::Stylize
|
||||
[`Backend`]: backend::Backend
|
||||
[`backend` module]: backend
|
||||
[`crossterm::event`]: https://docs.rs/crossterm/latest/crossterm/event/index.html
|
||||
[Crate]: https://crates.io/crates/ratatui
|
||||
[Crossterm]: https://crates.io/crates/crossterm
|
||||
[Termion]: https://crates.io/crates/termion
|
||||
[Termwiz]: https://crates.io/crates/termwiz
|
||||
[tui-rs]: https://crates.io/crates/tui
|
||||
[GitHub Sponsors]: https://github.com/sponsors/ratatui
|
||||
[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/ratatui/ci.yml?style=flat-square&logo=github
|
||||
[CI Workflow]: https://github.com/ratatui/ratatui/actions/workflows/ci.yml
|
||||
[Codecov Badge]: https://img.shields.io/codecov/c/github/ratatui/ratatui?logo=codecov&style=flat-square&token=BAQ8SOKEST&color=C43AC3&logoColor=C43AC3
|
||||
[Codecov]: https://app.codecov.io/gh/ratatui/ratatui
|
||||
[Deps.rs Badge]: https://deps.rs/repo/github/ratatui/ratatui/status.svg?style=flat-square
|
||||
[Deps.rs]: https://deps.rs/repo/github/ratatui/ratatui
|
||||
[Discord Badge]: 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&logoColor=E05D44
|
||||
[Matrix Badge]: 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
|
||||
[Forum Badge]: https://img.shields.io/discourse/likes?server=https%3A%2F%2Fforum.ratatui.rs&style=flat-square&logo=discourse&label=forum&color=C43AC3
|
||||
[Forum]: https://forum.ratatui.rs
|
||||
[Sponsors Badge]: https://img.shields.io/github/sponsors/ratatui?logo=github&style=flat-square&color=1370D3
|
||||
|
||||
<!-- cargo-rdme end -->
|
||||
|
||||
## Contributing
|
||||
|
||||
In order to organize ourselves, we currently use a [Discord server](https://discord.gg/pMCEU9hNEj),
|
||||
feel free to join and come chat! There is also a [Matrix](https://matrix.org/) bridge available at
|
||||
[#ratatui:matrix.org](https://matrix.to/#/#ratatui:matrix.org).
|
||||
|
||||
We have also recently launched the [Ratatui Forum][Forum], For bugs and features, we rely on GitHub.
|
||||
Please [Report a bug], [Request a Feature] or [Create a Pull Request].
|
||||
|
||||
Please make sure you read the [contributing](./CONTRIBUTING.md) guidelines, especially if you are
|
||||
interested in working on a PR or issue opened in the previous repository.
|
||||
|
||||
## Built with Ratatui
|
||||
|
||||
Ratatui has a number of built-in [widgets](https://docs.rs/ratatui/latest/ratatui/widgets/), as well
|
||||
as many contributed by external contributors. Check out the [Showcase](https://ratatui.rs/showcase/)
|
||||
section of the website, or the [awesome-ratatui](https://github.com/ratatui/awesome-ratatui) repo
|
||||
for a curated list of awesome apps/libraries built with `ratatui`!
|
||||
[][awesome-ratatui]
|
||||
|
||||
Check out the [showcase] section of the website, or the [awesome-ratatui] repository for a curated
|
||||
list of awesome apps and libraries built with Ratatui!
|
||||
|
||||
## Alternatives
|
||||
|
||||
You might want to checkout [Cursive](https://github.com/gyscos/Cursive) or
|
||||
[iocraft](https://github.com/ccbrown/iocraft/) for an alternative solutions
|
||||
to build text user interfaces in Rust.
|
||||
- [Cursive](https://crates.io/crates/cursive) - a ncurses-based TUI library.
|
||||
- [iocraft](https://crates.io/crates/iocraft) - a declarative TUI library.
|
||||
|
||||
## Acknowledgments
|
||||
## Contributing
|
||||
|
||||
None of this could be possible without [**Florian Dehau**](https://github.com/fdehau) who originally
|
||||
created [tui-rs] which inspired many Rust TUIs.
|
||||
[![Discord Badge]][Discord Server] [![Matrix Badge]][Matrix] [![Forum Badge]][Ratatui Forum]
|
||||
|
||||
Special thanks to [**Pavel Fomchenkov**](https://github.com/nawok) for his work in designing **an
|
||||
awesome logo** for the ratatui project and ratatui organization.
|
||||
Feel free to join our [Discord server](https://discord.gg/pMCEU9hNEj) for discussions and questions!
|
||||
There is also a [Matrix](https://matrix.org/) bridge available at
|
||||
[#ratatui:matrix.org](https://matrix.to/#/#ratatui:matrix.org). We have also recently launched the
|
||||
[Ratatui Forum].
|
||||
|
||||
We rely on GitHub for [bugs][Report a bug] and [feature requests][Request a Feature].
|
||||
|
||||
Please make sure you read the [contributing](./CONTRIBUTING.md) guidelines before [creating a pull
|
||||
request][Create a Pull Request].
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Ratatui was forked from the [tui-rs] crate in 2023 in order to continue its development. None of
|
||||
this could be possible without [Florian Dehau] who originally created [tui-rs] which inspired many
|
||||
Rust TUIs.
|
||||
|
||||
Special thanks to [Pavel Fomchenkov] for his work in designing an awesome logo for the Ratatui
|
||||
project and organization.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
This project is licensed under the [MIT License][License].
|
||||
|
||||
[Repo]: https://github.com/ratatui/ratatui
|
||||
[Ratatui Website]: https://ratatui.rs/
|
||||
[Ratatui Forum]: https://forum.ratatui.rs
|
||||
[Docs]: https://docs.rs/ratatui
|
||||
[Widget Examples]: https://github.com/ratatui/ratatui/tree/main/ratatui-widgets/examples
|
||||
[App Examples]: https://github.com/ratatui/ratatui/tree/main/examples
|
||||
[Changelog]: https://github.com/ratatui/ratatui/blob/main/CHANGELOG.md
|
||||
[git-cliff]: https://git-cliff.org
|
||||
[Conventional Commits]: https://www.conventionalcommits.org
|
||||
[Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md
|
||||
[EuroRust 2024 talk]: https://www.youtube.com/watch?v=hWG51Mc1DlM
|
||||
[Report a bug]: https://github.com/ratatui/ratatui/issues/new?labels=bug&projects=&template=bug_report.md
|
||||
[Request a Feature]: https://github.com/ratatui/ratatui/issues/new?labels=enhancement&projects=&template=feature_request.md
|
||||
[Create a Pull Request]: https://github.com/ratatui/ratatui/compare
|
||||
[Contributing]: https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md
|
||||
[Crate]: https://crates.io/crates/ratatui
|
||||
[tui-rs]: https://crates.io/crates/tui
|
||||
[Sponsors]: https://github.com/sponsors/ratatui
|
||||
[Crate Badge]: https://img.shields.io/crates/v/ratatui?logo=rust&style=flat-square&color=E05D44
|
||||
[Repo Badge]: https://img.shields.io/badge/repo-ratatui/ratatui-1370D3?style=flat-square&logo=github
|
||||
[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/ratatui/ci.yml?style=flat-square&logo=github
|
||||
[CI]: https://github.com/ratatui/ratatui/actions/workflows/ci.yml
|
||||
[Codecov Badge]: https://img.shields.io/codecov/c/github/ratatui/ratatui?logo=codecov&style=flat-square&token=BAQ8SOKEST&color=C43AC3
|
||||
[Codecov]: https://app.codecov.io/gh/ratatui/ratatui
|
||||
[Deps Badge]: https://deps.rs/repo/github/ratatui/ratatui/status.svg?path=ratatui&style=flat-square
|
||||
[Deps]: https://deps.rs/repo/github/ratatui/ratatui?path=ratatui
|
||||
[Discord Badge]: 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/badge/docs-ratatui-1370D3?style=flat-square&logo=rust
|
||||
[Matrix Badge]: 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
|
||||
[Forum Badge]: https://img.shields.io/discourse/likes?server=https%3A%2F%2Fforum.ratatui.rs&style=flat-square&logo=discourse&label=forum&color=C43AC3
|
||||
[Sponsors Badge]: https://img.shields.io/github/sponsors/ratatui?logo=github&style=flat-square&color=1370D3
|
||||
[templates]: https://github.com/ratatui/templates/
|
||||
[showcase]: https://ratatui.rs/showcase/
|
||||
[awesome-ratatui]: https://github.com/ratatui/awesome-ratatui
|
||||
[Pavel Fomchenkov]: https://github.com/nawok
|
||||
[Florian Dehau]: https://github.com/fdehau
|
||||
[`cargo-generate`]: https://crates.io/crates/cargo-generate
|
||||
[License]: ./LICENSE
|
||||
|
||||
99
bacon.toml
99
bacon.toml
@@ -8,119 +8,57 @@
|
||||
default_job = "check"
|
||||
|
||||
[jobs.check]
|
||||
command = ["cargo", "check", "--all-features"]
|
||||
command = ["cargo", "xtask", "check"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-all]
|
||||
command = ["cargo", "check", "--all-targets", "--all-features"]
|
||||
command = ["cargo", "xtask", "check", "--all-features"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-crossterm]
|
||||
command = [
|
||||
"cargo",
|
||||
"check",
|
||||
"--all-targets",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"crossterm",
|
||||
]
|
||||
command = ["cargo", "xtask", "check-crossterm"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-termion]
|
||||
command = [
|
||||
"cargo",
|
||||
"check",
|
||||
"--all-targets",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"termion",
|
||||
]
|
||||
command = ["cargo", "xtask", "check-termion"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-termwiz]
|
||||
command = [
|
||||
"cargo",
|
||||
"check",
|
||||
"--all-targets",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"termwiz",
|
||||
]
|
||||
command = ["cargo", "xtask", "check-termwiz"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.clippy]
|
||||
command = ["cargo", "clippy", "--all-targets"]
|
||||
[jobs.clippy-all]
|
||||
command = ["cargo", "xtask", "clippy"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.test]
|
||||
command = ["cargo", "test", "--all-features"]
|
||||
command = ["cargo", "xtask", "test"]
|
||||
need_stdout = true
|
||||
|
||||
[jobs.test-unit]
|
||||
command = ["cargo", "test", "--lib", "--all-features"]
|
||||
command = ["cargo", "xtask", "test-libs"]
|
||||
need_stdout = true
|
||||
|
||||
[jobs.doc]
|
||||
command = [
|
||||
"cargo",
|
||||
"+nightly",
|
||||
"doc",
|
||||
"-Zunstable-options",
|
||||
"-Zrustdoc-scrape-examples",
|
||||
"--all-features",
|
||||
"--no-deps",
|
||||
]
|
||||
env.RUSTDOCFLAGS = "--cfg docsrs"
|
||||
command = ["cargo", "xtask", "docs"]
|
||||
need_stdout = false
|
||||
|
||||
# If the doc compiles, then it opens in your browser and bacon switches
|
||||
# to the previous job
|
||||
[jobs.doc-open]
|
||||
command = [
|
||||
"cargo",
|
||||
"+nightly",
|
||||
"doc",
|
||||
"-Zunstable-options",
|
||||
"-Zrustdoc-scrape-examples",
|
||||
"--all-features",
|
||||
"--no-deps",
|
||||
"--open",
|
||||
]
|
||||
env.RUSTDOCFLAGS = "--cfg docsrs"
|
||||
command = ["cargo", "xtask", "docs", "--open"]
|
||||
on_success = "job:doc"
|
||||
need_stdout = false
|
||||
on_success = "job:doc" # so that we don't open the browser at each change
|
||||
|
||||
[jobs.coverage]
|
||||
command = [
|
||||
"cargo",
|
||||
"llvm-cov",
|
||||
"--lcov",
|
||||
"--output-path",
|
||||
"target/lcov.info",
|
||||
"--all-features",
|
||||
]
|
||||
command = ["cargo", "xtask", "coverage"]
|
||||
|
||||
[jobs.coverage-unit-tests-only]
|
||||
command = [
|
||||
"cargo",
|
||||
"llvm-cov",
|
||||
"--lcov",
|
||||
"--output-path",
|
||||
"target/lcov.info",
|
||||
"--lib",
|
||||
"--all-features",
|
||||
]
|
||||
command = ["cargo", "xtask", "coverage-unit"]
|
||||
|
||||
[jobs.hack]
|
||||
command = [
|
||||
"cargo",
|
||||
"hack",
|
||||
"test",
|
||||
"--lib",
|
||||
"--each-feature",
|
||||
# "--all-targets",
|
||||
"--workspace",
|
||||
]
|
||||
command = ["cargo", "xtask", "hack"]
|
||||
|
||||
[jobs.format]
|
||||
command = ["cargo", "xtask", "format"]
|
||||
|
||||
# You may define here keybindings that would be specific to
|
||||
# a project, for example a shortcut to launch a specific job.
|
||||
@@ -135,3 +73,4 @@ v = "job:coverage"
|
||||
ctrl-v = "job:coverage-unit-tests-only"
|
||||
u = "job:test-unit"
|
||||
n = "job:nextest"
|
||||
f = "job:format"
|
||||
|
||||
23
clippy.toml
23
clippy.toml
@@ -1,24 +1 @@
|
||||
avoid-breaking-exported-api = false
|
||||
|
||||
# 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
|
||||
# (also, memoffset, syn, nix, strsim, windows-sys
|
||||
# crossterm -> all the windows- deps https://github.com/ratatui/ratatui/pull/1064#issuecomment-2078848980
|
||||
allowed-duplicate-crates = [
|
||||
"bitflags",
|
||||
"memoffset",
|
||||
"nix",
|
||||
"strsim",
|
||||
"syn",
|
||||
"windows-sys",
|
||||
"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",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
30
deny.toml
30
deny.toml
@@ -4,14 +4,16 @@
|
||||
version = 2
|
||||
confidence-threshold = 0.8
|
||||
allow = [
|
||||
"Apache-2.0",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"ISC",
|
||||
"MIT",
|
||||
"Unicode-DFS-2016",
|
||||
"WTFPL",
|
||||
"Zlib",
|
||||
"Apache-2.0",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"ISC",
|
||||
"MIT",
|
||||
"OpenSSL",
|
||||
"Unicode-3.0",
|
||||
"Unicode-DFS-2016",
|
||||
"WTFPL",
|
||||
"Zlib",
|
||||
]
|
||||
|
||||
[advisories]
|
||||
@@ -24,3 +26,15 @@ multiple-versions = "allow"
|
||||
unknown-registry = "deny"
|
||||
unknown-git = "warn"
|
||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||
|
||||
[[licenses.clarify]]
|
||||
crate = "ring"
|
||||
# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses
|
||||
# https://spdx.org/licenses/OpenSSL.html
|
||||
# ISC - Both BoringSSL and ring use this for their new files
|
||||
# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT
|
||||
# license, for third_party/fiat, which, unlike other third_party directories, is
|
||||
# compiled into non-test libraries, is included below."
|
||||
# OpenSSL - Obviously
|
||||
expression = "ISC AND MIT AND OpenSSL"
|
||||
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]
|
||||
|
||||
66
examples/README.md
Normal file
66
examples/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Examples
|
||||
|
||||
This folder contains examples that are more app focused. There are also other examples in example
|
||||
folders under each crate folder e.g. [ratatui examples], [ratatui-widgets examples].
|
||||
|
||||
[ratatui examples]: ../ratatui/examples
|
||||
[ratatui-widgets examples]: ../ratatui-widgets/examples
|
||||
|
||||
You can run these examples using:
|
||||
|
||||
```shell
|
||||
cargo run -p example-name
|
||||
```
|
||||
|
||||
This folder might use unreleased code. Consider viewing the examples in the `latest` branch instead
|
||||
of the `main` branch for code which is guaranteed to work with the released ratatui version.
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> There may be backwards incompatible changes in these examples, as they are designed to compile
|
||||
> against the `main` branch.
|
||||
>
|
||||
> There are a few workaround for this problem:
|
||||
>
|
||||
> - View the examples as they were when the latest version was release by selecting the tag that
|
||||
> matches that version. E.g. <https://github.com/ratatui/ratatui/tree/v0.26.1/examples>.
|
||||
> - If you're viewing this file on GitHub, there is a combo box at the top of this page which
|
||||
> allows you to select any previous tagged version.
|
||||
> - To view the code locally, checkout the tag. E.g. `git switch --detach v0.26.1`.
|
||||
> - Use the latest [alpha version of Ratatui] in your app. These are released weekly on Saturdays.
|
||||
> - Compile your code against the main branch either locally by adding e.g. `path = "../ratatui"` to
|
||||
> the dependency, or remotely by adding `git = "https://github.com/ratatui/ratatui"`
|
||||
>
|
||||
> For a list of unreleased breaking changes, see [BREAKING-CHANGES.md].
|
||||
>
|
||||
> We don't keep the CHANGELOG updated with unreleased changes, check the git commit history or run
|
||||
> `git-cliff -u` against a cloned version of this repository.
|
||||
|
||||
## Demo
|
||||
|
||||
This is the original demo example from the main README. It is available for each of the backends.
|
||||
[Source](./apps/demo/).
|
||||
|
||||

|
||||
|
||||
## Demo2
|
||||
|
||||
This is the demo example from the main README and crate page. [Source](./apps/demo2/).
|
||||
|
||||

|
||||
|
||||
## Mouse Drawing demo
|
||||
|
||||
Shows how to handle mouse events. [Source](./apps/mouse-drawing/).
|
||||
|
||||
## Async GitHub demo
|
||||
|
||||
Shows how to fetch data from GitHub API asynchronously. [Source](./apps/async-github/).
|
||||
|
||||
## Weather demo
|
||||
|
||||
Shows how to render weather data using barchart widget. [Source](./apps/weather/).
|
||||
|
||||
## Calendar explorer demo
|
||||
|
||||
Shows how to render a calendar with different styles. [Source](./apps/calendar-explorer/).
|
||||
22
examples/apps/async-github/Cargo.toml
Normal file
22
examples/apps/async-github/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "async-github"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
documentation.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
keywords.workspace = true
|
||||
categories.workspace = true
|
||||
readme.workspace = true
|
||||
license.workspace = true
|
||||
exclude.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
color-eyre = "0.6.3"
|
||||
crossterm = { workspace = true, features = ["event-stream"] }
|
||||
octocrab = "0.42.1"
|
||||
ratatui.workspace = true
|
||||
tokio = { version = "1.43.0", features = ["rt-multi-thread", "macros"] }
|
||||
tokio-stream = "0.1.17"
|
||||
9
examples/apps/async-github/README.md
Normal file
9
examples/apps/async-github/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Async GitHub demo
|
||||
|
||||
This example demonstrates how to use Ratatui with widgets that fetch data from GitHub API asynchronously.
|
||||
|
||||
To run this demo:
|
||||
|
||||
```shell
|
||||
cargo run -p async-github
|
||||
```
|
||||
@@ -1,9 +1,7 @@
|
||||
//! # [Ratatui] Async example
|
||||
//!
|
||||
//! This example demonstrates how to use Ratatui with widgets that fetch data asynchronously. It
|
||||
//! uses the `octocrab` crate to fetch a list of pull requests from the GitHub API. You will need an
|
||||
//! environment variable named `GITHUB_TOKEN` with a valid GitHub personal access token. The token
|
||||
//! does not need any special permissions.
|
||||
//! uses the `octocrab` crate to fetch a list of pull requests from the GitHub API.
|
||||
//!
|
||||
//! <https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token>
|
||||
//! <https://github.com/settings/tokens/new> to create a new token (select classic, and no scopes)
|
||||
@@ -34,11 +32,10 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use color_eyre::{eyre::Context, Result, Section};
|
||||
use futures::StreamExt;
|
||||
use color_eyre::Result;
|
||||
use octocrab::{
|
||||
params::{pulls::Sort, Direction},
|
||||
OctocrabBuilder, Page,
|
||||
Page,
|
||||
};
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
@@ -49,29 +46,17 @@ use ratatui::{
|
||||
widgets::{Block, HighlightSpacing, Row, StatefulWidget, Table, TableState, Widget},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
init_octocrab()?;
|
||||
let terminal = ratatui::init();
|
||||
let app_result = App::default().run(terminal).await;
|
||||
ratatui::restore();
|
||||
app_result
|
||||
}
|
||||
|
||||
fn init_octocrab() -> Result<()> {
|
||||
let token = std::env::var("GITHUB_TOKEN")
|
||||
.wrap_err("The GITHUB_TOKEN environment variable was not found")
|
||||
.suggestion(
|
||||
"Go to https://github.com/settings/tokens/new to create a token, and re-run:
|
||||
GITHUB_TOKEN=ghp_... cargo run --example async --features crossterm",
|
||||
)?;
|
||||
let crab = OctocrabBuilder::new().personal_token(token).build()?;
|
||||
octocrab::initialise(crab);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct App {
|
||||
should_quit: bool,
|
||||
15
examples/apps/calendar-explorer/Cargo.toml
Normal file
15
examples/apps/calendar-explorer/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "calendar-explorer"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
color-eyre.workspace = true
|
||||
crossterm.workspace = true
|
||||
ratatui.workspace = true
|
||||
time = { version = "0.3.37", features = ["formatting", "parsing"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
9
examples/apps/calendar-explorer/README.md
Normal file
9
examples/apps/calendar-explorer/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Calendar explorer demo
|
||||
|
||||
This example shows how to render a calendar with different styles.
|
||||
|
||||
To run this demo:
|
||||
|
||||
```shell
|
||||
cargo run -p calendar-explorer
|
||||
```
|
||||
248
examples/apps/calendar-explorer/src/main.rs
Normal file
248
examples/apps/calendar-explorer/src/main.rs
Normal file
@@ -0,0 +1,248 @@
|
||||
//! A Ratatui example that demonstrates how to render calendar with different styles.
|
||||
//!
|
||||
//! Marks the holidays and seasons on the calendar.
|
||||
//!
|
||||
//! This example runs with the Ratatui library code in the branch that you are currently reading.
|
||||
//! See the [`latest`] branch for the code which works with the most recent Ratatui release.
|
||||
//!
|
||||
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
|
||||
//! [`BarChart`]: https://docs.rs/ratatui/latest/ratatui/widgets/struct.BarChart.html
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEventKind};
|
||||
use ratatui::{
|
||||
layout::{Constraint, Layout, Margin, Rect},
|
||||
style::{Color, Modifier, Style, Stylize},
|
||||
text::{Line, Text},
|
||||
widgets::calendar::{CalendarEventStore, Monthly},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
use time::{ext::NumericalDuration, Date, Month, OffsetDateTime};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
let mut selected_date = OffsetDateTime::now_local()?.date();
|
||||
let mut calendar_style = StyledCalendar::Default;
|
||||
loop {
|
||||
terminal.draw(|frame| render(frame, calendar_style, selected_date))?;
|
||||
if let Event::Key(key) = event::read()? {
|
||||
if key.kind == KeyEventKind::Press {
|
||||
match key.code {
|
||||
KeyCode::Char('q') => break Ok(()),
|
||||
KeyCode::Char('s') => calendar_style = calendar_style.next(),
|
||||
KeyCode::Char('n') | KeyCode::Tab => selected_date = next_month(selected_date),
|
||||
KeyCode::Char('p') | KeyCode::BackTab => {
|
||||
selected_date = previous_month(selected_date);
|
||||
}
|
||||
KeyCode::Char('h') | KeyCode::Left => selected_date -= 1.days(),
|
||||
KeyCode::Char('j') | KeyCode::Down => selected_date += 1.weeks(),
|
||||
KeyCode::Char('k') | KeyCode::Up => selected_date -= 1.weeks(),
|
||||
KeyCode::Char('l') | KeyCode::Right => selected_date += 1.days(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn next_month(date: Date) -> Date {
|
||||
if date.month() == Month::December {
|
||||
date.replace_month(Month::January)
|
||||
.unwrap()
|
||||
.replace_year(date.year() + 1)
|
||||
.unwrap()
|
||||
} else {
|
||||
date.replace_month(date.month().next()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn previous_month(date: Date) -> Date {
|
||||
if date.month() == Month::January {
|
||||
date.replace_month(Month::December)
|
||||
.unwrap()
|
||||
.replace_year(date.year() - 1)
|
||||
.unwrap()
|
||||
} else {
|
||||
date.replace_month(date.month().previous()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with a calendar.
|
||||
fn render(frame: &mut Frame, calendar_style: StyledCalendar, selected_date: Date) {
|
||||
let header = Text::from_iter([
|
||||
Line::from("Calendar Example".bold()),
|
||||
Line::from(
|
||||
"<q> Quit | <s> Change Style | <n> Next Month | <p> Previous Month, <hjkl> Move",
|
||||
),
|
||||
Line::from(format!(
|
||||
"Current date: {selected_date} | Current style: {calendar_style}"
|
||||
)),
|
||||
]);
|
||||
|
||||
let vertical = Layout::vertical([
|
||||
Constraint::Length(header.height() as u16),
|
||||
Constraint::Fill(1),
|
||||
]);
|
||||
let [text_area, area] = vertical.areas(frame.area());
|
||||
frame.render_widget(header.centered(), text_area);
|
||||
calendar_style
|
||||
.render_year(frame, area, selected_date)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum StyledCalendar {
|
||||
Default,
|
||||
Surrounding,
|
||||
WeekdaysHeader,
|
||||
SurroundingAndWeekdaysHeader,
|
||||
MonthHeader,
|
||||
MonthAndWeekdaysHeader,
|
||||
}
|
||||
|
||||
impl StyledCalendar {
|
||||
// Cycle through the different styles.
|
||||
const fn next(self) -> Self {
|
||||
match self {
|
||||
Self::Default => Self::Surrounding,
|
||||
Self::Surrounding => Self::WeekdaysHeader,
|
||||
Self::WeekdaysHeader => Self::SurroundingAndWeekdaysHeader,
|
||||
Self::SurroundingAndWeekdaysHeader => Self::MonthHeader,
|
||||
Self::MonthHeader => Self::MonthAndWeekdaysHeader,
|
||||
Self::MonthAndWeekdaysHeader => Self::Default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for StyledCalendar {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Default => write!(f, "Default"),
|
||||
Self::Surrounding => write!(f, "Show Surrounding"),
|
||||
Self::WeekdaysHeader => write!(f, "Show Weekdays Header"),
|
||||
Self::SurroundingAndWeekdaysHeader => write!(f, "Show Surrounding and Weekdays Header"),
|
||||
Self::MonthHeader => write!(f, "Show Month Header"),
|
||||
Self::MonthAndWeekdaysHeader => write!(f, "Show Month Header and Weekdays Header"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StyledCalendar {
|
||||
fn render_year(self, frame: &mut Frame, area: Rect, date: Date) -> Result<()> {
|
||||
let events = events(date)?;
|
||||
|
||||
let area = area.inner(Margin {
|
||||
vertical: 1,
|
||||
horizontal: 1,
|
||||
});
|
||||
let rows = Layout::vertical([Constraint::Ratio(1, 3); 3]).split(area);
|
||||
let areas = rows.iter().flat_map(|row| {
|
||||
Layout::horizontal([Constraint::Ratio(1, 4); 4])
|
||||
.split(*row)
|
||||
.to_vec()
|
||||
});
|
||||
for (i, area) in areas.enumerate() {
|
||||
let month = date
|
||||
.replace_day(1)
|
||||
.unwrap()
|
||||
.replace_month(Month::try_from(i as u8 + 1).unwrap())
|
||||
.unwrap();
|
||||
self.render_month(frame, area, month, &events);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_month(self, frame: &mut Frame, area: Rect, date: Date, events: &CalendarEventStore) {
|
||||
let calendar = match self {
|
||||
Self::Default => Monthly::new(date, events)
|
||||
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
|
||||
.show_month_header(Style::default()),
|
||||
Self::Surrounding => Monthly::new(date, events)
|
||||
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
|
||||
.show_month_header(Style::default())
|
||||
.show_surrounding(Style::new().dim()),
|
||||
Self::WeekdaysHeader => Monthly::new(date, events)
|
||||
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
|
||||
.show_month_header(Style::default())
|
||||
.show_weekdays_header(Style::new().bold().green()),
|
||||
Self::SurroundingAndWeekdaysHeader => Monthly::new(date, events)
|
||||
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
|
||||
.show_month_header(Style::default())
|
||||
.show_surrounding(Style::new().dim())
|
||||
.show_weekdays_header(Style::new().bold().green()),
|
||||
Self::MonthHeader => Monthly::new(date, events)
|
||||
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
|
||||
.show_month_header(Style::default())
|
||||
.show_month_header(Style::new().bold().green()),
|
||||
Self::MonthAndWeekdaysHeader => Monthly::new(date, events)
|
||||
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
|
||||
.show_month_header(Style::default())
|
||||
.show_weekdays_header(Style::new().bold().dim().light_yellow()),
|
||||
};
|
||||
frame.render_widget(calendar, area);
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a list of dates for the current year.
|
||||
fn events(selected_date: Date) -> Result<CalendarEventStore> {
|
||||
const SELECTED: Style = Style::new()
|
||||
.fg(Color::White)
|
||||
.bg(Color::Red)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
const HOLIDAY: Style = Style::new()
|
||||
.fg(Color::Red)
|
||||
.add_modifier(Modifier::UNDERLINED);
|
||||
const SEASON: Style = Style::new()
|
||||
.fg(Color::Green)
|
||||
.bg(Color::Black)
|
||||
.add_modifier(Modifier::UNDERLINED);
|
||||
|
||||
let mut list = CalendarEventStore::today(
|
||||
Style::default()
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.bg(Color::Blue),
|
||||
);
|
||||
let y = selected_date.year();
|
||||
|
||||
// new year's
|
||||
list.add(Date::from_calendar_date(y, Month::January, 1)?, HOLIDAY);
|
||||
// next new_year's for December "show surrounding"
|
||||
list.add(Date::from_calendar_date(y + 1, Month::January, 1)?, HOLIDAY);
|
||||
// groundhog day
|
||||
list.add(Date::from_calendar_date(y, Month::February, 2)?, HOLIDAY);
|
||||
// april fool's
|
||||
list.add(Date::from_calendar_date(y, Month::April, 1)?, HOLIDAY);
|
||||
// earth day
|
||||
list.add(Date::from_calendar_date(y, Month::April, 22)?, HOLIDAY);
|
||||
// star wars day
|
||||
list.add(Date::from_calendar_date(y, Month::May, 4)?, HOLIDAY);
|
||||
// festivus
|
||||
list.add(Date::from_calendar_date(y, Month::December, 23)?, HOLIDAY);
|
||||
// new year's eve
|
||||
list.add(Date::from_calendar_date(y, Month::December, 31)?, HOLIDAY);
|
||||
|
||||
// seasons
|
||||
// spring equinox
|
||||
list.add(Date::from_calendar_date(y, Month::March, 22)?, SEASON);
|
||||
// summer solstice
|
||||
list.add(Date::from_calendar_date(y, Month::June, 21)?, SEASON);
|
||||
// fall equinox
|
||||
list.add(Date::from_calendar_date(y, Month::September, 22)?, SEASON);
|
||||
// winter solstice
|
||||
list.add(Date::from_calendar_date(y, Month::December, 21)?, SEASON);
|
||||
|
||||
// selected date
|
||||
list.add(selected_date, SELECTED);
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
15
examples/apps/canvas/Cargo.toml
Normal file
15
examples/apps/canvas/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "canvas"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
color-eyre.workspace = true
|
||||
crossterm.workspace = true
|
||||
itertools.workspace = true
|
||||
ratatui.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
9
examples/apps/canvas/README.md
Normal file
9
examples/apps/canvas/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Canvas demo
|
||||
|
||||
This example shows how to render various shapes and a map on a canvas.
|
||||
|
||||
To run this demo:
|
||||
|
||||
```shell
|
||||
cargo run -p canvas
|
||||
```
|
||||
@@ -1,18 +1,13 @@
|
||||
//! # [Ratatui] Canvas example
|
||||
//!
|
||||
//! The latest version of this example is available in the [examples] folder in the repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
/// A Ratatui example that demonstrates how to draw on a canvas.
|
||||
///
|
||||
/// This example demonstrates how to draw various shapes such as rectangles, circles, and lines
|
||||
/// on a canvas. It also demonstrates how to draw a map.
|
||||
///
|
||||
/// This example runs with the Ratatui library code in the branch that you are currently
|
||||
/// reading. See the [`latest`] branch for the code which works with the most recent Ratatui
|
||||
/// release.
|
||||
///
|
||||
/// [`latest`]: https://github.com/ratatui/ratatui/tree/latest
|
||||
use std::{
|
||||
io::stdout,
|
||||
time::{Duration, Instant},
|
||||
@@ -29,6 +24,7 @@ use ratatui::{
|
||||
layout::{Constraint, Layout, Position, Rect},
|
||||
style::{Color, Stylize},
|
||||
symbols::Marker,
|
||||
text::Text,
|
||||
widgets::{
|
||||
canvas::{Canvas, Circle, Map, MapResolution, Points, Rectangle},
|
||||
Block, Widget,
|
||||
@@ -54,7 +50,6 @@ struct App {
|
||||
playground: Rect,
|
||||
vx: f64,
|
||||
vy: f64,
|
||||
tick_count: u64,
|
||||
marker: Marker,
|
||||
points: Vec<Position>,
|
||||
is_drawing: bool,
|
||||
@@ -75,7 +70,6 @@ impl App {
|
||||
playground: Rect::new(10, 10, 200, 100),
|
||||
vx: 1.0,
|
||||
vy: 1.0,
|
||||
tick_count: 0,
|
||||
marker: Marker::Dot,
|
||||
points: vec![],
|
||||
is_drawing: false,
|
||||
@@ -114,6 +108,15 @@ impl App {
|
||||
KeyCode::Up | KeyCode::Char('k') => self.y -= 1.0,
|
||||
KeyCode::Right | KeyCode::Char('l') => self.x += 1.0,
|
||||
KeyCode::Left | KeyCode::Char('h') => self.x -= 1.0,
|
||||
KeyCode::Enter => {
|
||||
self.marker = match self.marker {
|
||||
Marker::Dot => Marker::Braille,
|
||||
Marker::Braille => Marker::Block,
|
||||
Marker::Block => Marker::HalfBlock,
|
||||
Marker::HalfBlock => Marker::Bar,
|
||||
Marker::Bar => Marker::Dot,
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -130,17 +133,6 @@ impl App {
|
||||
}
|
||||
|
||||
fn on_tick(&mut self) {
|
||||
self.tick_count += 1;
|
||||
// only change marker every 180 ticks (3s) to avoid stroboscopic effect
|
||||
if (self.tick_count % 180) == 0 {
|
||||
self.marker = match self.marker {
|
||||
Marker::Dot => Marker::Braille,
|
||||
Marker::Braille => Marker::Block,
|
||||
Marker::Block => Marker::HalfBlock,
|
||||
Marker::HalfBlock => Marker::Bar,
|
||||
Marker::Bar => Marker::Dot,
|
||||
};
|
||||
}
|
||||
// bounce the ball by flipping the velocity vector
|
||||
let ball = &self.ball;
|
||||
let playground = self.playground;
|
||||
@@ -154,18 +146,28 @@ impl App {
|
||||
{
|
||||
self.vy = -self.vy;
|
||||
}
|
||||
|
||||
self.ball.x += self.vx;
|
||||
self.ball.y += self.vy;
|
||||
}
|
||||
|
||||
fn draw(&self, frame: &mut Frame) {
|
||||
let header = Text::from_iter([
|
||||
"Canvas Example".bold(),
|
||||
"<q> Quit | <enter> Change Marker | <hjkl> Move".into(),
|
||||
]);
|
||||
|
||||
let vertical = Layout::vertical([
|
||||
Constraint::Length(header.height() as u16),
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage(50),
|
||||
]);
|
||||
let [text_area, up, down] = vertical.areas(frame.area());
|
||||
frame.render_widget(header.centered(), text_area);
|
||||
|
||||
let horizontal =
|
||||
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
|
||||
let vertical = Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]);
|
||||
let [left, right] = horizontal.areas(frame.area());
|
||||
let [draw, map] = vertical.areas(left);
|
||||
let [pong, boxes] = vertical.areas(right);
|
||||
let [draw, pong] = horizontal.areas(up);
|
||||
let [map, boxes] = horizontal.areas(down);
|
||||
|
||||
frame.render_widget(self.map_canvas(), map);
|
||||
frame.render_widget(self.draw_canvas(draw), draw);
|
||||
14
examples/apps/chart/Cargo.toml
Normal file
14
examples/apps/chart/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "chart"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
color-eyre.workspace = true
|
||||
crossterm.workspace = true
|
||||
ratatui.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
9
examples/apps/chart/README.md
Normal file
9
examples/apps/chart/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Chart demo
|
||||
|
||||
This example shows how to render line, bar, and scatter charts.
|
||||
|
||||
To run this demo:
|
||||
|
||||
```shell
|
||||
cargo run -p chart
|
||||
```
|
||||
@@ -1,18 +1,13 @@
|
||||
//! # [Ratatui] Chart example
|
||||
//!
|
||||
//! The latest version of this example is available in the [examples] folder in the repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
/// A Ratatui example that demonstrates how to handle charts.
|
||||
///
|
||||
/// This example demonstrates how to draw various types of charts such as line, bar, and
|
||||
/// scatter charts.
|
||||
///
|
||||
/// This example runs with the Ratatui library code in the branch that you are currently
|
||||
/// reading. See the [`latest`] branch for the code which works with the most recent Ratatui
|
||||
/// release.
|
||||
///
|
||||
/// [`latest`]: https://github.com/ratatui/ratatui/tree/latest
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use color_eyre::Result;
|
||||
15
examples/apps/color-explorer/Cargo.toml
Normal file
15
examples/apps/color-explorer/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "color-explorer"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
color-eyre.workspace = true
|
||||
crossterm.workspace = true
|
||||
itertools.workspace = true
|
||||
ratatui.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
9
examples/apps/color-explorer/README.md
Normal file
9
examples/apps/color-explorer/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Color explorer demo
|
||||
|
||||
This example shows how to handle the supported colors.
|
||||
|
||||
To run this demo:
|
||||
|
||||
```shell
|
||||
cargo run -p color-explorer
|
||||
```
|
||||
@@ -1,20 +1,12 @@
|
||||
//! # [Ratatui] Colors example
|
||||
//! A Ratatui example that demonstrates how to handle colors.
|
||||
//!
|
||||
//! The latest version of this example is available in the [examples] folder in the repository.
|
||||
//! This example shows all the colors supported by Ratatui. It will render a grid of foreground
|
||||
//! and background colors with their names and indexes.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//! This example runs with the Ratatui library code in the branch that you are currently reading.
|
||||
//! See the [`latest`] branch for the code which works with the most recent Ratatui release.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
// This example shows all the colors supported by ratatui. It will render a grid of foreground
|
||||
// and background colors with their names and indexes.
|
||||
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
|
||||
|
||||
use color_eyre::Result;
|
||||
use itertools::Itertools;
|
||||
17
examples/apps/demo/Cargo.toml
Normal file
17
examples/apps/demo/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "demo"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["crossterm"]
|
||||
crossterm = ["ratatui/crossterm"]
|
||||
termion = ["ratatui/termion"]
|
||||
termwiz = ["ratatui/termwiz"]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.23", features = ["derive"] }
|
||||
rand = "0.8.5"
|
||||
ratatui.workspace = true
|
||||
25
examples/apps/demo/README.md
Normal file
25
examples/apps/demo/README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Demo example
|
||||
|
||||
This is the original demo that was developed for Tui-rs (the library that Ratatui was forked from).
|
||||
|
||||

|
||||
|
||||
This example is available for each backend. To run it:
|
||||
|
||||
## crossterm
|
||||
|
||||
```shell
|
||||
cargo run -p demo
|
||||
```
|
||||
|
||||
## termion
|
||||
|
||||
```shell
|
||||
cargo run -p demo --no-default-features --features termion
|
||||
```
|
||||
|
||||
## termwiz
|
||||
|
||||
```shell
|
||||
cargo run -p demo --no-default-features --features termwiz
|
||||
```
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
use std::{error::Error, time::Duration};
|
||||
|
||||
use argh::FromArgs;
|
||||
use clap::Parser;
|
||||
|
||||
mod app;
|
||||
#[cfg(feature = "crossterm")]
|
||||
@@ -28,28 +28,29 @@ mod termwiz;
|
||||
mod ui;
|
||||
|
||||
/// Demo
|
||||
#[derive(Debug, FromArgs)]
|
||||
#[derive(Debug, Parser)]
|
||||
struct Cli {
|
||||
/// time in ms between two ticks.
|
||||
#[argh(option, default = "250")]
|
||||
#[arg(short, long, default_value_t = 250)]
|
||||
tick_rate: u64,
|
||||
|
||||
/// whether unicode symbols are used to improve the overall look of the app
|
||||
#[argh(option, default = "true")]
|
||||
enhanced_graphics: bool,
|
||||
#[arg(short, long, default_value_t = true)]
|
||||
unicode: bool,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let cli: Cli = argh::from_env();
|
||||
let cli = Cli::parse();
|
||||
let tick_rate = Duration::from_millis(cli.tick_rate);
|
||||
#[cfg(feature = "crossterm")]
|
||||
crate::crossterm::run(tick_rate, cli.enhanced_graphics)?;
|
||||
crate::crossterm::run(tick_rate, cli.unicode)?;
|
||||
#[cfg(all(not(windows), feature = "termion", not(feature = "crossterm")))]
|
||||
crate::termion::run(tick_rate, cli.enhanced_graphics)?;
|
||||
crate::termion::run(tick_rate, cli.unicode)?;
|
||||
#[cfg(all(
|
||||
feature = "termwiz",
|
||||
not(feature = "crossterm"),
|
||||
not(feature = "termion")
|
||||
))]
|
||||
crate::termwiz::run(tick_rate, cli.enhanced_graphics)?;
|
||||
crate::termwiz::run(tick_rate, cli.unicode)?;
|
||||
Ok(())
|
||||
}
|
||||
19
examples/apps/demo2/Cargo.toml
Normal file
19
examples/apps/demo2/Cargo.toml
Normal file
@@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "demo2"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
color-eyre = "0.6.3"
|
||||
crossterm.workspace = true
|
||||
indoc.workspace = true
|
||||
itertools.workspace = true
|
||||
palette = "0.7.6"
|
||||
rand = "0.8.5"
|
||||
rand_chacha = "0.3.1"
|
||||
ratatui = { workspace = true, features = ["all-widgets"] }
|
||||
strum.workspace = true
|
||||
time = "0.3.37"
|
||||
unicode-width = "0.2.0"
|
||||
9
examples/apps/demo2/README.md
Normal file
9
examples/apps/demo2/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## Demo2
|
||||
|
||||
This is the demo example from the main README and crate page. Source: [demo2](./demo2/).
|
||||
|
||||
```shell
|
||||
cargo run -p demo2
|
||||
```
|
||||
|
||||

|
||||
75
examples/apps/demo2/src/tabs/about.rs
Normal file
75
examples/apps/demo2/src/tabs/about.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Alignment, Constraint, Layout, Margin, Rect},
|
||||
widgets::{
|
||||
Block, Borders, Clear, MascotEyeColor, Padding, Paragraph, RatatuiMascot, Widget, Wrap,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{RgbSwatch, THEME};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct AboutTab {
|
||||
row_index: usize,
|
||||
}
|
||||
|
||||
impl AboutTab {
|
||||
pub fn prev_row(&mut self) {
|
||||
self.row_index = self.row_index.saturating_sub(1);
|
||||
}
|
||||
|
||||
pub fn next_row(&mut self) {
|
||||
self.row_index = self.row_index.saturating_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for AboutTab {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
RgbSwatch.render(area, buf);
|
||||
let horizontal = Layout::horizontal([Constraint::Length(34), Constraint::Min(0)]);
|
||||
let [logo_area, description] = horizontal.areas(area);
|
||||
render_crate_description(description, buf);
|
||||
let eye_state = if self.row_index % 2 == 0 {
|
||||
MascotEyeColor::Default
|
||||
} else {
|
||||
MascotEyeColor::Red
|
||||
};
|
||||
RatatuiMascot::default().set_eye(eye_state).render(
|
||||
logo_area.inner(Margin {
|
||||
vertical: 0,
|
||||
horizontal: 2,
|
||||
}),
|
||||
buf,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_crate_description(area: Rect, buf: &mut Buffer) {
|
||||
let area = area.inner(Margin {
|
||||
vertical: 4,
|
||||
horizontal: 2,
|
||||
});
|
||||
Clear.render(area, buf); // clear out the color swatches
|
||||
Block::new().style(THEME.content).render(area, buf);
|
||||
let area = area.inner(Margin {
|
||||
vertical: 1,
|
||||
horizontal: 2,
|
||||
});
|
||||
let text = "- cooking up terminal user interfaces -
|
||||
|
||||
Ratatui is a Rust crate that provides widgets (e.g. Paragraph, Table) and draws them to the \
|
||||
screen efficiently every frame.";
|
||||
Paragraph::new(text)
|
||||
.style(THEME.description)
|
||||
.block(
|
||||
Block::new()
|
||||
.title(" Ratatui ")
|
||||
.title_alignment(Alignment::Center)
|
||||
.borders(Borders::TOP)
|
||||
.border_style(THEME.description_title)
|
||||
.padding(Padding::new(0, 0, 0, 0)),
|
||||
)
|
||||
.wrap(Wrap { trim: true })
|
||||
.scroll((0, 0))
|
||||
.render(area, buf);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ impl Ingredient {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Ingredient> for Row<'a> {
|
||||
impl From<Ingredient> for Row<'_> {
|
||||
fn from(i: Ingredient) -> Self {
|
||||
Row::new(vec![i.quantity, i.name]).height(i.height())
|
||||
}
|
||||
@@ -22,10 +22,8 @@ pub struct KeyBinding {
|
||||
}
|
||||
|
||||
pub struct Logo {
|
||||
pub rat: Color,
|
||||
pub rat_eye: Color,
|
||||
pub rat_eye_alt: Color,
|
||||
pub term: Color,
|
||||
}
|
||||
|
||||
pub struct Email {
|
||||
@@ -77,10 +75,8 @@ pub const THEME: Theme = Theme {
|
||||
description: Style::new().fg(LIGHT_GRAY).bg(DARK_BLUE),
|
||||
description_title: Style::new().fg(LIGHT_GRAY).add_modifier(Modifier::BOLD),
|
||||
logo: Logo {
|
||||
rat: WHITE,
|
||||
rat_eye: BLACK,
|
||||
rat_eye_alt: RED,
|
||||
term: BLACK,
|
||||
},
|
||||
key_binding: KeyBinding {
|
||||
key: Style::new().fg(BLACK).bg(DARK_GRAY),
|
||||
16
examples/apps/input-form/Cargo.toml
Normal file
16
examples/apps/input-form/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "input-form"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
color-eyre.workspace = true
|
||||
crossterm.workspace = true
|
||||
ratatui.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
27
examples/apps/input-form/README.md
Normal file
27
examples/apps/input-form/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Input Form example
|
||||
|
||||
This example demonstrates how to handle input across several form fields (2 strings and an number).
|
||||
It uses an enum to track the focused field, and sends keyboard events to one which is current.
|
||||
|
||||
Run this example with:
|
||||
|
||||
```shell
|
||||
cargo run -p input-form
|
||||
```
|
||||
|
||||
This example does not handle things like cursor movement within the line (just keys and backspace).
|
||||
Most apps would benefit from using the following crates for text input rather than directly using
|
||||
strings:
|
||||
|
||||
- [`tui-input`](https://crates.io/crates/tui-input)
|
||||
- [`tui-prompts`](https://crates.io/crates/tui-prompts)
|
||||
- [`tui-textarea`](https://crates.io/crates/tui-textarea)
|
||||
- [`rat-salsa`](https://crates.io/crates/rat-salsa)
|
||||
|
||||
Some more ideas for handling focus can be found in:
|
||||
|
||||
- [`focusable`](https://crates.io/crates/focusable) (see also [Ratatui forum
|
||||
post](https://forum.ratatui.rs/t/focusable-crate-manage-focus-state-for-your-widgets/73))
|
||||
- [`rat-focus`](https://crates.io/crates/rat-focus)
|
||||
- A useful [`Bevy` discssion](https://github.com/bevyengine/bevy/discussions/15374) about focus
|
||||
more generally.
|
||||
270
examples/apps/input-form/src/main.rs
Normal file
270
examples/apps/input-form/src/main.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
//! A Ratatui example that demonstrates how to handle input form focus
|
||||
//!
|
||||
//! This example demonstrates how to handle cursor and input focus between multiple fields in a
|
||||
//! form. You can navigate between fields using the Tab key.
|
||||
//!
|
||||
//! This does not handle cursor movement etc. This is just a simple example. In a real application,
|
||||
//! consider using [`tui-input`], or [`tui-prompts`], or [`tui-textarea`].
|
||||
//!
|
||||
//! This example runs with the Ratatui library code in the branch that you are currently reading.
|
||||
//! See the [`latest`] branch for the code which works with the most recent Ratatui release.
|
||||
//!
|
||||
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
|
||||
//! [`tui-input`]: https://crates.io/crates/tui-input
|
||||
//! [`tui-prompts`]: https://crates.io/crates/tui-prompts
|
||||
//! [`tui-textarea`]: https://crates.io/crates/tui-textarea
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::{Constraint, Layout, Offset, Rect},
|
||||
style::Stylize,
|
||||
text::Line,
|
||||
widgets::Widget,
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = App::default().run(terminal);
|
||||
ratatui::restore();
|
||||
|
||||
// serialize the form to JSON if the user submitted it, otherwise print "Canceled"
|
||||
match result {
|
||||
Ok(Some(form)) => println!("{}", serde_json::to_string_pretty(&form)?),
|
||||
Ok(None) => println!("Canceled"),
|
||||
Err(err) => eprintln!("{err}"),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct App {
|
||||
state: AppState,
|
||||
form: InputForm,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq)]
|
||||
enum AppState {
|
||||
#[default]
|
||||
Running,
|
||||
Cancelled,
|
||||
Submitted,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn run(mut self, mut terminal: DefaultTerminal) -> Result<Option<InputForm>> {
|
||||
while self.state == AppState::Running {
|
||||
terminal.draw(|frame| self.render(frame))?;
|
||||
self.handle_events()?;
|
||||
}
|
||||
match self.state {
|
||||
AppState::Cancelled => Ok(None),
|
||||
AppState::Submitted => Ok(Some(self.form)),
|
||||
AppState::Running => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, frame: &mut Frame) {
|
||||
self.form.render(frame);
|
||||
}
|
||||
|
||||
fn handle_events(&mut self) -> Result<()> {
|
||||
match event::read()? {
|
||||
Event::Key(event) if event.kind == KeyEventKind::Press => match event.code {
|
||||
KeyCode::Esc => self.state = AppState::Cancelled,
|
||||
KeyCode::Enter => self.state = AppState::Submitted,
|
||||
_ => self.form.on_key_press(event),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct InputForm {
|
||||
#[serde(skip)]
|
||||
focus: Focus,
|
||||
first_name: StringField,
|
||||
last_name: StringField,
|
||||
age: AgeField,
|
||||
}
|
||||
|
||||
impl Default for InputForm {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
focus: Focus::FirstName,
|
||||
first_name: StringField::new("First Name"),
|
||||
last_name: StringField::new("Last Name"),
|
||||
age: AgeField::new("Age"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InputForm {
|
||||
// Handle focus navigation or pass the event to the focused field.
|
||||
fn on_key_press(&mut self, event: KeyEvent) {
|
||||
match event.code {
|
||||
KeyCode::Tab => self.focus = self.focus.next(),
|
||||
_ => match self.focus {
|
||||
Focus::FirstName => self.first_name.on_key_press(event),
|
||||
Focus::LastName => self.last_name.on_key_press(event),
|
||||
Focus::Age => self.age.on_key_press(event),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Render the form with the current focus.
|
||||
///
|
||||
/// The cursor is placed at the end of the focused field.
|
||||
fn render(&self, frame: &mut Frame) {
|
||||
let [first_name_area, last_name_area, age_area] =
|
||||
Layout::vertical(Constraint::from_lengths([1, 1, 1])).areas(frame.area());
|
||||
|
||||
frame.render_widget(&self.first_name, first_name_area);
|
||||
frame.render_widget(&self.last_name, last_name_area);
|
||||
frame.render_widget(&self.age, age_area);
|
||||
|
||||
let cursor_position = match self.focus {
|
||||
Focus::FirstName => first_name_area.offset(self.first_name.cursor_offset()),
|
||||
Focus::LastName => last_name_area.offset(self.last_name.cursor_offset()),
|
||||
Focus::Age => age_area.offset(self.age.cursor_offset()),
|
||||
};
|
||||
frame.set_cursor_position(cursor_position);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq)]
|
||||
enum Focus {
|
||||
#[default]
|
||||
FirstName,
|
||||
LastName,
|
||||
Age,
|
||||
}
|
||||
|
||||
impl Focus {
|
||||
// Round-robin focus order.
|
||||
const fn next(&self) -> Self {
|
||||
match self {
|
||||
Self::FirstName => Self::LastName,
|
||||
Self::LastName => Self::Age,
|
||||
Self::Age => Self::FirstName,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A new-type representing a string field with a label.
|
||||
#[derive(Debug, Serialize)]
|
||||
struct StringField {
|
||||
#[serde(skip)]
|
||||
label: &'static str,
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl StringField {
|
||||
const fn new(label: &'static str) -> Self {
|
||||
Self {
|
||||
label,
|
||||
value: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle input events for the string input.
|
||||
fn on_key_press(&mut self, event: KeyEvent) {
|
||||
match event.code {
|
||||
KeyCode::Char(c) => self.value.push(c),
|
||||
KeyCode::Backspace => {
|
||||
self.value.pop();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_offset(&self) -> Offset {
|
||||
let x = (self.label.len() + self.value.len() + 2) as i32;
|
||||
Offset::new(x, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &StringField {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let constraints = [
|
||||
Constraint::Length(self.label.len() as u16 + 2),
|
||||
Constraint::Fill(1),
|
||||
];
|
||||
let [label_area, value_area] = Layout::horizontal(constraints).areas(area);
|
||||
let label = Line::from_iter([self.label, ": "]).bold();
|
||||
label.render(label_area, buf);
|
||||
self.value.clone().render(value_area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// A new-type representing a person's age in years (0-130).
|
||||
#[derive(Default, Clone, Copy, Serialize)]
|
||||
struct AgeField {
|
||||
#[serde(skip)]
|
||||
label: &'static str,
|
||||
value: u8,
|
||||
}
|
||||
|
||||
impl AgeField {
|
||||
const MAX: u8 = 130;
|
||||
|
||||
const fn new(label: &'static str) -> Self {
|
||||
Self { label, value: 0 }
|
||||
}
|
||||
|
||||
/// Handle input events for the age input.
|
||||
///
|
||||
/// Digits are accepted as input, with any input which would exceed the maximum age being
|
||||
/// ignored. The up/down arrow keys and 'j'/'k' keys can be used to increment/decrement the
|
||||
/// age.
|
||||
fn on_key_press(&mut self, event: KeyEvent) {
|
||||
match event.code {
|
||||
KeyCode::Char(digit @ '0'..='9') => {
|
||||
let value = self
|
||||
.value
|
||||
.saturating_mul(10)
|
||||
.saturating_add(digit as u8 - b'0');
|
||||
if value <= Self::MAX {
|
||||
self.value = value;
|
||||
}
|
||||
}
|
||||
KeyCode::Backspace => self.value /= 10,
|
||||
KeyCode::Up | KeyCode::Char('k') => self.increment(),
|
||||
KeyCode::Down | KeyCode::Char('j') => self.decrement(),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
fn increment(&mut self) {
|
||||
self.value = self.value.saturating_add(1).min(Self::MAX);
|
||||
}
|
||||
|
||||
fn decrement(&mut self) {
|
||||
self.value = self.value.saturating_sub(1);
|
||||
}
|
||||
|
||||
fn cursor_offset(&self) -> Offset {
|
||||
let x = (self.label.len() + self.value.to_string().len() + 2) as i32;
|
||||
Offset::new(x, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for &AgeField {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let constraints = [
|
||||
Constraint::Length(self.label.len() as u16 + 2),
|
||||
Constraint::Fill(1),
|
||||
];
|
||||
let [label_area, value_area] = Layout::horizontal(constraints).areas(area);
|
||||
let label = Line::from_iter([self.label, ": "]).bold();
|
||||
let value = self.value.to_string();
|
||||
label.render(label_area, buf);
|
||||
value.render(value_area, buf);
|
||||
}
|
||||
}
|
||||
17
examples/apps/mouse-drawing/Cargo.toml
Normal file
17
examples/apps/mouse-drawing/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "mouse-drawing"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
color-eyre.workspace = true
|
||||
crossterm.workspace = true
|
||||
## a collection of line drawing algorithms (e.g. Bresenham's line algorithm)
|
||||
line_drawing = "1.0.0"
|
||||
rand = "0.8.5"
|
||||
ratatui.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
9
examples/apps/mouse-drawing/README.md
Normal file
9
examples/apps/mouse-drawing/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Mouse drawing demo
|
||||
|
||||
This example shows how to receive mouse and handle mouse events.
|
||||
|
||||
To run this demo:
|
||||
|
||||
```shell
|
||||
cargo run -p mouse-drawing
|
||||
```
|
||||
123
examples/apps/mouse-drawing/src/main.rs
Normal file
123
examples/apps/mouse-drawing/src/main.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
/// A Ratatui example that demonstrates how to handle mouse events.
|
||||
///
|
||||
/// This example demonstrates how to handle mouse events in Ratatui. You can draw lines by
|
||||
/// clicking and dragging the mouse.
|
||||
///
|
||||
/// This example runs with the Ratatui library code in the branch that you are currently
|
||||
/// reading. See the [`latest`] branch for the code which works with the most recent Ratatui
|
||||
/// release.
|
||||
///
|
||||
/// [`latest`]: https://github.com/ratatui/ratatui/tree/latest
|
||||
use color_eyre::Result;
|
||||
use crossterm::{
|
||||
event::{
|
||||
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEvent,
|
||||
MouseEventKind,
|
||||
},
|
||||
execute,
|
||||
};
|
||||
use ratatui::{
|
||||
layout::{Position, Rect, Size},
|
||||
style::{Color, Stylize},
|
||||
symbols,
|
||||
text::Line,
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = MouseDrawingApp::default().run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MouseDrawingApp {
|
||||
// Whether the app should exit
|
||||
pub should_exit: bool,
|
||||
// The last known mouse position
|
||||
pub mouse_position: Option<Position>,
|
||||
// The points that have been clicked / drawn by dragging the mouse
|
||||
pub points: Vec<(Position, Color)>,
|
||||
// The color to draw with
|
||||
pub current_color: Color,
|
||||
}
|
||||
|
||||
impl MouseDrawingApp {
|
||||
fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
|
||||
execute!(std::io::stdout(), EnableMouseCapture)?;
|
||||
while !self.should_exit {
|
||||
terminal.draw(|frame| self.render(frame))?;
|
||||
self.handle_events()?;
|
||||
}
|
||||
execute!(std::io::stdout(), DisableMouseCapture)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_events(&mut self) -> Result<()> {
|
||||
match event::read()? {
|
||||
Event::Key(event) => self.on_key_event(event),
|
||||
Event::Mouse(event) => self.on_mouse_event(event),
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Quit the app if the user presses 'q' or 'Esc'
|
||||
fn on_key_event(&mut self, event: KeyEvent) {
|
||||
match event.code {
|
||||
KeyCode::Char(' ') => {
|
||||
self.current_color = Color::Rgb(rand::random(), rand::random(), rand::random());
|
||||
}
|
||||
KeyCode::Char('q') | KeyCode::Esc => self.should_exit = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds any points which were clicked or dragged to the `points` vector.
|
||||
fn on_mouse_event(&mut self, event: MouseEvent) {
|
||||
let position = Position::new(event.column, event.row);
|
||||
match event.kind {
|
||||
MouseEventKind::Down(_) => self.points.push((position, self.current_color)),
|
||||
MouseEventKind::Drag(_) => self.draw_line(position),
|
||||
_ => {}
|
||||
}
|
||||
self.mouse_position = Some(position);
|
||||
}
|
||||
|
||||
/// Draw a line between the last point and the given position
|
||||
fn draw_line(&mut self, position: Position) {
|
||||
if let Some(start) = self.points.last() {
|
||||
let (x0, y0) = (i32::from(start.0.x), i32::from(start.0.y));
|
||||
let (x1, y1) = (i32::from(position.x), i32::from(position.y));
|
||||
for (x, y) in line_drawing::Bresenham::new((x0, y0), (x1, y1)) {
|
||||
let point = (Position::new(x as u16, y as u16), self.current_color);
|
||||
self.points.push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, frame: &mut Frame) {
|
||||
// call order is important here as later elements are drawn on top of earlier elements
|
||||
self.render_points(frame);
|
||||
self.render_mouse_cursor(frame);
|
||||
let value = "Mouse Example ('Esc' to quit. Click / drag to draw. 'Space' to change color)";
|
||||
let title = Line::from(value).centered();
|
||||
frame.render_widget(title, frame.area());
|
||||
}
|
||||
|
||||
fn render_points(&self, frame: &mut Frame<'_>) {
|
||||
for (position, color) in &self.points {
|
||||
let area = Rect::from((*position, Size::new(1, 1))).clamp(frame.area());
|
||||
frame.render_widget(symbols::block::FULL.fg(*color), area);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_mouse_cursor(&self, frame: &mut Frame<'_>) {
|
||||
if let Some(position) = self.mouse_position {
|
||||
let area = Rect::from((position, Size::new(1, 1))).clamp(frame.area());
|
||||
frame.render_widget("╳".bg(self.current_color), area);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
examples/apps/weather/Cargo.toml
Normal file
15
examples/apps/weather/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "weather"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
color-eyre.workspace = true
|
||||
crossterm.workspace = true
|
||||
rand = "0.8.5"
|
||||
ratatui.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
9
examples/apps/weather/README.md
Normal file
9
examples/apps/weather/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Weather demo
|
||||
|
||||
This example shows how to render weather data using barchart widget.
|
||||
|
||||
To run this demo:
|
||||
|
||||
```shell
|
||||
cargo run -p weather
|
||||
```
|
||||
@@ -1,26 +1,21 @@
|
||||
//! # [Ratatui] `BarChart` example
|
||||
//! A Ratatui example that demonstrates how to render weather data using [`BarChart`] widget.
|
||||
//!
|
||||
//! The latest version of this example is available in the [examples] folder in the repository.
|
||||
//! Generates random temperature data for each hour of the day and renders it as a vertical bar.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//! This example runs with the Ratatui library code in the branch that you are currently reading.
|
||||
//! See the [`latest`] branch for the code which works with the most recent Ratatui release.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
|
||||
//! [`BarChart`]: https://docs.rs/ratatui/latest/ratatui/widgets/struct.BarChart.html
|
||||
|
||||
use color_eyre::Result;
|
||||
use rand::{thread_rng, Rng};
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event, KeyCode, KeyEventKind},
|
||||
layout::{Constraint, Direction, Layout},
|
||||
layout::{Constraint, Layout},
|
||||
style::{Color, Style, Stylize},
|
||||
text::Line,
|
||||
widgets::{Bar, BarChart, BarGroup, Block},
|
||||
widgets::{Bar, BarChart, BarGroup},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
|
||||
@@ -65,17 +60,12 @@ impl App {
|
||||
}
|
||||
|
||||
fn draw(&self, frame: &mut Frame) {
|
||||
let [title, vertical, horizontal] = Layout::vertical([
|
||||
Constraint::Length(1),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.spacing(1)
|
||||
.areas(frame.area());
|
||||
let [title, main] = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)])
|
||||
.spacing(1)
|
||||
.areas(frame.area());
|
||||
|
||||
frame.render_widget("Barchart".bold().into_centered_line(), title);
|
||||
frame.render_widget(vertical_barchart(&self.temperatures), vertical);
|
||||
frame.render_widget(horizontal_barchart(&self.temperatures), horizontal);
|
||||
frame.render_widget("Weather demo".bold().into_centered_line(), title);
|
||||
frame.render_widget(vertical_barchart(&self.temperatures), main);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,10 +76,8 @@ fn vertical_barchart(temperatures: &[u8]) -> BarChart {
|
||||
.enumerate()
|
||||
.map(|(hour, value)| vertical_bar(hour, value))
|
||||
.collect();
|
||||
let title = Line::from("Weather (Vertical)").centered();
|
||||
BarChart::default()
|
||||
.data(BarGroup::default().bars(&bars))
|
||||
.block(Block::new().title(title))
|
||||
.bar_width(5)
|
||||
}
|
||||
|
||||
@@ -102,32 +90,6 @@ fn vertical_bar(hour: usize, temperature: &u8) -> Bar {
|
||||
.value_style(temperature_style(*temperature).reversed())
|
||||
}
|
||||
|
||||
/// Create a horizontal bar chart from the temperatures data.
|
||||
fn horizontal_barchart(temperatures: &[u8]) -> BarChart {
|
||||
let bars: Vec<Bar> = temperatures
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(hour, value)| horizontal_bar(hour, value))
|
||||
.collect();
|
||||
let title = Line::from("Weather (Horizontal)").centered();
|
||||
BarChart::default()
|
||||
.block(Block::new().title(title))
|
||||
.data(BarGroup::default().bars(&bars))
|
||||
.bar_width(1)
|
||||
.bar_gap(0)
|
||||
.direction(Direction::Horizontal)
|
||||
}
|
||||
|
||||
fn horizontal_bar(hour: usize, temperature: &u8) -> Bar {
|
||||
let style = temperature_style(*temperature);
|
||||
Bar::default()
|
||||
.value(u64::from(*temperature))
|
||||
.label(Line::from(format!("{hour:>02}:00")))
|
||||
.text_value(format!("{temperature:>3}°"))
|
||||
.style(style)
|
||||
.value_style(style.reversed())
|
||||
}
|
||||
|
||||
/// create a yellow to red value based on the value (50-90)
|
||||
fn temperature_style(value: u8) -> Style {
|
||||
let green = (255.0 * (1.0 - f64::from(value - 50) / 40.0)) as u8;
|
||||
@@ -4,7 +4,7 @@ description = """
|
||||
Core types and traits for the Ratatui Terminal UI library.
|
||||
Widget libraries should use this crate. Applications should use the main Ratatui crate.
|
||||
"""
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.2"
|
||||
readme = "README.md"
|
||||
authors.workspace = true
|
||||
documentation.workspace = true
|
||||
@@ -24,6 +24,9 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
[features]
|
||||
default = []
|
||||
|
||||
## enables conversions to / from colors, modifiers, and styles in the ['anstyle'] crate
|
||||
anstyle = ["dep:anstyle"]
|
||||
|
||||
## enables conversions from colors in the [`palette`] crate to [`Color`](crate::style::Color).
|
||||
palette = ["dep:palette"]
|
||||
|
||||
@@ -40,6 +43,7 @@ scrolling-regions = []
|
||||
serde = ["dep:serde", "bitflags/serde", "compact_str/serde"]
|
||||
|
||||
[dependencies]
|
||||
anstyle = { version = "1", optional = true }
|
||||
bitflags = "2.3"
|
||||
cassowary = "0.3"
|
||||
compact_str = "0.8.0"
|
||||
@@ -51,20 +55,15 @@ palette = { version = "0.7.6", optional = true }
|
||||
paste = "1.0.2"
|
||||
serde = { workspace = true, optional = true }
|
||||
strum.workspace = true
|
||||
thiserror = "2"
|
||||
unicode-segmentation.workspace = true
|
||||
unicode-truncate = "2"
|
||||
unicode-width.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions.workspace = true
|
||||
ratatui = { workspace = true, features = ["crossterm", "termwiz"] }
|
||||
rstest.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
[target.'cfg(not(windows))'.dev-dependencies]
|
||||
ratatui = { workspace = true, features = ["termion"] }
|
||||
|
||||
[lints.clippy]
|
||||
# we often split up a module into multiple files with the main type in a file named after the
|
||||
# module, so we want to allow this pattern
|
||||
module_inception = "allow"
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! ```rust,ignore
|
||||
//! use std::io::stdout;
|
||||
//!
|
||||
//! use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
@@ -171,7 +171,7 @@ pub trait Backend {
|
||||
/// See also [`show_cursor`].
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use ratatui::backend::{TestBackend};
|
||||
/// # let mut backend = TestBackend::new(80, 25);
|
||||
/// use ratatui::backend::Backend;
|
||||
@@ -208,7 +208,7 @@ pub trait Backend {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use ratatui::backend::{TestBackend};
|
||||
/// # let mut backend = TestBackend::new(80, 25);
|
||||
/// use ratatui::{backend::Backend, layout::Position};
|
||||
@@ -241,7 +241,7 @@ pub trait Backend {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// ```rust,ignore
|
||||
/// # use ratatui::backend::{TestBackend};
|
||||
/// # let mut backend = TestBackend::new(80, 25);
|
||||
/// use ratatui::backend::Backend;
|
||||
@@ -259,7 +259,7 @@ pub trait Backend {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// ```rust,ignore
|
||||
/// # use ratatui::{backend::{TestBackend}};
|
||||
/// # let mut backend = TestBackend::new(80, 25);
|
||||
/// use ratatui::backend::{Backend, ClearType};
|
||||
@@ -293,7 +293,7 @@ pub trait Backend {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use ratatui::{backend::{TestBackend}};
|
||||
/// # let backend = TestBackend::new(80, 25);
|
||||
/// use ratatui::{backend::Backend, layout::Size};
|
||||
|
||||
@@ -23,7 +23,7 @@ use crate::{
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// use ratatui::backend::{Backend, TestBackend};
|
||||
///
|
||||
/// let mut backend = TestBackend::new(10, 2);
|
||||
|
||||
@@ -345,7 +345,7 @@ impl Buffer {
|
||||
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)
|
||||
.filter(|symbol| !symbol.contains(|char: char| char.is_control()))
|
||||
.filter(|symbol| !symbol.contains(char::is_control))
|
||||
.map(|symbol| (symbol, symbol.width() as u16))
|
||||
.filter(|(_symbol, width)| *width > 0)
|
||||
.map_while(|(symbol, width)| {
|
||||
|
||||
@@ -40,6 +40,16 @@ pub struct Offset {
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
impl Offset {
|
||||
/// A zero offset
|
||||
pub const ZERO: Self = Self { x: 0, y: 0 };
|
||||
|
||||
/// Creates a new `Offset` with the given values.
|
||||
pub const fn new(x: i32, y: i32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Rect {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}x{}+{}+{}", self.width, self.height, self.x, self.y)
|
||||
|
||||
@@ -43,5 +43,6 @@ pub mod buffer;
|
||||
pub mod layout;
|
||||
pub mod style;
|
||||
pub mod symbols;
|
||||
pub mod terminal;
|
||||
pub mod text;
|
||||
pub mod widgets;
|
||||
|
||||
@@ -79,6 +79,8 @@ pub use color::{Color, ParseColorError};
|
||||
use stylize::ColorDebugKind;
|
||||
pub use stylize::{Styled, Stylize};
|
||||
|
||||
#[cfg(feature = "anstyle")]
|
||||
mod anstyle;
|
||||
mod color;
|
||||
pub mod palette;
|
||||
#[cfg(feature = "palette")]
|
||||
|
||||
331
ratatui-core/src/style/anstyle.rs
Normal file
331
ratatui-core/src/style/anstyle.rs
Normal file
@@ -0,0 +1,331 @@
|
||||
//! This module contains conversion functions for styles from the `anstyle` crate.
|
||||
use anstyle::{Ansi256Color, AnsiColor, Effects, RgbColor};
|
||||
use thiserror::Error;
|
||||
|
||||
use super::{Color, Modifier, Style};
|
||||
|
||||
/// Error type for converting between `anstyle` colors and `Color`
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
pub enum TryFromColorError {
|
||||
#[error("cannot convert Ratatui Color to an Ansi256Color as it is not an indexed color")]
|
||||
Ansi256,
|
||||
#[error("cannot convert Ratatui Color to AnsiColor as it is not a 4-bit color")]
|
||||
Ansi,
|
||||
#[error("cannot convert Ratatui Color to RgbColor as it is not an RGB color")]
|
||||
RgbColor,
|
||||
}
|
||||
|
||||
impl From<Ansi256Color> for Color {
|
||||
fn from(color: Ansi256Color) -> Self {
|
||||
Self::Indexed(color.index())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Color> for Ansi256Color {
|
||||
type Error = TryFromColorError;
|
||||
|
||||
fn try_from(color: Color) -> Result<Self, Self::Error> {
|
||||
match color {
|
||||
Color::Indexed(index) => Ok(Self(index)),
|
||||
_ => Err(TryFromColorError::Ansi256),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnsiColor> for Color {
|
||||
fn from(value: AnsiColor) -> Self {
|
||||
match value {
|
||||
AnsiColor::Black => Self::Black,
|
||||
AnsiColor::Red => Self::Red,
|
||||
AnsiColor::Green => Self::Green,
|
||||
AnsiColor::Yellow => Self::Yellow,
|
||||
AnsiColor::Blue => Self::Blue,
|
||||
AnsiColor::Magenta => Self::Magenta,
|
||||
AnsiColor::Cyan => Self::Cyan,
|
||||
AnsiColor::White => Self::Gray,
|
||||
AnsiColor::BrightBlack => Self::DarkGray,
|
||||
AnsiColor::BrightRed => Self::LightRed,
|
||||
AnsiColor::BrightGreen => Self::LightGreen,
|
||||
AnsiColor::BrightYellow => Self::LightYellow,
|
||||
AnsiColor::BrightBlue => Self::LightBlue,
|
||||
AnsiColor::BrightMagenta => Self::LightMagenta,
|
||||
AnsiColor::BrightCyan => Self::LightCyan,
|
||||
AnsiColor::BrightWhite => Self::White,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Color> for AnsiColor {
|
||||
type Error = TryFromColorError;
|
||||
|
||||
fn try_from(color: Color) -> Result<Self, Self::Error> {
|
||||
match color {
|
||||
Color::Black => Ok(Self::Black),
|
||||
Color::Red => Ok(Self::Red),
|
||||
Color::Green => Ok(Self::Green),
|
||||
Color::Yellow => Ok(Self::Yellow),
|
||||
Color::Blue => Ok(Self::Blue),
|
||||
Color::Magenta => Ok(Self::Magenta),
|
||||
Color::Cyan => Ok(Self::Cyan),
|
||||
Color::Gray => Ok(Self::White),
|
||||
Color::DarkGray => Ok(Self::BrightBlack),
|
||||
Color::LightRed => Ok(Self::BrightRed),
|
||||
Color::LightGreen => Ok(Self::BrightGreen),
|
||||
Color::LightYellow => Ok(Self::BrightYellow),
|
||||
Color::LightBlue => Ok(Self::BrightBlue),
|
||||
Color::LightMagenta => Ok(Self::BrightMagenta),
|
||||
Color::LightCyan => Ok(Self::BrightCyan),
|
||||
Color::White => Ok(Self::BrightWhite),
|
||||
_ => Err(TryFromColorError::Ansi),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RgbColor> for Color {
|
||||
fn from(color: RgbColor) -> Self {
|
||||
Self::Rgb(color.r(), color.g(), color.b())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Color> for RgbColor {
|
||||
type Error = TryFromColorError;
|
||||
|
||||
fn try_from(color: Color) -> Result<Self, Self::Error> {
|
||||
match color {
|
||||
Color::Rgb(red, green, blue) => Ok(Self(red, green, blue)),
|
||||
_ => Err(TryFromColorError::RgbColor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<anstyle::Color> for Color {
|
||||
fn from(color: anstyle::Color) -> Self {
|
||||
match color {
|
||||
anstyle::Color::Ansi(ansi_color) => Self::from(ansi_color),
|
||||
anstyle::Color::Ansi256(ansi256_color) => Self::from(ansi256_color),
|
||||
anstyle::Color::Rgb(rgb_color) => Self::from(rgb_color),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for anstyle::Color {
|
||||
fn from(color: Color) -> Self {
|
||||
match color {
|
||||
Color::Rgb(_, _, _) => Self::Rgb(RgbColor::try_from(color).unwrap()),
|
||||
Color::Indexed(_) => Self::Ansi256(Ansi256Color::try_from(color).unwrap()),
|
||||
_ => Self::Ansi(AnsiColor::try_from(color).unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Effects> for Modifier {
|
||||
fn from(effect: Effects) -> Self {
|
||||
let mut modifier = Self::empty();
|
||||
if effect.contains(Effects::BOLD) {
|
||||
modifier |= Self::BOLD;
|
||||
}
|
||||
if effect.contains(Effects::DIMMED) {
|
||||
modifier |= Self::DIM;
|
||||
}
|
||||
if effect.contains(Effects::ITALIC) {
|
||||
modifier |= Self::ITALIC;
|
||||
}
|
||||
if effect.contains(Effects::UNDERLINE)
|
||||
|| effect.contains(Effects::DOUBLE_UNDERLINE)
|
||||
|| effect.contains(Effects::CURLY_UNDERLINE)
|
||||
|| effect.contains(Effects::DOTTED_UNDERLINE)
|
||||
|| effect.contains(Effects::DASHED_UNDERLINE)
|
||||
{
|
||||
modifier |= Self::UNDERLINED;
|
||||
}
|
||||
if effect.contains(Effects::BLINK) {
|
||||
modifier |= Self::SLOW_BLINK;
|
||||
}
|
||||
if effect.contains(Effects::INVERT) {
|
||||
modifier |= Self::REVERSED;
|
||||
}
|
||||
if effect.contains(Effects::HIDDEN) {
|
||||
modifier |= Self::HIDDEN;
|
||||
}
|
||||
if effect.contains(Effects::STRIKETHROUGH) {
|
||||
modifier |= Self::CROSSED_OUT;
|
||||
}
|
||||
modifier
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Modifier> for Effects {
|
||||
fn from(modifier: Modifier) -> Self {
|
||||
let mut effects = Self::new();
|
||||
if modifier.contains(Modifier::BOLD) {
|
||||
effects |= Self::BOLD;
|
||||
}
|
||||
if modifier.contains(Modifier::DIM) {
|
||||
effects |= Self::DIMMED;
|
||||
}
|
||||
if modifier.contains(Modifier::ITALIC) {
|
||||
effects |= Self::ITALIC;
|
||||
}
|
||||
if modifier.contains(Modifier::UNDERLINED) {
|
||||
effects |= Self::UNDERLINE;
|
||||
}
|
||||
if modifier.contains(Modifier::SLOW_BLINK) || modifier.contains(Modifier::RAPID_BLINK) {
|
||||
effects |= Self::BLINK;
|
||||
}
|
||||
if modifier.contains(Modifier::REVERSED) {
|
||||
effects |= Self::INVERT;
|
||||
}
|
||||
if modifier.contains(Modifier::HIDDEN) {
|
||||
effects |= Self::HIDDEN;
|
||||
}
|
||||
if modifier.contains(Modifier::CROSSED_OUT) {
|
||||
effects |= Self::STRIKETHROUGH;
|
||||
}
|
||||
effects
|
||||
}
|
||||
}
|
||||
|
||||
impl From<anstyle::Style> for Style {
|
||||
fn from(style: anstyle::Style) -> Self {
|
||||
Self {
|
||||
fg: style.get_fg_color().map(Color::from),
|
||||
bg: style.get_bg_color().map(Color::from),
|
||||
add_modifier: style.get_effects().into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Style> for anstyle::Style {
|
||||
fn from(style: Style) -> Self {
|
||||
let mut anstyle_style = Self::new();
|
||||
if let Some(fg) = style.fg {
|
||||
let fg = anstyle::Color::from(fg);
|
||||
anstyle_style = anstyle_style.fg_color(Some(fg));
|
||||
}
|
||||
if let Some(bg) = style.bg {
|
||||
let bg = anstyle::Color::from(bg);
|
||||
anstyle_style = anstyle_style.bg_color(Some(bg));
|
||||
}
|
||||
anstyle_style = anstyle_style.effects(style.add_modifier.into());
|
||||
anstyle_style
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn anstyle_to_color() {
|
||||
let anstyle_color = Ansi256Color(42);
|
||||
let color = Color::from(anstyle_color);
|
||||
assert_eq!(color, Color::Indexed(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_to_ansi256color() {
|
||||
let color = Color::Indexed(42);
|
||||
let anstyle_color = Ansi256Color::try_from(color);
|
||||
assert_eq!(anstyle_color, Ok(Ansi256Color(42)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_to_ansi256color_error() {
|
||||
let color = Color::Rgb(0, 0, 0);
|
||||
let anstyle_color = Ansi256Color::try_from(color);
|
||||
assert_eq!(anstyle_color, Err(TryFromColorError::Ansi256));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ansi_color_to_color() {
|
||||
let ansi_color = AnsiColor::Red;
|
||||
let color = Color::from(ansi_color);
|
||||
assert_eq!(color, Color::Red);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_to_ansicolor() {
|
||||
let color = Color::Red;
|
||||
let ansi_color = AnsiColor::try_from(color);
|
||||
assert_eq!(ansi_color, Ok(AnsiColor::Red));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_to_ansicolor_error() {
|
||||
let color = Color::Rgb(0, 0, 0);
|
||||
let ansi_color = AnsiColor::try_from(color);
|
||||
assert_eq!(ansi_color, Err(TryFromColorError::Ansi));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rgb_color_to_color() {
|
||||
let rgb_color = RgbColor(255, 0, 0);
|
||||
let color = Color::from(rgb_color);
|
||||
assert_eq!(color, Color::Rgb(255, 0, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_to_rgbcolor() {
|
||||
let color = Color::Rgb(255, 0, 0);
|
||||
let rgb_color = RgbColor::try_from(color);
|
||||
assert_eq!(rgb_color, Ok(RgbColor(255, 0, 0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_to_rgbcolor_error() {
|
||||
let color = Color::Indexed(42);
|
||||
let rgb_color = RgbColor::try_from(color);
|
||||
assert_eq!(rgb_color, Err(TryFromColorError::RgbColor));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn effects_to_modifier() {
|
||||
let effects = Effects::BOLD | Effects::ITALIC;
|
||||
let modifier = Modifier::from(effects);
|
||||
assert!(modifier.contains(Modifier::BOLD));
|
||||
assert!(modifier.contains(Modifier::ITALIC));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn modifier_to_effects() {
|
||||
let modifier = Modifier::BOLD | Modifier::ITALIC;
|
||||
let effects = Effects::from(modifier);
|
||||
assert!(effects.contains(Effects::BOLD));
|
||||
assert!(effects.contains(Effects::ITALIC));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anstyle_style_to_style() {
|
||||
let anstyle_style = anstyle::Style::new()
|
||||
.fg_color(Some(anstyle::Color::Ansi(AnsiColor::Red)))
|
||||
.bg_color(Some(anstyle::Color::Ansi(AnsiColor::Blue)))
|
||||
.effects(Effects::BOLD | Effects::ITALIC);
|
||||
let style = Style::from(anstyle_style);
|
||||
assert_eq!(style.fg, Some(Color::Red));
|
||||
assert_eq!(style.bg, Some(Color::Blue));
|
||||
assert!(style.add_modifier.contains(Modifier::BOLD));
|
||||
assert!(style.add_modifier.contains(Modifier::ITALIC));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn style_to_anstyle_style() {
|
||||
let style = Style {
|
||||
fg: Some(Color::Red),
|
||||
bg: Some(Color::Blue),
|
||||
add_modifier: Modifier::BOLD | Modifier::ITALIC,
|
||||
..Default::default()
|
||||
};
|
||||
let anstyle_style = anstyle::Style::from(style);
|
||||
assert_eq!(
|
||||
anstyle_style.get_fg_color(),
|
||||
Some(anstyle::Color::Ansi(AnsiColor::Red))
|
||||
);
|
||||
assert_eq!(
|
||||
anstyle_style.get_bg_color(),
|
||||
Some(anstyle::Color::Ansi(AnsiColor::Blue))
|
||||
);
|
||||
assert!(anstyle_style.get_effects().contains(Effects::BOLD));
|
||||
assert!(anstyle_style.get_effects().contains(Effects::ITALIC));
|
||||
}
|
||||
}
|
||||
@@ -250,7 +250,7 @@ pub trait Stylize<'a, T>: Sized {
|
||||
modifier!(crossed_out);
|
||||
}
|
||||
|
||||
impl<'a, T, U> Stylize<'a, T> for U
|
||||
impl<T, U> Stylize<'_, T> for U
|
||||
where
|
||||
U: Styled<Item = T>,
|
||||
{
|
||||
|
||||
@@ -1,238 +1,13 @@
|
||||
//! Symbols and markers for drawing various widgets.
|
||||
|
||||
use strum::{Display, EnumString};
|
||||
pub use marker::{Marker, DOT};
|
||||
|
||||
pub mod bar;
|
||||
pub mod block;
|
||||
pub mod border;
|
||||
pub mod braille;
|
||||
pub mod half_block;
|
||||
pub mod line;
|
||||
|
||||
pub mod block {
|
||||
pub const FULL: &str = "█";
|
||||
pub const SEVEN_EIGHTHS: &str = "▉";
|
||||
pub const THREE_QUARTERS: &str = "▊";
|
||||
pub const FIVE_EIGHTHS: &str = "▋";
|
||||
pub const HALF: &str = "▌";
|
||||
pub const THREE_EIGHTHS: &str = "▍";
|
||||
pub const ONE_QUARTER: &str = "▎";
|
||||
pub const ONE_EIGHTH: &str = "▏";
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Set {
|
||||
pub full: &'static str,
|
||||
pub seven_eighths: &'static str,
|
||||
pub three_quarters: &'static str,
|
||||
pub five_eighths: &'static str,
|
||||
pub half: &'static str,
|
||||
pub three_eighths: &'static str,
|
||||
pub one_quarter: &'static str,
|
||||
pub one_eighth: &'static str,
|
||||
pub empty: &'static str,
|
||||
}
|
||||
|
||||
impl Default for Set {
|
||||
fn default() -> Self {
|
||||
NINE_LEVELS
|
||||
}
|
||||
}
|
||||
|
||||
pub const THREE_LEVELS: Set = Set {
|
||||
full: FULL,
|
||||
seven_eighths: FULL,
|
||||
three_quarters: HALF,
|
||||
five_eighths: HALF,
|
||||
half: HALF,
|
||||
three_eighths: HALF,
|
||||
one_quarter: HALF,
|
||||
one_eighth: " ",
|
||||
empty: " ",
|
||||
};
|
||||
|
||||
pub const NINE_LEVELS: Set = Set {
|
||||
full: FULL,
|
||||
seven_eighths: SEVEN_EIGHTHS,
|
||||
three_quarters: THREE_QUARTERS,
|
||||
five_eighths: FIVE_EIGHTHS,
|
||||
half: HALF,
|
||||
three_eighths: THREE_EIGHTHS,
|
||||
one_quarter: ONE_QUARTER,
|
||||
one_eighth: ONE_EIGHTH,
|
||||
empty: " ",
|
||||
};
|
||||
}
|
||||
|
||||
pub mod half_block {
|
||||
pub const UPPER: char = '▀';
|
||||
pub const LOWER: char = '▄';
|
||||
pub const FULL: char = '█';
|
||||
}
|
||||
|
||||
pub mod bar {
|
||||
pub const FULL: &str = "█";
|
||||
pub const SEVEN_EIGHTHS: &str = "▇";
|
||||
pub const THREE_QUARTERS: &str = "▆";
|
||||
pub const FIVE_EIGHTHS: &str = "▅";
|
||||
pub const HALF: &str = "▄";
|
||||
pub const THREE_EIGHTHS: &str = "▃";
|
||||
pub const ONE_QUARTER: &str = "▂";
|
||||
pub const ONE_EIGHTH: &str = "▁";
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Set {
|
||||
pub full: &'static str,
|
||||
pub seven_eighths: &'static str,
|
||||
pub three_quarters: &'static str,
|
||||
pub five_eighths: &'static str,
|
||||
pub half: &'static str,
|
||||
pub three_eighths: &'static str,
|
||||
pub one_quarter: &'static str,
|
||||
pub one_eighth: &'static str,
|
||||
pub empty: &'static str,
|
||||
}
|
||||
|
||||
impl Default for Set {
|
||||
fn default() -> Self {
|
||||
NINE_LEVELS
|
||||
}
|
||||
}
|
||||
|
||||
pub const THREE_LEVELS: Set = Set {
|
||||
full: FULL,
|
||||
seven_eighths: FULL,
|
||||
three_quarters: HALF,
|
||||
five_eighths: HALF,
|
||||
half: HALF,
|
||||
three_eighths: HALF,
|
||||
one_quarter: HALF,
|
||||
one_eighth: " ",
|
||||
empty: " ",
|
||||
};
|
||||
|
||||
pub const NINE_LEVELS: Set = Set {
|
||||
full: FULL,
|
||||
seven_eighths: SEVEN_EIGHTHS,
|
||||
three_quarters: THREE_QUARTERS,
|
||||
five_eighths: FIVE_EIGHTHS,
|
||||
half: HALF,
|
||||
three_eighths: THREE_EIGHTHS,
|
||||
one_quarter: ONE_QUARTER,
|
||||
one_eighth: ONE_EIGHTH,
|
||||
empty: " ",
|
||||
};
|
||||
}
|
||||
|
||||
pub const DOT: &str = "•";
|
||||
|
||||
pub mod braille {
|
||||
pub const BLANK: u16 = 0x2800;
|
||||
pub const DOTS: [[u16; 2]; 4] = [
|
||||
[0x0001, 0x0008],
|
||||
[0x0002, 0x0010],
|
||||
[0x0004, 0x0020],
|
||||
[0x0040, 0x0080],
|
||||
];
|
||||
}
|
||||
|
||||
/// Marker to use when plotting data points
|
||||
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum Marker {
|
||||
/// One point per cell in shape of dot (`•`)
|
||||
#[default]
|
||||
Dot,
|
||||
/// One point per cell in shape of a block (`█`)
|
||||
Block,
|
||||
/// One point per cell in the shape of a bar (`▄`)
|
||||
Bar,
|
||||
/// Use the [Unicode Braille Patterns](https://en.wikipedia.org/wiki/Braille_Patterns) block to
|
||||
/// represent data points.
|
||||
///
|
||||
/// This is a 2x4 grid of dots, where each dot can be either on or off.
|
||||
///
|
||||
/// Note: Support for this marker is limited to terminals and fonts that support Unicode
|
||||
/// Braille Patterns. If your terminal does not support this, you will see unicode replacement
|
||||
/// characters (`<60>`) instead of Braille dots (`⠓`, `⣇`, `⣿`).
|
||||
Braille,
|
||||
/// Use the unicode block and half block characters (`█`, `▄`, and `▀`) to represent points in
|
||||
/// a grid that is double the resolution of the terminal. Because each terminal cell is
|
||||
/// generally about twice as tall as it is wide, this allows for a square grid of pixels.
|
||||
HalfBlock,
|
||||
}
|
||||
|
||||
pub mod scrollbar {
|
||||
use crate::symbols::{block, line};
|
||||
|
||||
/// Scrollbar Set
|
||||
/// ```text
|
||||
/// <--▮------->
|
||||
/// ^ ^ ^ ^
|
||||
/// │ │ │ └ end
|
||||
/// │ │ └──── track
|
||||
/// │ └──────── thumb
|
||||
/// └─────────── begin
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Set {
|
||||
pub track: &'static str,
|
||||
pub thumb: &'static str,
|
||||
pub begin: &'static str,
|
||||
pub end: &'static str,
|
||||
}
|
||||
|
||||
pub const DOUBLE_VERTICAL: Set = Set {
|
||||
track: line::DOUBLE_VERTICAL,
|
||||
thumb: block::FULL,
|
||||
begin: "▲",
|
||||
end: "▼",
|
||||
};
|
||||
|
||||
pub const DOUBLE_HORIZONTAL: Set = Set {
|
||||
track: line::DOUBLE_HORIZONTAL,
|
||||
thumb: block::FULL,
|
||||
begin: "◄",
|
||||
end: "►",
|
||||
};
|
||||
|
||||
pub const VERTICAL: Set = Set {
|
||||
track: line::VERTICAL,
|
||||
thumb: block::FULL,
|
||||
begin: "↑",
|
||||
end: "↓",
|
||||
};
|
||||
|
||||
pub const HORIZONTAL: Set = Set {
|
||||
track: line::HORIZONTAL,
|
||||
thumb: block::FULL,
|
||||
begin: "←",
|
||||
end: "→",
|
||||
};
|
||||
}
|
||||
|
||||
pub mod shade {
|
||||
pub const EMPTY: &str = " ";
|
||||
pub const LIGHT: &str = "░";
|
||||
pub const MEDIUM: &str = "▒";
|
||||
pub const DARK: &str = "▓";
|
||||
pub const FULL: &str = "█";
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use strum::ParseError;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn marker_tostring() {
|
||||
assert_eq!(Marker::Dot.to_string(), "Dot");
|
||||
assert_eq!(Marker::Block.to_string(), "Block");
|
||||
assert_eq!(Marker::Bar.to_string(), "Bar");
|
||||
assert_eq!(Marker::Braille.to_string(), "Braille");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marker_from_str() {
|
||||
assert_eq!("Dot".parse::<Marker>(), Ok(Marker::Dot));
|
||||
assert_eq!("Block".parse::<Marker>(), Ok(Marker::Block));
|
||||
assert_eq!("Bar".parse::<Marker>(), Ok(Marker::Bar));
|
||||
assert_eq!("Braille".parse::<Marker>(), Ok(Marker::Braille));
|
||||
assert_eq!("".parse::<Marker>(), Err(ParseError::VariantNotFound));
|
||||
}
|
||||
}
|
||||
pub mod marker;
|
||||
pub mod scrollbar;
|
||||
pub mod shade;
|
||||
|
||||
51
ratatui-core/src/symbols/bar.rs
Normal file
51
ratatui-core/src/symbols/bar.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
pub const FULL: &str = "█";
|
||||
pub const SEVEN_EIGHTHS: &str = "▇";
|
||||
pub const THREE_QUARTERS: &str = "▆";
|
||||
pub const FIVE_EIGHTHS: &str = "▅";
|
||||
pub const HALF: &str = "▄";
|
||||
pub const THREE_EIGHTHS: &str = "▃";
|
||||
pub const ONE_QUARTER: &str = "▂";
|
||||
pub const ONE_EIGHTH: &str = "▁";
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Set {
|
||||
pub full: &'static str,
|
||||
pub seven_eighths: &'static str,
|
||||
pub three_quarters: &'static str,
|
||||
pub five_eighths: &'static str,
|
||||
pub half: &'static str,
|
||||
pub three_eighths: &'static str,
|
||||
pub one_quarter: &'static str,
|
||||
pub one_eighth: &'static str,
|
||||
pub empty: &'static str,
|
||||
}
|
||||
|
||||
impl Default for Set {
|
||||
fn default() -> Self {
|
||||
NINE_LEVELS
|
||||
}
|
||||
}
|
||||
|
||||
pub const THREE_LEVELS: Set = Set {
|
||||
full: FULL,
|
||||
seven_eighths: FULL,
|
||||
three_quarters: HALF,
|
||||
five_eighths: HALF,
|
||||
half: HALF,
|
||||
three_eighths: HALF,
|
||||
one_quarter: HALF,
|
||||
one_eighth: " ",
|
||||
empty: " ",
|
||||
};
|
||||
|
||||
pub const NINE_LEVELS: Set = Set {
|
||||
full: FULL,
|
||||
seven_eighths: SEVEN_EIGHTHS,
|
||||
three_quarters: THREE_QUARTERS,
|
||||
five_eighths: FIVE_EIGHTHS,
|
||||
half: HALF,
|
||||
three_eighths: THREE_EIGHTHS,
|
||||
one_quarter: ONE_QUARTER,
|
||||
one_eighth: ONE_EIGHTH,
|
||||
empty: " ",
|
||||
};
|
||||
51
ratatui-core/src/symbols/block.rs
Normal file
51
ratatui-core/src/symbols/block.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
pub const FULL: &str = "█";
|
||||
pub const SEVEN_EIGHTHS: &str = "▉";
|
||||
pub const THREE_QUARTERS: &str = "▊";
|
||||
pub const FIVE_EIGHTHS: &str = "▋";
|
||||
pub const HALF: &str = "▌";
|
||||
pub const THREE_EIGHTHS: &str = "▍";
|
||||
pub const ONE_QUARTER: &str = "▎";
|
||||
pub const ONE_EIGHTH: &str = "▏";
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Set {
|
||||
pub full: &'static str,
|
||||
pub seven_eighths: &'static str,
|
||||
pub three_quarters: &'static str,
|
||||
pub five_eighths: &'static str,
|
||||
pub half: &'static str,
|
||||
pub three_eighths: &'static str,
|
||||
pub one_quarter: &'static str,
|
||||
pub one_eighth: &'static str,
|
||||
pub empty: &'static str,
|
||||
}
|
||||
|
||||
impl Default for Set {
|
||||
fn default() -> Self {
|
||||
NINE_LEVELS
|
||||
}
|
||||
}
|
||||
|
||||
pub const THREE_LEVELS: Set = Set {
|
||||
full: FULL,
|
||||
seven_eighths: FULL,
|
||||
three_quarters: HALF,
|
||||
five_eighths: HALF,
|
||||
half: HALF,
|
||||
three_eighths: HALF,
|
||||
one_quarter: HALF,
|
||||
one_eighth: " ",
|
||||
empty: " ",
|
||||
};
|
||||
|
||||
pub const NINE_LEVELS: Set = Set {
|
||||
full: FULL,
|
||||
seven_eighths: SEVEN_EIGHTHS,
|
||||
three_quarters: THREE_QUARTERS,
|
||||
five_eighths: FIVE_EIGHTHS,
|
||||
half: HALF,
|
||||
three_eighths: THREE_EIGHTHS,
|
||||
one_quarter: ONE_QUARTER,
|
||||
one_eighth: ONE_EIGHTH,
|
||||
empty: " ",
|
||||
};
|
||||
7
ratatui-core/src/symbols/braille.rs
Normal file
7
ratatui-core/src/symbols/braille.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub const BLANK: u16 = 0x2800;
|
||||
pub const DOTS: [[u16; 2]; 4] = [
|
||||
[0x0001, 0x0008],
|
||||
[0x0002, 0x0010],
|
||||
[0x0004, 0x0020],
|
||||
[0x0040, 0x0080],
|
||||
];
|
||||
3
ratatui-core/src/symbols/half_block.rs
Normal file
3
ratatui-core/src/symbols/half_block.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub const UPPER: char = '▀';
|
||||
pub const LOWER: char = '▄';
|
||||
pub const FULL: char = '█';
|
||||
52
ratatui-core/src/symbols/marker.rs
Normal file
52
ratatui-core/src/symbols/marker.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use strum::{Display, EnumString};
|
||||
|
||||
pub const DOT: &str = "•";
|
||||
|
||||
/// Marker to use when plotting data points
|
||||
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub enum Marker {
|
||||
/// One point per cell in shape of dot (`•`)
|
||||
#[default]
|
||||
Dot,
|
||||
/// One point per cell in shape of a block (`█`)
|
||||
Block,
|
||||
/// One point per cell in the shape of a bar (`▄`)
|
||||
Bar,
|
||||
/// Use the [Unicode Braille Patterns](https://en.wikipedia.org/wiki/Braille_Patterns) block to
|
||||
/// represent data points.
|
||||
///
|
||||
/// This is a 2x4 grid of dots, where each dot can be either on or off.
|
||||
///
|
||||
/// Note: Support for this marker is limited to terminals and fonts that support Unicode
|
||||
/// Braille Patterns. If your terminal does not support this, you will see unicode replacement
|
||||
/// characters (`<60>`) instead of Braille dots (`⠓`, `⣇`, `⣿`).
|
||||
Braille,
|
||||
/// Use the unicode block and half block characters (`█`, `▄`, and `▀`) to represent points in
|
||||
/// a grid that is double the resolution of the terminal. Because each terminal cell is
|
||||
/// generally about twice as tall as it is wide, this allows for a square grid of pixels.
|
||||
HalfBlock,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use strum::ParseError;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn marker_tostring() {
|
||||
assert_eq!(Marker::Dot.to_string(), "Dot");
|
||||
assert_eq!(Marker::Block.to_string(), "Block");
|
||||
assert_eq!(Marker::Bar.to_string(), "Bar");
|
||||
assert_eq!(Marker::Braille.to_string(), "Braille");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn marker_from_str() {
|
||||
assert_eq!("Dot".parse::<Marker>(), Ok(Marker::Dot));
|
||||
assert_eq!("Block".parse::<Marker>(), Ok(Marker::Block));
|
||||
assert_eq!("Bar".parse::<Marker>(), Ok(Marker::Bar));
|
||||
assert_eq!("Braille".parse::<Marker>(), Ok(Marker::Braille));
|
||||
assert_eq!("".parse::<Marker>(), Err(ParseError::VariantNotFound));
|
||||
}
|
||||
}
|
||||
46
ratatui-core/src/symbols/scrollbar.rs
Normal file
46
ratatui-core/src/symbols/scrollbar.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use crate::symbols::{block, line};
|
||||
|
||||
/// Scrollbar Set
|
||||
/// ```text
|
||||
/// <--▮------->
|
||||
/// ^ ^ ^ ^
|
||||
/// │ │ │ └ end
|
||||
/// │ │ └──── track
|
||||
/// │ └──────── thumb
|
||||
/// └─────────── begin
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Set {
|
||||
pub track: &'static str,
|
||||
pub thumb: &'static str,
|
||||
pub begin: &'static str,
|
||||
pub end: &'static str,
|
||||
}
|
||||
|
||||
pub const DOUBLE_VERTICAL: Set = Set {
|
||||
track: line::DOUBLE_VERTICAL,
|
||||
thumb: block::FULL,
|
||||
begin: "▲",
|
||||
end: "▼",
|
||||
};
|
||||
|
||||
pub const DOUBLE_HORIZONTAL: Set = Set {
|
||||
track: line::DOUBLE_HORIZONTAL,
|
||||
thumb: block::FULL,
|
||||
begin: "◄",
|
||||
end: "►",
|
||||
};
|
||||
|
||||
pub const VERTICAL: Set = Set {
|
||||
track: line::VERTICAL,
|
||||
thumb: block::FULL,
|
||||
begin: "↑",
|
||||
end: "↓",
|
||||
};
|
||||
|
||||
pub const HORIZONTAL: Set = Set {
|
||||
track: line::HORIZONTAL,
|
||||
thumb: block::FULL,
|
||||
begin: "←",
|
||||
end: "→",
|
||||
};
|
||||
5
ratatui-core/src/symbols/shade.rs
Normal file
5
ratatui-core/src/symbols/shade.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub const EMPTY: &str = " ";
|
||||
pub const LIGHT: &str = "░";
|
||||
pub const MEDIUM: &str = "▒";
|
||||
pub const DARK: &str = "▓";
|
||||
pub const FULL: &str = "█";
|
||||
@@ -10,7 +10,7 @@
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! ```rust,ignore
|
||||
//! use std::io::stdout;
|
||||
//!
|
||||
//! use ratatui::{backend::CrosstermBackend, widgets::Paragraph, Terminal};
|
||||
@@ -32,15 +32,9 @@
|
||||
//! [`Buffer`]: crate::buffer::Buffer
|
||||
|
||||
mod frame;
|
||||
#[cfg(feature = "crossterm")]
|
||||
mod init;
|
||||
mod terminal;
|
||||
mod viewport;
|
||||
|
||||
pub use frame::{CompletedFrame, Frame};
|
||||
#[cfg(feature = "crossterm")]
|
||||
pub use init::{
|
||||
init, init_with_options, restore, try_init, try_init_with_options, try_restore, DefaultTerminal,
|
||||
};
|
||||
pub use terminal::{Options as TerminalOptions, Terminal};
|
||||
pub use viewport::Viewport;
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
buffer::Buffer,
|
||||
layout::{Position, Rect},
|
||||
widgets::{StatefulWidget, StatefulWidgetRef, Widget, WidgetRef},
|
||||
widgets::{StatefulWidget, Widget},
|
||||
};
|
||||
|
||||
/// A consistent view into the terminal state for rendering a single frame.
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
/// to the terminal. This avoids drawing redundant cells.
|
||||
///
|
||||
/// [`Buffer`]: crate::buffer::Buffer
|
||||
/// [`Terminal::draw`]: crate::Terminal::draw
|
||||
/// [`Terminal::draw`]: crate::terminal::Terminal::draw
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct Frame<'a> {
|
||||
/// Where should the cursor be after drawing this frame?
|
||||
@@ -37,7 +37,7 @@ pub struct Frame<'a> {
|
||||
/// [`Terminal::draw`] call have been applied. Therefore, it is only valid until the next call to
|
||||
/// [`Terminal::draw`].
|
||||
///
|
||||
/// [`Terminal::draw`]: crate::Terminal::draw
|
||||
/// [`Terminal::draw`]: crate::terminal::Terminal::draw
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct CompletedFrame<'a> {
|
||||
/// The buffer that was used to draw the last frame.
|
||||
@@ -79,7 +79,7 @@ impl Frame<'_> {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use ratatui::{backend::TestBackend, Terminal};
|
||||
/// # let backend = TestBackend::new(5, 5);
|
||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||
@@ -96,32 +96,6 @@ impl Frame<'_> {
|
||||
widget.render(area, self.buffer);
|
||||
}
|
||||
|
||||
/// Render a [`WidgetRef`] to the current buffer using [`WidgetRef::render_ref`].
|
||||
///
|
||||
/// Usually the area argument is the size of the current frame or a sub-area of the current
|
||||
/// frame (which can be obtained using [`Layout`] to split the total area).
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||
/// # use ratatui::{backend::TestBackend, Terminal};
|
||||
/// # let backend = TestBackend::new(5, 5);
|
||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||
/// # let mut frame = terminal.get_frame();
|
||||
/// use ratatui::{layout::Rect, widgets::Block};
|
||||
///
|
||||
/// let block = Block::new();
|
||||
/// let area = Rect::new(0, 0, 5, 5);
|
||||
/// frame.render_widget_ref(&block, area);
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
#[instability::unstable(feature = "widget-ref")]
|
||||
pub fn render_widget_ref<W: WidgetRef>(&mut self, widget: W, area: Rect) {
|
||||
widget.render_ref(area, self.buffer);
|
||||
}
|
||||
|
||||
/// Render a [`StatefulWidget`] to the current buffer using [`StatefulWidget::render`].
|
||||
///
|
||||
/// Usually the area argument is the size of the current frame or a sub-area of the current
|
||||
@@ -132,7 +106,7 @@ impl Frame<'_> {
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use ratatui::{backend::TestBackend, Terminal};
|
||||
/// # let backend = TestBackend::new(5, 5);
|
||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||
@@ -156,43 +130,6 @@ impl Frame<'_> {
|
||||
widget.render(area, self.buffer, state);
|
||||
}
|
||||
|
||||
/// Render a [`StatefulWidgetRef`] to the current buffer using
|
||||
/// [`StatefulWidgetRef::render_ref`].
|
||||
///
|
||||
/// Usually the area argument is the size of the current frame or a sub-area of the current
|
||||
/// frame (which can be obtained using [`Layout`] to split the total area).
|
||||
///
|
||||
/// The last argument should be an instance of the [`StatefulWidgetRef::State`] associated to
|
||||
/// the given [`StatefulWidgetRef`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||
/// # use ratatui::{backend::TestBackend, Terminal};
|
||||
/// # let backend = TestBackend::new(5, 5);
|
||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||
/// # let mut frame = terminal.get_frame();
|
||||
/// use ratatui::{
|
||||
/// layout::Rect,
|
||||
/// widgets::{List, ListItem, ListState},
|
||||
/// };
|
||||
///
|
||||
/// let mut state = ListState::default().with_selected(Some(1));
|
||||
/// let list = List::new(vec![ListItem::new("Item 1"), ListItem::new("Item 2")]);
|
||||
/// let area = Rect::new(0, 0, 5, 5);
|
||||
/// frame.render_stateful_widget_ref(&list, area, &mut state);
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
#[instability::unstable(feature = "widget-ref")]
|
||||
pub fn render_stateful_widget_ref<W>(&mut self, widget: W, area: Rect, state: &mut W::State)
|
||||
where
|
||||
W: StatefulWidgetRef,
|
||||
{
|
||||
widget.render_ref(area, self.buffer, state);
|
||||
}
|
||||
|
||||
/// After drawing this frame, make the cursor visible and put it at the specified (x, y)
|
||||
/// coordinates. If this method is not called, the cursor will be hidden.
|
||||
///
|
||||
@@ -200,9 +137,9 @@ impl Frame<'_> {
|
||||
/// [`Terminal::show_cursor`], and [`Terminal::set_cursor_position`]. Pick one of the APIs and
|
||||
/// stick with it.
|
||||
///
|
||||
/// [`Terminal::hide_cursor`]: crate::Terminal::hide_cursor
|
||||
/// [`Terminal::show_cursor`]: crate::Terminal::show_cursor
|
||||
/// [`Terminal::set_cursor_position`]: crate::Terminal::set_cursor_position
|
||||
/// [`Terminal::hide_cursor`]: crate::terminal::Terminal::hide_cursor
|
||||
/// [`Terminal::show_cursor`]: crate::terminal::Terminal::show_cursor
|
||||
/// [`Terminal::set_cursor_position`]: crate::terminal::Terminal::set_cursor_position
|
||||
pub fn set_cursor_position<P: Into<Position>>(&mut self, position: P) {
|
||||
self.cursor_position = Some(position.into());
|
||||
}
|
||||
@@ -214,9 +151,9 @@ impl Frame<'_> {
|
||||
/// [`Terminal::show_cursor`], and [`Terminal::set_cursor_position`]. Pick one of the APIs and
|
||||
/// stick with it.
|
||||
///
|
||||
/// [`Terminal::hide_cursor`]: crate::Terminal::hide_cursor
|
||||
/// [`Terminal::show_cursor`]: crate::Terminal::show_cursor
|
||||
/// [`Terminal::set_cursor_position`]: crate::Terminal::set_cursor_position
|
||||
/// [`Terminal::hide_cursor`]: crate::terminal::Terminal::hide_cursor
|
||||
/// [`Terminal::show_cursor`]: crate::terminal::Terminal::show_cursor
|
||||
/// [`Terminal::set_cursor_position`]: crate::terminal::Terminal::set_cursor_position
|
||||
#[deprecated = "the method set_cursor_position indicates more clearly what about the cursor to set"]
|
||||
pub fn set_cursor(&mut self, x: u16, y: u16) {
|
||||
self.set_cursor_position(Position { x, y });
|
||||
@@ -243,7 +180,7 @@ impl Frame<'_> {
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// # use ratatui::{backend::TestBackend, Terminal};
|
||||
/// # let backend = TestBackend::new(5, 5);
|
||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||
@@ -1,11 +1,10 @@
|
||||
use std::io;
|
||||
|
||||
use ratatui_core::backend::{Backend, ClearType};
|
||||
|
||||
use crate::{
|
||||
backend::{Backend, ClearType},
|
||||
buffer::{Buffer, Cell},
|
||||
layout::{Position, Rect, Size},
|
||||
CompletedFrame, Frame, TerminalOptions, Viewport,
|
||||
terminal::{CompletedFrame, Frame, TerminalOptions, Viewport},
|
||||
};
|
||||
|
||||
/// An interface to interact and draw [`Frame`]s on the user's terminal.
|
||||
@@ -33,7 +32,7 @@ use crate::{
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// ```rust,ignore
|
||||
/// use std::io::stdout;
|
||||
///
|
||||
/// use ratatui::{backend::CrosstermBackend, widgets::Paragraph, Terminal};
|
||||
@@ -109,7 +108,7 @@ where
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// ```rust,ignore
|
||||
/// use std::io::stdout;
|
||||
///
|
||||
/// use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
@@ -131,7 +130,7 @@ where
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// use std::io::stdout;
|
||||
///
|
||||
/// use ratatui::{backend::CrosstermBackend, layout::Rect, Terminal, TerminalOptions, Viewport};
|
||||
@@ -282,7 +281,7 @@ where
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// # let backend = ratatui::backend::TestBackend::new(10, 10);
|
||||
/// # let mut terminal = ratatui::Terminal::new(backend)?;
|
||||
/// use ratatui::{layout::Position, widgets::Paragraph};
|
||||
@@ -350,7 +349,7 @@ where
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```should_panic
|
||||
/// ```ignore
|
||||
/// # use ratatui::layout::Position;;
|
||||
/// # let backend = ratatui::backend::TestBackend::new(10, 10);
|
||||
/// # let mut terminal = ratatui::Terminal::new(backend)?;
|
||||
@@ -558,7 +557,7 @@ where
|
||||
///
|
||||
/// ## Insert a single line before the current viewport
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// use ratatui::{
|
||||
/// backend::TestBackend,
|
||||
/// style::{Color, Style},
|
||||
@@ -15,7 +15,7 @@ use crate::layout::Rect;
|
||||
///
|
||||
/// See [`Terminal::with_options`] for more information.
|
||||
///
|
||||
/// [`Terminal::with_options`]: crate::Terminal::with_options
|
||||
/// [`Terminal::with_options`]: crate::terminal::Terminal::with_options
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Viewport {
|
||||
/// The viewport is fullscreen
|
||||
@@ -34,7 +34,7 @@ impl<'a> StyledGrapheme<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for StyledGrapheme<'a> {
|
||||
impl Styled for StyledGrapheme<'_> {
|
||||
type Item = Self;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
|
||||
6
ratatui-core/src/text/line.rs
Executable file → Normal file
6
ratatui-core/src/text/line.rs
Executable file → Normal file
@@ -599,7 +599,7 @@ impl<'a> IntoIterator for &'a mut Line<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<String> for Line<'a> {
|
||||
impl From<String> for Line<'_> {
|
||||
fn from(s: String) -> Self {
|
||||
Self::raw(s)
|
||||
}
|
||||
@@ -753,7 +753,7 @@ fn render_spans(spans: &[Span], mut area: Rect, buf: &mut Buffer, span_skip_widt
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over the spans that lie after a given skip widtch from the start of the
|
||||
/// Returns an iterator over the spans that lie after a given skip width from the start of the
|
||||
/// `Line` (including a partially visible span if the `skip_width` lands within a span).
|
||||
fn spans_after_width<'a>(
|
||||
spans: &'a [Span],
|
||||
@@ -830,7 +830,7 @@ impl fmt::Display for Line<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Line<'a> {
|
||||
impl Styled for Line<'_> {
|
||||
type Item = Self;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
|
||||
@@ -322,7 +322,7 @@ impl<'a> Span<'a> {
|
||||
self.content
|
||||
.as_ref()
|
||||
.graphemes(true)
|
||||
.filter(|g| *g != "\n")
|
||||
.filter(|g| !g.contains(char::is_control))
|
||||
.map(move |g| StyledGrapheme { symbol: g, style })
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@ impl<'a> std::ops::Add<Self> for Span<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Span<'a> {
|
||||
impl Styled for Span<'_> {
|
||||
type Item = Self;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
|
||||
8
ratatui-core/src/text/text.rs
Executable file → Normal file
8
ratatui-core/src/text/text.rs
Executable file → Normal file
@@ -598,7 +598,7 @@ impl<'a> IntoIterator for &'a mut Text<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<String> for Text<'a> {
|
||||
impl From<String> for Text<'_> {
|
||||
fn from(s: String) -> Self {
|
||||
Self::raw(s)
|
||||
}
|
||||
@@ -668,7 +668,7 @@ impl<'a> std::ops::Add<Line<'a>> for Text<'a> {
|
||||
/// Adds two `Text` together.
|
||||
///
|
||||
/// This ignores the style and alignment of the second `Text`.
|
||||
impl<'a> std::ops::Add<Self> for Text<'a> {
|
||||
impl std::ops::Add<Self> for Text<'_> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, text: Self) -> Self::Output {
|
||||
@@ -730,7 +730,7 @@ impl fmt::Display for Text<'_> {
|
||||
|
||||
impl Widget for Text<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
Widget::render(&self, area, buf)
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,7 +744,7 @@ impl Widget for &Text<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Text<'a> {
|
||||
impl Styled for Text<'_> {
|
||||
type Item = Self;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ratatui-crossterm"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.1"
|
||||
description = "Crossterm backend for the Ratatui Terminal UI library."
|
||||
documentation = "https://docs.rs/ratatui-crossterm/"
|
||||
readme = "README.md"
|
||||
@@ -18,8 +18,7 @@ rust-version.workspace = true
|
||||
default = ["underline-color"]
|
||||
|
||||
## enables the backend code that sets the underline color.
|
||||
## Underline color is only supported by the [`CrosstermBackend`](backend::CrosstermBackend) backend,
|
||||
## and is not supported on Windows 7.
|
||||
## Underline color is not supported on Windows 7.
|
||||
underline-color = ["ratatui-core/underline-color"]
|
||||
|
||||
## Use terminal scrolling regions to make Terminal::insert_before less prone to flickering.
|
||||
@@ -43,3 +42,6 @@ ratatui-core = { workspace = true }
|
||||
[dev-dependencies]
|
||||
ratatui = { path = "../ratatui", features = ["crossterm"] }
|
||||
rstest.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -405,11 +405,19 @@ impl ModifierDiff {
|
||||
if removed.contains(Modifier::REVERSED) {
|
||||
queue!(w, SetAttribute(CrosstermAttribute::NoReverse))?;
|
||||
}
|
||||
if removed.contains(Modifier::BOLD) {
|
||||
if removed.contains(Modifier::BOLD) || removed.contains(Modifier::DIM) {
|
||||
// Bold and Dim are both reset by applying the Normal intensity
|
||||
queue!(w, SetAttribute(CrosstermAttribute::NormalIntensity))?;
|
||||
|
||||
// The remaining Bold and Dim attributes must be
|
||||
// reapplied after the intensity reset above.
|
||||
if self.to.contains(Modifier::DIM) {
|
||||
queue!(w, SetAttribute(CrosstermAttribute::Dim))?;
|
||||
}
|
||||
|
||||
if self.to.contains(Modifier::BOLD) {
|
||||
queue!(w, SetAttribute(CrosstermAttribute::Bold))?;
|
||||
}
|
||||
}
|
||||
if removed.contains(Modifier::ITALIC) {
|
||||
queue!(w, SetAttribute(CrosstermAttribute::NoItalic))?;
|
||||
@@ -417,9 +425,6 @@ impl ModifierDiff {
|
||||
if removed.contains(Modifier::UNDERLINED) {
|
||||
queue!(w, SetAttribute(CrosstermAttribute::NoUnderline))?;
|
||||
}
|
||||
if removed.contains(Modifier::DIM) {
|
||||
queue!(w, SetAttribute(CrosstermAttribute::NormalIntensity))?;
|
||||
}
|
||||
if removed.contains(Modifier::CROSSED_OUT) {
|
||||
queue!(w, SetAttribute(CrosstermAttribute::NotCrossedOut))?;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ratatui-termion"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.1"
|
||||
description = "Termion backend for the Ratatui Terminal UI library."
|
||||
documentation = "https://docs.rs/ratatui-termion/"
|
||||
readme = "README.md"
|
||||
@@ -21,15 +21,21 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
## Use terminal scrolling regions to make Terminal::insert_before less prone to flickering.
|
||||
scrolling-regions = ["ratatui-core/scrolling-regions"]
|
||||
## Enables all unstable features.
|
||||
unstable = ["unstable-backend-writer"]
|
||||
## Enables getting access to backends' writer.
|
||||
unstable-backend-writer = []
|
||||
|
||||
[dependencies]
|
||||
document-features = { workspace = true, optional = true }
|
||||
instability.workspace = true
|
||||
ratatui-core = { workspace = true }
|
||||
termion.workspace = true
|
||||
instability.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
rstest.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -72,7 +72,7 @@ use termion::{color as tcolor, color::Color as _, style as tstyle};
|
||||
///
|
||||
/// [`IntoRawMode::into_raw_mode()`]: termion::raw::IntoRawMode
|
||||
/// [`IntoAlternateScreen::into_alternate_screen()`]: termion::screen::IntoAlternateScreen
|
||||
/// [`Terminal`]: ratatui::terminal::Terminal
|
||||
/// [`Terminal`]: ratatui_core::terminal::Terminal
|
||||
/// [Termion]: https://docs.rs/termion
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct TermionBackend<W>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ratatui-termwiz"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.1"
|
||||
description = "Termwiz backend for the Ratatui Terminal UI library."
|
||||
documentation = "https://docs.rs/ratatui-termwiz/"
|
||||
readme = "README.md"
|
||||
@@ -37,3 +37,6 @@ termwiz.workspace = true
|
||||
[dev-dependencies]
|
||||
ratatui = { path = "../ratatui", features = ["termwiz"] }
|
||||
rstest.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "ratatui-widgets"
|
||||
description = "A collection of Ratatui widgets for building terminal user interfaces using Ratatui."
|
||||
# Note that this started at 0.3.0 as there was a previous crate using the name `ratatui-widgets`.
|
||||
# <https://github.com/joshka/ratatui-widgets/issues/46>
|
||||
version = "0.3.0-alpha.0"
|
||||
version = "0.3.0-alpha.1"
|
||||
readme = "README.md"
|
||||
authors.workspace = true
|
||||
documentation.workspace = true
|
||||
@@ -48,17 +48,17 @@ unstable-rendered-line-info = []
|
||||
|
||||
[dependencies]
|
||||
bitflags.workspace = true
|
||||
itertools.workspace = true
|
||||
document-features = { workspace = true, optional = true }
|
||||
indoc.workspace = true
|
||||
instability.workspace = true
|
||||
itertools.workspace = true
|
||||
line-clipping = "0.2.1"
|
||||
ratatui-core = { workspace = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
strum.workspace = true
|
||||
time = { version = "0.3.11", optional = true, features = ["local-offset"] }
|
||||
unicode-segmentation.workspace = true
|
||||
unicode-width.workspace = true
|
||||
serde = { workspace = true, optional = true }
|
||||
document-features = { workspace = true, optional = true }
|
||||
line-clipping = "0.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
color-eyre.workspace = true
|
||||
@@ -66,52 +66,10 @@ pretty_assertions.workspace = true
|
||||
ratatui = { path = "../ratatui" }
|
||||
rstest.workspace = true
|
||||
|
||||
[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"
|
||||
cast_precision_loss = "allow"
|
||||
cast_sign_loss = "allow"
|
||||
missing_errors_doc = "allow"
|
||||
missing_panics_doc = "allow"
|
||||
module_name_repetitions = "allow"
|
||||
must_use_candidate = "allow"
|
||||
|
||||
# we often split up a module into multiple files with the main type in a file named after the
|
||||
# module, so we want to allow this pattern
|
||||
module_inception = "allow"
|
||||
|
||||
# nursery or restricted
|
||||
as_underscore = "warn"
|
||||
deref_by_slicing = "warn"
|
||||
else_if_without_else = "warn"
|
||||
empty_line_after_doc_comments = "warn"
|
||||
equatable_if_let = "warn"
|
||||
fn_to_numeric_cast_any = "warn"
|
||||
format_push_string = "warn"
|
||||
map_err_ignore = "warn"
|
||||
missing_const_for_fn = "warn"
|
||||
mixed_read_write_in_expression = "warn"
|
||||
mod_module_files = "warn"
|
||||
needless_pass_by_ref_mut = "warn"
|
||||
needless_raw_strings = "warn"
|
||||
or_fun_call = "warn"
|
||||
redundant_type_annotations = "warn"
|
||||
rest_pat_in_fully_bound_structs = "warn"
|
||||
string_lit_chars_any = "warn"
|
||||
string_slice = "warn"
|
||||
string_to_string = "warn"
|
||||
unnecessary_self_imports = "warn"
|
||||
use_self = "warn"
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
# Adding a single example is enough for activating rustdoc-scrape-examples
|
||||
[[example]]
|
||||
name = "barchart"
|
||||
doc-scrape-examples = true
|
||||
|
||||
[[example]]
|
||||
name = "block"
|
||||
doc-scrape-examples = true
|
||||
|
||||
@@ -44,6 +44,7 @@ cargo add ratatui-widgets
|
||||
- [`LineGauge`]: displays progress as a line.
|
||||
- [`List`]: displays a list of items and allows selection.
|
||||
- [`RatatuiLogo`]: displays the Ratatui logo.
|
||||
- [`RatatuiMascot`]: displays the Ratatui mascot.
|
||||
- [`Paragraph`]: displays a paragraph of optionally styled and wrapped text.
|
||||
- [`Scrollbar`]: displays a scrollbar.
|
||||
- [`Sparkline`]: displays a single dataset as a sparkline.
|
||||
@@ -60,6 +61,7 @@ cargo add ratatui-widgets
|
||||
[`LineGauge`]: https://docs.rs/ratatui-widgets/latest/ratatui_widgets/gauge/struct.LineGauge.html
|
||||
[`List`]: https://docs.rs/ratatui-widgets/latest/ratatui_widgets/list/struct.List.html
|
||||
[`RatatuiLogo`]: https://docs.rs/ratatui-widgets/latest/ratatui_widgets/logo/struct.RatatuiLogo.html
|
||||
[`RatatuiMascot`]: https://docs.rs/ratatui-widgets/latest/ratatui_widgets/mascot/struct.RatatuiMascot.html
|
||||
[`Paragraph`]: https://docs.rs/ratatui-widgets/latest/ratatui_widgets/paragraph/struct.Paragraph.html
|
||||
[`Scrollbar`]: https://docs.rs/ratatui-widgets/latest/ratatui_widgets/scrollbar/struct.Scrollbar.html
|
||||
[`Sparkline`]: https://docs.rs/ratatui-widgets/latest/ratatui_widgets/sparkline/struct.Sparkline.html
|
||||
|
||||
103
ratatui-widgets/examples/barchart-grouped.rs
Normal file
103
ratatui-widgets/examples/barchart-grouped.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
//! # [Ratatui] `BarChart` example with grouped bars
|
||||
//!
|
||||
//! The latest version of this example is available in the [widget examples] folder in the
|
||||
//! repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [widget examples]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
use std::iter::zip;
|
||||
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event},
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::{Color, Stylize},
|
||||
text::{Line, Span},
|
||||
widgets::{Bar, BarChart, BarGroup},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
loop {
|
||||
terminal.draw(draw)?;
|
||||
if matches!(event::read()?, Event::Key(_)) {
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with a barchart on the left and right side.
|
||||
fn draw(frame: &mut Frame) {
|
||||
let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1);
|
||||
let horizontal = Layout::horizontal([Constraint::Fill(1); 2]).spacing(1);
|
||||
let [top, main] = vertical.areas(frame.area());
|
||||
let [left, right] = horizontal.areas(main);
|
||||
|
||||
let title = Line::from_iter([
|
||||
Span::from("BarChart Widget (Grouped)").bold(),
|
||||
Span::from(" (Press 'q' to quit)"),
|
||||
]);
|
||||
frame.render_widget(title.centered(), top);
|
||||
render_barchart(frame, left, Direction::Vertical, 6);
|
||||
render_barchart(frame, right, Direction::Horizontal, 1);
|
||||
}
|
||||
|
||||
/// Render a barchart with grouped bars.
|
||||
fn render_barchart(frame: &mut Frame, area: Rect, direction: Direction, bar_width: u16) {
|
||||
let companies = [
|
||||
("BITE", Color::Blue),
|
||||
("TART", Color::White),
|
||||
("BAKE", Color::LightRed),
|
||||
];
|
||||
let revenues = [
|
||||
("Jan", [8500, 6500, 7000]),
|
||||
("Feb", [9000, 7500, 8500]),
|
||||
("Mar", [9500, 4500, 8200]),
|
||||
("Apr", [6300, 4000, 5000]),
|
||||
];
|
||||
|
||||
let mut barchart = BarChart::default()
|
||||
.bar_gap(0)
|
||||
.bar_width(bar_width)
|
||||
.group_gap(2)
|
||||
.direction(direction);
|
||||
|
||||
for (period, values) in revenues {
|
||||
let bars: Vec<_> = zip(companies, values)
|
||||
.map(|((label, color), value)| bar(label, value, color))
|
||||
.collect();
|
||||
let label = Line::from(period).centered();
|
||||
let group = BarGroup::new(bars).label(label);
|
||||
barchart = barchart.data(group);
|
||||
}
|
||||
|
||||
frame.render_widget(barchart, area);
|
||||
}
|
||||
|
||||
/// Return a bar with the given label, value, and color.
|
||||
fn bar(label: &str, value: u64, color: Color) -> Bar<'_> {
|
||||
Bar::default()
|
||||
.label(label)
|
||||
.value(value)
|
||||
.text_value(format!("{:.1}M", value as f64 / 1000.))
|
||||
.style(color)
|
||||
.value_style((Color::Black, color))
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event},
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::Stylize,
|
||||
text::{Line, Span},
|
||||
@@ -35,7 +36,7 @@ fn main() -> Result<()> {
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
loop {
|
||||
terminal.draw(draw)?;
|
||||
if quit_key_pressed()? {
|
||||
if matches!(event::read()?, Event::Key(_)) {
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
@@ -80,12 +81,3 @@ fn render_vertical_barchart(frame: &mut Frame, area: Rect) {
|
||||
let chart = BarChart::vertical(bars).bar_width(6);
|
||||
frame.render_widget(chart, area);
|
||||
}
|
||||
|
||||
/// Wait for an event and return `true` if the Esc or 'q' key is pressed.
|
||||
fn quit_key_pressed() -> Result<bool> {
|
||||
use ratatui::crossterm::event::{self, Event, KeyCode};
|
||||
match event::read()? {
|
||||
Event::Key(event) if matches!(event.code, KeyCode::Esc | KeyCode::Char('q')) => Ok(true),
|
||||
_ => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
92
ratatui-widgets/examples/calendar.rs
Normal file
92
ratatui-widgets/examples/calendar.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
//! # [Ratatui] `Calendar` example
|
||||
//!
|
||||
//! The latest version of this example is available in the [widget examples] folder in the
|
||||
//! repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [widget examples]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event},
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::{Color, Modifier, Style, Stylize},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Padding},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
use ratatui_widgets::calendar::{CalendarEventStore, Monthly};
|
||||
use time::{Date, Month, OffsetDateTime};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
loop {
|
||||
terminal.draw(draw)?;
|
||||
if matches!(event::read()?, Event::Key(_)) {
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with 2 monthly calendars side by side.
|
||||
fn draw(frame: &mut Frame) {
|
||||
let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1);
|
||||
let horizontal = Layout::horizontal([Constraint::Percentage(50); 2]).spacing(1);
|
||||
let [top, main] = vertical.areas(frame.area());
|
||||
let [left, right] = horizontal.areas(main);
|
||||
|
||||
let title = Line::from_iter([
|
||||
Span::from("Calendar Widget").bold(),
|
||||
Span::from(" (Press 'q' to quit)"),
|
||||
]);
|
||||
frame.render_widget(title.centered(), top);
|
||||
|
||||
render_current_month(frame, left);
|
||||
render_styled_month(frame, right);
|
||||
}
|
||||
|
||||
/// Render the current month calendar.
|
||||
fn render_current_month(frame: &mut Frame, area: Rect) {
|
||||
let date = OffsetDateTime::now_utc().date();
|
||||
|
||||
let monthly = Monthly::new(
|
||||
date,
|
||||
CalendarEventStore::today(Style::default().red().bold()),
|
||||
)
|
||||
.block(Block::new().padding(Padding::new(0, 0, 2, 0)))
|
||||
.show_month_header(Modifier::BOLD)
|
||||
.show_weekdays_header(Modifier::ITALIC);
|
||||
frame.render_widget(monthly, area);
|
||||
}
|
||||
|
||||
/// Render an arbitrary month with more styles.
|
||||
fn render_styled_month(frame: &mut Frame, area: Rect) {
|
||||
// Release date of the movie Ratatouille.
|
||||
let date = Date::from_calendar_date(2007, Month::June, 29).unwrap();
|
||||
|
||||
let mut event_store = CalendarEventStore::today(Style::default().red().bold());
|
||||
event_store.add(date, Style::default().blue().italic());
|
||||
|
||||
let monthly = Monthly::new(date, event_store)
|
||||
.show_surrounding(Modifier::DIM)
|
||||
.show_month_header(Modifier::BOLD)
|
||||
.show_weekdays_header(Style::default().bold().green())
|
||||
.default_style(Style::default().bold().bg(Color::Rgb(50, 50, 50)));
|
||||
frame.render_widget(monthly, area);
|
||||
}
|
||||
95
ratatui-widgets/examples/canvas.rs
Normal file
95
ratatui-widgets/examples/canvas.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
//! # [Ratatui] `Canvas` example
|
||||
//!
|
||||
//! The latest version of this example is available in the [widget examples] folder in the
|
||||
//! repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [widget examples]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event},
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::{Color, Stylize},
|
||||
symbols::Marker,
|
||||
text::{Line as TextLine, Span},
|
||||
widgets::canvas::{Canvas, Line, Map, MapResolution, Rectangle},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
use ratatui_widgets::canvas::Points;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
loop {
|
||||
terminal.draw(draw)?;
|
||||
if matches!(event::read()?, Event::Key(_)) {
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with a canvas widget.
|
||||
fn draw(frame: &mut Frame) {
|
||||
let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1);
|
||||
let horizontal = Layout::horizontal([Constraint::Percentage(100)]).spacing(1);
|
||||
let [top, main] = vertical.areas(frame.area());
|
||||
let [area] = horizontal.areas(main);
|
||||
|
||||
let title = TextLine::from_iter([
|
||||
Span::from("Canvas Widget").bold(),
|
||||
Span::from(" (Press 'q' to quit)"),
|
||||
]);
|
||||
frame.render_widget(title.centered(), top);
|
||||
|
||||
render_canvas(frame, area);
|
||||
}
|
||||
|
||||
/// Renders the canvas widget with various shapes and a map.
|
||||
pub fn render_canvas(frame: &mut Frame, area: Rect) {
|
||||
let canvas = Canvas::default()
|
||||
.x_bounds([-180.0, 180.0])
|
||||
.y_bounds([-90.0, 90.0])
|
||||
.marker(Marker::Braille)
|
||||
.paint(|ctx| {
|
||||
ctx.draw(&Map {
|
||||
resolution: MapResolution::High,
|
||||
color: Color::White,
|
||||
});
|
||||
ctx.layer();
|
||||
ctx.draw(&Line::new(0.0, 10.0, 10.0, 10.0, Color::Blue));
|
||||
ctx.draw(&Rectangle {
|
||||
x: 10.0,
|
||||
y: 20.0,
|
||||
width: 10.0,
|
||||
height: 10.0,
|
||||
color: Color::Green,
|
||||
});
|
||||
ctx.draw(&Points {
|
||||
coords: &[
|
||||
(2.3522, 48.8566), // Paris
|
||||
(-122.3321, 47.6062), // Seattle
|
||||
(-79.3837, 43.6511), // Toronto
|
||||
(32.8597, 39.9334), // Ankara
|
||||
],
|
||||
color: Color::Red,
|
||||
});
|
||||
});
|
||||
|
||||
frame.render_widget(canvas, area);
|
||||
}
|
||||
92
ratatui-widgets/examples/chart.rs
Normal file
92
ratatui-widgets/examples/chart.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
//! # [Ratatui] `Chart` example
|
||||
//!
|
||||
//! The latest version of this example is available in the [widget examples] folder in the
|
||||
//! repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [widget examples]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event},
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::{Color, Stylize},
|
||||
symbols::Marker,
|
||||
text::{Line, Span},
|
||||
widgets::{Axis, Chart, Dataset, GraphType},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
loop {
|
||||
terminal.draw(draw)?;
|
||||
if matches!(event::read()?, Event::Key(_)) {
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with a chart.
|
||||
fn draw(frame: &mut Frame) {
|
||||
let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1);
|
||||
let [top, main] = vertical.areas(frame.area());
|
||||
|
||||
let title = Line::from_iter([
|
||||
Span::from("Chart Widget").bold(),
|
||||
Span::from(" (Press 'q' to quit)"),
|
||||
]);
|
||||
frame.render_widget(title.centered(), top);
|
||||
|
||||
render_chart(frame, main);
|
||||
}
|
||||
|
||||
/// Render a chart going upward.
|
||||
pub fn render_chart(frame: &mut Frame, area: Rect) {
|
||||
let dataset = Dataset::default()
|
||||
.name("Stonks")
|
||||
.marker(Marker::Braille)
|
||||
.graph_type(GraphType::Line)
|
||||
.style(Color::Blue)
|
||||
.data(&[
|
||||
(0.0, 1.0),
|
||||
(1.0, 3.0),
|
||||
(2.0, 0.5),
|
||||
(3.0, 2.0),
|
||||
(4.0, 0.8),
|
||||
(5.0, 4.0),
|
||||
(6.0, 1.0),
|
||||
(7.0, 6.0),
|
||||
(8.0, 3.0),
|
||||
(10.0, 10.0),
|
||||
]);
|
||||
|
||||
let x_axis = Axis::default()
|
||||
.title("Hustle".blue())
|
||||
.bounds([0.0, 10.0])
|
||||
.labels(["0%", "50%", "100%"]);
|
||||
|
||||
let y_axis = Axis::default()
|
||||
.title("Profit".blue())
|
||||
.bounds([0.0, 10.0])
|
||||
.labels(["0", "5", "10"]);
|
||||
|
||||
let chart = Chart::new(vec![dataset]).x_axis(x_axis).y_axis(y_axis);
|
||||
frame.render_widget(chart, area);
|
||||
}
|
||||
85
ratatui-widgets/examples/gauge.rs
Normal file
85
ratatui-widgets/examples/gauge.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
//! # [Ratatui] `Gauge` example
|
||||
//!
|
||||
//! The latest version of this example is available in the [widget examples] folder in the
|
||||
//! repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [widget examples]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event},
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::{Modifier, Style, Stylize},
|
||||
symbols,
|
||||
text::{Line, Span},
|
||||
widgets::{Gauge, LineGauge},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
loop {
|
||||
terminal.draw(draw)?;
|
||||
if matches!(event::read()?, Event::Key(_)) {
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with various progress bars.
|
||||
fn draw(frame: &mut Frame) {
|
||||
let vertical = Layout::vertical([
|
||||
Constraint::Length(1),
|
||||
Constraint::Max(2),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.spacing(1);
|
||||
let [top, first, second] = vertical.areas(frame.area());
|
||||
|
||||
let title = Line::from_iter([
|
||||
Span::from("Gauge Widget").bold(),
|
||||
Span::from(" (Press 'q' to quit)"),
|
||||
]);
|
||||
frame.render_widget(title.centered(), top);
|
||||
|
||||
render_gauge(frame, first);
|
||||
render_line_gauge(frame, second);
|
||||
}
|
||||
|
||||
/// Render a gauge with a custom style.
|
||||
pub fn render_gauge(frame: &mut Frame, area: Rect) {
|
||||
let gauge = Gauge::default()
|
||||
.style(Modifier::BOLD)
|
||||
.gauge_style(Style::new().blue().on_black())
|
||||
.label("Year Progress")
|
||||
.percent(80);
|
||||
frame.render_widget(gauge, area);
|
||||
}
|
||||
|
||||
/// Render a line gauge (compact progress bar).
|
||||
pub fn render_line_gauge(frame: &mut Frame, area: Rect) {
|
||||
let line_gauge = LineGauge::default()
|
||||
.filled_style(Style::new().white().on_red().bold())
|
||||
.unfilled_style(Style::new().gray().on_black())
|
||||
.label("❤️ HP")
|
||||
.ratio(0.42)
|
||||
.line_set(symbols::line::THICK);
|
||||
frame.render_widget(line_gauge, area);
|
||||
}
|
||||
103
ratatui-widgets/examples/list.rs
Normal file
103
ratatui-widgets/examples/list.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
//! # [Ratatui] `List` example
|
||||
//!
|
||||
//! The latest version of this example is available in the [widget examples] folder in the
|
||||
//! repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [widget examples]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event, KeyCode},
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::{Color, Modifier, Style, Stylize},
|
||||
text::{Line, Span},
|
||||
widgets::{List, ListDirection, ListState},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
let mut list_state = ListState::default();
|
||||
list_state.select_first();
|
||||
loop {
|
||||
terminal.draw(|frame| draw(frame, &mut list_state))?;
|
||||
if let Event::Key(key) = event::read()? {
|
||||
match key.code {
|
||||
KeyCode::Char('q') => break Ok(()),
|
||||
KeyCode::Down | KeyCode::Char('j') => list_state.select_next(),
|
||||
KeyCode::Up | KeyCode::Char('k') => list_state.select_previous(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with various lists.
|
||||
fn draw(frame: &mut Frame, list_state: &mut ListState) {
|
||||
let vertical = Layout::vertical([
|
||||
Constraint::Length(1),
|
||||
Constraint::Fill(1),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.spacing(1);
|
||||
let [top, first, second] = vertical.areas(frame.area());
|
||||
|
||||
let title = Line::from_iter([
|
||||
Span::from("List Widget").bold(),
|
||||
Span::from(" (Press 'q' to quit and arrow keys to navigate)"),
|
||||
]);
|
||||
frame.render_widget(title.centered(), top);
|
||||
|
||||
render_list(frame, first, list_state);
|
||||
render_bottom_list(frame, second);
|
||||
}
|
||||
|
||||
/// Render a list.
|
||||
pub fn render_list(frame: &mut Frame, area: Rect, list_state: &mut ListState) {
|
||||
let items = ["Item 1", "Item 2", "Item 3", "Item 4"];
|
||||
let list = List::new(items)
|
||||
.style(Color::White)
|
||||
.highlight_style(Modifier::REVERSED)
|
||||
.highlight_symbol("> ");
|
||||
|
||||
frame.render_stateful_widget(list, area, list_state);
|
||||
}
|
||||
|
||||
/// Render a bottom-to-top list.
|
||||
pub fn render_bottom_list(frame: &mut Frame, area: Rect) {
|
||||
let items = [
|
||||
"[Remy]: I'm building one now.\nIt even supports multiline text!",
|
||||
"[Gusteau]: With enough passion, yes.",
|
||||
"[Remy]: But can anyone build a TUI in Rust?",
|
||||
"[Gusteau]: Anyone can cook!",
|
||||
];
|
||||
let list = List::new(items)
|
||||
.style(Color::White)
|
||||
.highlight_style(Style::new().yellow().italic())
|
||||
.highlight_symbol("> ")
|
||||
.scroll_padding(1)
|
||||
.direction(ListDirection::BottomToTop)
|
||||
.repeat_highlight_symbol(true);
|
||||
|
||||
let mut state = ListState::default();
|
||||
state.select_first();
|
||||
|
||||
frame.render_stateful_widget(list, area, &mut state);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
//! # [Ratatui] Logo example
|
||||
//! # [Ratatui] `RatatuiLogo` example
|
||||
//!
|
||||
//! The latest version of this example is available in the [examples] folder in the repository.
|
||||
//! The latest version of this example is available in the [widget examples] folder in the
|
||||
//! repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
@@ -10,17 +11,17 @@
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
|
||||
//! [widget examples]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
use std::env::args;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossterm::event::{self, Event};
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event},
|
||||
layout::{Constraint, Layout},
|
||||
widgets::{RatatuiLogo, RatatuiLogoSize},
|
||||
DefaultTerminal, TerminalOptions, Viewport,
|
||||
DefaultTerminal, Frame, TerminalOptions, Viewport,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@@ -39,16 +40,21 @@ fn main() -> Result<()> {
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal, size: RatatuiLogoSize) -> Result<()> {
|
||||
loop {
|
||||
terminal.draw(|frame| {
|
||||
use Constraint::{Fill, Length};
|
||||
let [top, bottom] = Layout::vertical([Length(1), Fill(1)]).areas(frame.area());
|
||||
frame.render_widget("Powered by", top);
|
||||
frame.render_widget(RatatuiLogo::new(size), bottom);
|
||||
})?;
|
||||
terminal.draw(|frame| draw(frame, size))?;
|
||||
if matches!(event::read()?, Event::Key(_)) {
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with a logo.
|
||||
fn draw(frame: &mut Frame, size: RatatuiLogoSize) {
|
||||
let [top, bottom] =
|
||||
Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).areas(frame.area());
|
||||
|
||||
frame.render_widget("Powered by", top);
|
||||
frame.render_widget(RatatuiLogo::new(size), bottom);
|
||||
}
|
||||
102
ratatui-widgets/examples/paragraph.rs
Normal file
102
ratatui-widgets/examples/paragraph.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
//! # [Ratatui] `Paragraph` example
|
||||
//!
|
||||
//! The latest version of this example is available in the [widget examples] folder in the
|
||||
//! repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [widget examples]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event},
|
||||
layout::{Alignment, Constraint, Layout, Rect},
|
||||
style::{Color, Stylize},
|
||||
text::{Line, Masked, Span},
|
||||
widgets::{Paragraph, Wrap},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
loop {
|
||||
terminal.draw(draw)?;
|
||||
if matches!(event::read()?, Event::Key(_)) {
|
||||
break Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with various text.
|
||||
fn draw(frame: &mut Frame) {
|
||||
let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1);
|
||||
let horizontal = Layout::horizontal([Constraint::Percentage(50); 2]).spacing(1);
|
||||
let [top, main] = vertical.areas(frame.area());
|
||||
let [first, second] = horizontal.areas(main);
|
||||
|
||||
let title = Line::from_iter([
|
||||
Span::from("Paragraph Widget").bold(),
|
||||
Span::from(" (Press 'q' to quit)"),
|
||||
]);
|
||||
frame.render_widget(title.centered(), top);
|
||||
|
||||
render_centered_paragraph(frame, first);
|
||||
render_wrapped_paragraph(frame, second);
|
||||
}
|
||||
|
||||
/// Render a paragraph with centered text.
|
||||
pub fn render_centered_paragraph(frame: &mut Frame, area: Rect) {
|
||||
let text = "Centered text\nwith multiple lines.\nCheck out the recipe!";
|
||||
let paragraph = Paragraph::new(text)
|
||||
.style(Color::White)
|
||||
.alignment(Alignment::Center);
|
||||
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
|
||||
/// Render a long paragraph that wraps text.
|
||||
pub fn render_wrapped_paragraph(frame: &mut Frame, area: Rect) {
|
||||
let paragraph = Paragraph::new(create_lines(area))
|
||||
.style(Color::White)
|
||||
.scroll((0, 0))
|
||||
.wrap(Wrap { trim: true });
|
||||
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
|
||||
/// Returns the lines for the paragraph.
|
||||
fn create_lines(area: Rect) -> Vec<Line<'static>> {
|
||||
let short_line = "Slice, layer, and bake the vegetables. ";
|
||||
let long_line = short_line.repeat((area.width as usize) / short_line.len() + 2);
|
||||
vec![
|
||||
"Recipe: Ratatouille".into(),
|
||||
"Ingredients:".bold().into(),
|
||||
Line::from_iter([
|
||||
"Bell Peppers".into(),
|
||||
", Eggplant".italic(),
|
||||
", Tomatoes".bold(),
|
||||
", Onion".into(),
|
||||
]),
|
||||
Line::from_iter([
|
||||
"Secret Ingredient: ".underlined(),
|
||||
Span::styled(Masked::new("herbs de Provence", '*'), Color::Red),
|
||||
]),
|
||||
"Instructions:".bold().yellow().into(),
|
||||
long_line.green().italic().into(),
|
||||
]
|
||||
}
|
||||
133
ratatui-widgets/examples/scrollbar.rs
Normal file
133
ratatui-widgets/examples/scrollbar.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
//! # [Ratatui] `Scrollbar` example
|
||||
//!
|
||||
//! The latest version of this example is available in the [widget examples] folder in the
|
||||
//! repository.
|
||||
//!
|
||||
//! Please note that the examples are designed to be run against the `main` branch of the Github
|
||||
//! repository. This means that you may not be able to compile with the latest release version on
|
||||
//! crates.io, or the one that you have installed locally.
|
||||
//!
|
||||
//! See the [examples readme] for more information on finding examples that match the version of the
|
||||
//! library you are using.
|
||||
//!
|
||||
//! [Ratatui]: https://github.com/ratatui/ratatui
|
||||
//! [widget examples]: https://github.com/ratatui/ratatui/blob/main/ratatui-widgets/examples
|
||||
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
|
||||
|
||||
use color_eyre::Result;
|
||||
use ratatui::{
|
||||
crossterm::event::{self, Event, KeyCode},
|
||||
layout::{Constraint, Layout, Margin, Rect},
|
||||
style::{Color, Stylize},
|
||||
symbols::scrollbar::Set,
|
||||
text::{Line, Span},
|
||||
widgets::{Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState},
|
||||
DefaultTerminal, Frame,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let result = run(terminal);
|
||||
ratatui::restore();
|
||||
result
|
||||
}
|
||||
|
||||
/// Run the application.
|
||||
fn run(mut terminal: DefaultTerminal) -> Result<()> {
|
||||
let mut vertical = ScrollbarState::new(100);
|
||||
let mut horizontal = ScrollbarState::new(100);
|
||||
loop {
|
||||
terminal.draw(|frame| draw(frame, &mut vertical, &mut horizontal))?;
|
||||
if let Event::Key(key) = event::read()? {
|
||||
match key.code {
|
||||
KeyCode::Char('q') => break Ok(()),
|
||||
KeyCode::Down | KeyCode::Char('j') => vertical.next(),
|
||||
KeyCode::Up | KeyCode::Char('k') => vertical.prev(),
|
||||
KeyCode::Right | KeyCode::Char('l') => horizontal.next(),
|
||||
KeyCode::Left | KeyCode::Char('h') => horizontal.prev(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw the UI with vertical/horizontal scrollbars.
|
||||
fn draw(frame: &mut Frame, vertical: &mut ScrollbarState, horizontal: &mut ScrollbarState) {
|
||||
let vertical_layout = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]).spacing(1);
|
||||
let [top, main] = vertical_layout.areas(frame.area());
|
||||
|
||||
let title = Line::from_iter([
|
||||
Span::from("Scrollbar Widget").bold(),
|
||||
Span::from(" (Press 'q' to quit, arrow keys to scroll)"),
|
||||
]);
|
||||
frame.render_widget(title.centered(), top);
|
||||
|
||||
render_content(frame, main, vertical, horizontal);
|
||||
render_vertical_scrollbar(frame, main, vertical);
|
||||
render_horizontal_scrollbar(frame, main, horizontal);
|
||||
}
|
||||
|
||||
/// Render a vertical scrollbar on the right side of the area.
|
||||
pub fn render_vertical_scrollbar(frame: &mut Frame, area: Rect, vertical: &mut ScrollbarState) {
|
||||
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight);
|
||||
frame.render_stateful_widget(
|
||||
scrollbar,
|
||||
area.inner(Margin {
|
||||
vertical: 1,
|
||||
horizontal: 0,
|
||||
}),
|
||||
vertical,
|
||||
);
|
||||
}
|
||||
|
||||
/// Render a horizontal scrollbar at the bottom of the area.
|
||||
pub fn render_horizontal_scrollbar(frame: &mut Frame, area: Rect, horizontal: &mut ScrollbarState) {
|
||||
let scrollbar = Scrollbar::new(ScrollbarOrientation::HorizontalBottom)
|
||||
.symbols(Set {
|
||||
track: "-",
|
||||
thumb: "▮",
|
||||
begin: "<",
|
||||
end: ">",
|
||||
})
|
||||
.track_style(Color::Yellow)
|
||||
.begin_style(Color::Green)
|
||||
.end_style(Color::Red);
|
||||
|
||||
frame.render_stateful_widget(
|
||||
scrollbar,
|
||||
area.inner(Margin {
|
||||
vertical: 0,
|
||||
horizontal: 1,
|
||||
}),
|
||||
horizontal,
|
||||
);
|
||||
}
|
||||
|
||||
/// Render some content.
|
||||
fn render_content(
|
||||
frame: &mut Frame,
|
||||
area: Rect,
|
||||
vertical: &ScrollbarState,
|
||||
horizontal: &ScrollbarState,
|
||||
) {
|
||||
let content = vec![
|
||||
Line::from("This is a paragraph with a vertical and horizontal scrollbar."),
|
||||
Line::from_iter(["Lorem ipsum dolor sit amet, consectetur adipiscing elit.".repeat(10)]),
|
||||
Line::from_iter([
|
||||
"Horizontal: ".bold(),
|
||||
horizontal.get_position().to_string().yellow(),
|
||||
]),
|
||||
Line::from_iter([
|
||||
"Vertical: ".bold(),
|
||||
vertical.get_position().to_string().yellow(),
|
||||
]),
|
||||
];
|
||||
frame.render_widget(
|
||||
Paragraph::new(content).scroll((
|
||||
vertical.get_position() as u16,
|
||||
horizontal.get_position() as u16,
|
||||
)),
|
||||
area,
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user