Compare commits
43 Commits
ratatui-co
...
joshka/pus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22f56ac43a | ||
|
|
24e3133456 | ||
|
|
96d097ef76 | ||
|
|
a6356c157c | ||
|
|
0fbefe9389 | ||
|
|
12c49c0eac | ||
|
|
f18bcbf06b | ||
|
|
ee673476d3 | ||
|
|
3ba3735650 | ||
|
|
bf268937d4 | ||
|
|
26b05dee59 | ||
|
|
3a945dfe06 | ||
|
|
8f6782acd5 | ||
|
|
dd6d315c7b | ||
|
|
fe410c6645 | ||
|
|
5895340cb3 | ||
|
|
e869cb9750 | ||
|
|
b1d47e7718 | ||
|
|
94ba82e9ca | ||
|
|
500f6c5a52 | ||
|
|
b6588fd1fa | ||
|
|
c7c3498025 | ||
|
|
b65788ce14 | ||
|
|
5d0a362f3e | ||
|
|
7762effd93 | ||
|
|
718dcd6ccb | ||
|
|
1dc18bf3cf | ||
|
|
90a77aaf8b | ||
|
|
d709b0380f | ||
|
|
f8c1006058 | ||
|
|
c60fc65915 | ||
|
|
6c2f276d1c | ||
|
|
4cb1b06371 | ||
|
|
09b943d5b9 | ||
|
|
ad56456e23 | ||
|
|
6957bce264 | ||
|
|
06f0c67131 | ||
|
|
f10a9e8308 | ||
|
|
4c3c0540cd | ||
|
|
f919b25ea6 | ||
|
|
714c6584c3 | ||
|
|
3812f69997 | ||
|
|
16b76e36ee |
2
.github/workflows/check-semver.yml
vendored
2
.github/workflows/check-semver.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Check semver
|
||||
|
||||
89
.github/workflows/ci.yml
vendored
89
.github/workflows/ci.yml
vendored
@@ -29,15 +29,15 @@ jobs:
|
||||
name: Check Formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: rustfmt
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
|
||||
- uses: taiki-e/install-action@c5b1b6f479c32f356cc6f4ba672a47f63853b13b # v2
|
||||
- uses: taiki-e/install-action@763e3324d4fd026c9bd284c504378585777a87d5 # v2
|
||||
with:
|
||||
tool: taplo-cli
|
||||
- run: cargo xtask format --check
|
||||
@@ -48,27 +48,32 @@ jobs:
|
||||
name: Check Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: crate-ci/typos@80c8a4945eec0f6d464eaf9e65ed98ef085283d1 # master
|
||||
- uses: crate-ci/typos@626c4bedb751ce0b7f03262ca97ddda9a076ae1c # master
|
||||
|
||||
# Check for any disallowed dependencies in the codebase due to license / security issues.
|
||||
# See <https://github.com/EmbarkStudios/cargo-deny>
|
||||
dependencies:
|
||||
name: Check Dependencies
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
checks:
|
||||
- advisories
|
||||
- bans licenses sources
|
||||
# Prevent sudden announcement of a new advisory from failing ci:
|
||||
continue-on-error: ${{ matrix.checks == 'advisories' }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: EmbarkStudios/cargo-deny-action@76cd80eb775d7bbbd2d80292136d74d39e1b4918 # v2
|
||||
with:
|
||||
toolchain: stable
|
||||
- uses: taiki-e/install-action@c5b1b6f479c32f356cc6f4ba672a47f63853b13b # v2
|
||||
with:
|
||||
tool: cargo-deny
|
||||
- run: cargo deny --log-level info --all-features check
|
||||
rust-toolchain: stable
|
||||
log-level: info
|
||||
arguments: --all-features --exclude-unpublished
|
||||
command: check ${{ matrix.checks }}
|
||||
|
||||
# Check for any unused dependencies in the codebase.
|
||||
# See <https://github.com/bnjbvr/cargo-machete/>
|
||||
@@ -76,7 +81,7 @@ jobs:
|
||||
name: Check Unused Dependencies
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: bnjbvr/cargo-machete@7959c845782fed02ee69303126d4a12d64f1db18 # v0.9.1
|
||||
@@ -95,10 +100,10 @@ jobs:
|
||||
toolchain: ["stable", "beta"]
|
||||
continue-on-error: ${{ matrix.toolchain == 'beta' }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
components: clippy
|
||||
@@ -112,10 +117,10 @@ jobs:
|
||||
name: Check Markdown
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: DavidAnson/markdownlint-cli2-action@992badcdf24e3b8eb7e87ff9287fe931bcb00c6e # v20
|
||||
- uses: DavidAnson/markdownlint-cli2-action@30a0e04f1870d58f8d717450cc6134995f993c63 # v21
|
||||
with:
|
||||
globs: |
|
||||
'**/*.md'
|
||||
@@ -127,14 +132,14 @@ jobs:
|
||||
name: Coverage Report
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: stable
|
||||
components: llvm-tools
|
||||
- uses: taiki-e/install-action@c5b1b6f479c32f356cc6f4ba672a47f63853b13b # v2
|
||||
- uses: taiki-e/install-action@763e3324d4fd026c9bd284c504378585777a87d5 # v2
|
||||
with:
|
||||
tool: cargo-llvm-cov
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
|
||||
@@ -151,16 +156,16 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
toolchain: ["1.85.0", "stable"]
|
||||
toolchain: ["1.86.0", "stable"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
- uses: taiki-e/install-action@c5b1b6f479c32f356cc6f4ba672a47f63853b13b # v2
|
||||
- uses: taiki-e/install-action@763e3324d4fd026c9bd284c504378585777a87d5 # v2
|
||||
with:
|
||||
tool: cargo-hack
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
|
||||
@@ -172,10 +177,10 @@ jobs:
|
||||
name: Build No-Std
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: stable
|
||||
targets: x86_64-unknown-none
|
||||
@@ -195,11 +200,11 @@ jobs:
|
||||
name: Check README
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
|
||||
- uses: taiki-e/install-action@c5b1b6f479c32f356cc6f4ba672a47f63853b13b # v2
|
||||
- uses: taiki-e/install-action@763e3324d4fd026c9bd284c504378585777a87d5 # v2
|
||||
with:
|
||||
tool: cargo-rdme
|
||||
- run: cargo xtask readme --check
|
||||
@@ -212,16 +217,16 @@ jobs:
|
||||
env:
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: nightly
|
||||
- uses: dtolnay/install@74f735cdf643820234e37ae1c4089a08fd266d8a # master
|
||||
with:
|
||||
crate: cargo-docs-rs
|
||||
- uses: taiki-e/install-action@c5b1b6f479c32f356cc6f4ba672a47f63853b13b # v2
|
||||
- uses: taiki-e/install-action@763e3324d4fd026c9bd284c504378585777a87d5 # v2
|
||||
with:
|
||||
tool: cargo-hack
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
|
||||
@@ -233,13 +238,13 @@ jobs:
|
||||
name: Test Docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: stable
|
||||
- uses: taiki-e/install-action@c5b1b6f479c32f356cc6f4ba672a47f63853b13b # v2
|
||||
- uses: taiki-e/install-action@763e3324d4fd026c9bd284c504378585777a87d5 # v2
|
||||
with:
|
||||
tool: cargo-hack
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
|
||||
@@ -252,15 +257,15 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain: ["1.85.0", "stable"]
|
||||
toolchain: ["1.86.0", "stable"]
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: stable
|
||||
- uses: taiki-e/install-action@c5b1b6f479c32f356cc6f4ba672a47f63853b13b # v2
|
||||
- uses: taiki-e/install-action@763e3324d4fd026c9bd284c504378585777a87d5 # v2
|
||||
with:
|
||||
tool: cargo-hack
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
|
||||
@@ -280,10 +285,10 @@ jobs:
|
||||
- os: windows-latest
|
||||
backend: termion
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
- uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: stable
|
||||
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
|
||||
|
||||
10
.github/workflows/release-plz.yml
vendored
10
.github/workflows/release-plz.yml
vendored
@@ -23,15 +23,15 @@ jobs:
|
||||
if: ${{ github.repository_owner == 'ratatui' }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: stable
|
||||
- uses: rust-lang/crates-io-auth-action@041cce5b4b821e6b0ebc9c9c38b58cac4e34dcc2 # v1
|
||||
- uses: rust-lang/crates-io-auth-action@b7e9a28eded4986ec6b1fa40eeee8f8f165559ec # v1
|
||||
id: auth
|
||||
- name: Run release-plz
|
||||
uses: release-plz/action@d529f731ae3e89610ada96eda34e5c6ba3b12214 # v0.5
|
||||
@@ -54,12 +54,12 @@ jobs:
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
|
||||
uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e # master
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: Run release-plz
|
||||
|
||||
4
.github/workflows/zizmor.yml
vendored
4
.github/workflows/zizmor.yml
vendored
@@ -18,9 +18,9 @@ jobs:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0
|
||||
uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0
|
||||
|
||||
@@ -18,8 +18,9 @@ This is a quick summary of the sections below:
|
||||
- `FrameExt` trait for `unstable-widget-ref` feature
|
||||
- `List::highlight_symbol` now accepts `Into<Line>` instead of `&str`
|
||||
- 'layout::Alignment' is renamed to 'layout::HorizontalAlignment'
|
||||
- The MSRV is now 1.85.0
|
||||
- MSRV is now 1.86.0
|
||||
- `Backend` now requires an associated `Error` type and `clear_region` method
|
||||
- `Rect::inner` now takes `Into<Inset>` instead of `Margin`
|
||||
- `TestBackend` now uses `core::convert::Infallible` for error handling instead of `std::io::Error`
|
||||
- Disabling `default-features` will now disable layout cache, which can have a negative impact on performance
|
||||
- `Layout::init_cache` and `Layout::DEFAULT_CACHE_SIZE` are now only available if `layout-cache`
|
||||
@@ -27,6 +28,9 @@ This is a quick summary of the sections below:
|
||||
- Disabling `default-features` suppresses the error message if `show_cursor()` fails when dropping
|
||||
`Terminal`
|
||||
- Support a broader range for `unicode-width` version
|
||||
- `Marker` is now non-exhaustive
|
||||
- `symbols::braille::BLANK` and `symbols::braille::DOTS` have been removed in favor of an ordered
|
||||
array of all Braille characters
|
||||
- [v0.29.0](#v0290)
|
||||
- `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer
|
||||
const
|
||||
@@ -92,6 +96,24 @@ This is a quick summary of the sections below:
|
||||
|
||||
## v0.30.0 Unreleased
|
||||
|
||||
### `Marker` is now non-exhaustive ([#2236])
|
||||
|
||||
[#2236]: https://github.com/ratatui/ratatui/pull/2236
|
||||
|
||||
The `Marker` enum is now marked as `#[non_exhaustive]`, if you were matching on `Marker` exhaustively,
|
||||
you will need to add a wildcard arm:
|
||||
|
||||
```diff
|
||||
match marker {
|
||||
Marker::Dot => { /* ... */ }
|
||||
Marker::Block => { /* ... */ }
|
||||
Marker::Bar => { /* ... */ }
|
||||
Marker::Braille => { /* ... */ }
|
||||
Marker::HalfBlock => { /* ... */ }
|
||||
+ _ => { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
### `Flex::SpaceAround` now mirrors flexbox: space between items is twice the size of the outer gaps ([#1952])
|
||||
|
||||
[#1952]: https://github.com/ratatui/ratatui/pull/1952
|
||||
@@ -105,6 +127,13 @@ behavior can be achieved by using `Flex::SpaceEvenly` instead.
|
||||
+ let rects = Layout::horizontal([Length(1), Length(2)]).flex(Flex::SpaceEvenly).split(area);
|
||||
```
|
||||
|
||||
### `Rect::inner` now takes `Into<Inset>` instead of `Margin`
|
||||
|
||||
`Rect::inner` accepts any type that converts into `Inset` (for example `Inset` or `Margin`). Calls
|
||||
that relied on inference for a `Margin` conversion may now need an explicit `Margin::new` (or
|
||||
`Inset::trbl`) to make type inference succeed. This change also removes `const` support for
|
||||
`Rect::inner`, so calls in const contexts need to move to runtime code.
|
||||
|
||||
### `block::Title` no longer exists ([#1926])
|
||||
|
||||
[#1926]: https://github.com/ratatui/ratatui/pull/1926
|
||||
@@ -235,11 +264,11 @@ instead.
|
||||
+ fn run(mut terminal: DefaultTerminal) -> io::Result<()> {
|
||||
```
|
||||
|
||||
### The MSRV is now 1.85.0 ([#1860])
|
||||
### MSRV is now 1.86.0 ([#2230])
|
||||
|
||||
[#1860]: https://github.com/ratatui/ratatui/pull/1860
|
||||
[#2230]: https://github.com/ratatui/ratatui/pull/2230
|
||||
|
||||
The minimum supported Rust version (MSRV) is now 1.85.0.
|
||||
The minimum supported Rust version (MSRV) is now 1.86.0.
|
||||
|
||||
### `layout::Alignment` is renamed to `layout::HorizontalAlignment` ([#1735])
|
||||
|
||||
@@ -1087,7 +1116,7 @@ previously did not need to use type annotations to fail to compile. To fix this,
|
||||
|
||||
[#133]: https://github.com/ratatui/ratatui/issues/133
|
||||
|
||||
Code using the `Block` marker that previously rendered using a half block character (`'▀'``) now
|
||||
Code using the `Block` marker that previously rendered using a half block character (`'▀'`) now
|
||||
renders using the full block character (`'█'`). A new marker variant`Bar` is introduced to replace
|
||||
the existing code.
|
||||
|
||||
|
||||
576
Cargo.lock
generated
576
Cargo.lock
generated
@@ -165,15 +165,6 @@ dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
|
||||
dependencies = [
|
||||
"autocfg 1.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
@@ -195,12 +186,24 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base16ct"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
@@ -274,6 +277,15 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "canvas"
|
||||
version = "0.0.0"
|
||||
@@ -284,6 +296,29 @@ dependencies = [
|
||||
"ratatui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
@@ -373,9 +408,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.50"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623"
|
||||
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -393,9 +428,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.50"
|
||||
version = "4.5.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0"
|
||||
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -421,15 +456,6 @@ version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
||||
|
||||
[[package]]
|
||||
name = "cloudabi"
|
||||
version = "0.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-explorer"
|
||||
version = "0.0.0"
|
||||
@@ -498,6 +524,12 @@ dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "constraint-explorer"
|
||||
version = "0.0.0"
|
||||
@@ -672,6 +704,18 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-bigint"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
@@ -692,6 +736,33 @@ dependencies = [
|
||||
"phf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "4.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"curve25519-dalek-derive",
|
||||
"digest",
|
||||
"fiat-crypto",
|
||||
"rustc_version",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.106",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "custom-widget"
|
||||
version = "0.0.0"
|
||||
@@ -771,6 +842,17 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.3"
|
||||
@@ -814,7 +896,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -845,9 +929,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "duct"
|
||||
version = "1.1.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7478638a31d1f1f3d6c9f5e57c76b906a04ac4879d6fd0fb6245bc88f73fd0b"
|
||||
checksum = "7e66e9c0c03d094e1a0ba1be130b849034aa80c3a2ab8ee94316bc809f3fa684"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"os_pipe",
|
||||
@@ -855,12 +939,71 @@ dependencies = [
|
||||
"shared_thread",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ecdsa"
|
||||
version = "0.16.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
|
||||
dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"elliptic-curve",
|
||||
"rfc6979",
|
||||
"signature",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
|
||||
dependencies = [
|
||||
"pkcs8",
|
||||
"signature",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ed25519-dalek"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
"serde",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.13.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"digest",
|
||||
"ff",
|
||||
"generic-array",
|
||||
"group",
|
||||
"hkdf",
|
||||
"pem-rfc7468",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"sec1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -898,12 +1041,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fakeit"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7926667ef705d624c77bfad7ee4a3a52df3d0957b4962d8f085da6a97e29e114"
|
||||
checksum = "d7cee7ae5e2b85f4a54db712b29ae088b4515d27235912c8aae90eea5548512a"
|
||||
dependencies = [
|
||||
"libmath",
|
||||
"rand 0.6.5",
|
||||
"rand 0.9.2",
|
||||
"simplerand",
|
||||
"uuid 0.8.2",
|
||||
]
|
||||
@@ -924,6 +1067,22 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||
|
||||
[[package]]
|
||||
name = "filedescriptor"
|
||||
version = "0.8.3"
|
||||
@@ -1096,6 +1255,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1135,6 +1295,17 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
|
||||
dependencies = [
|
||||
"ff",
|
||||
"rand_core 0.6.4",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.6.0"
|
||||
@@ -1153,9 +1324,9 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.0"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
@@ -1183,6 +1354,24 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.3.1"
|
||||
@@ -1552,26 +1741,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "9.3.1"
|
||||
version = "10.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
|
||||
checksum = "c76e1c7d7df3e34443b3621b459b066a7b79644f059fc8b2db7070c825fd417e"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"ed25519-dalek",
|
||||
"getrandom 0.2.16",
|
||||
"hmac",
|
||||
"js-sys",
|
||||
"p256",
|
||||
"p384",
|
||||
"pem",
|
||||
"ring",
|
||||
"rand 0.8.5",
|
||||
"rsa",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"signature",
|
||||
"simple_asn1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kasuari"
|
||||
version = "0.4.9"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12a3d6645acdef96d256c1f9fd3be7ecfa60d8457520a50bbd1600b6053f8173"
|
||||
checksum = "012b421f57b19b6dca64a11f6703087faa71aaef81c6e7cae57dcf7cdf286303"
|
||||
dependencies = [
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"thiserror 2.0.17",
|
||||
@@ -1588,6 +1785,9 @@ name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
@@ -1610,17 +1810,6 @@ dependencies = [
|
||||
"rand 0.3.23",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "line-clipping"
|
||||
version = "0.3.4"
|
||||
@@ -1669,7 +1858,7 @@ version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
dependencies = [
|
||||
"autocfg 1.5.0",
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
@@ -1685,7 +1874,7 @@ version = "0.16.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f"
|
||||
dependencies = [
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1725,7 +1914,7 @@ version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg 1.5.0",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1826,6 +2015,22 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint-dig"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libm",
|
||||
"num-integer",
|
||||
"num-iter",
|
||||
"num-traits",
|
||||
"rand 0.8.5",
|
||||
"smallvec",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
@@ -1852,13 +2057,24 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg 1.5.0",
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
@@ -1888,19 +2104,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "octocrab"
|
||||
version = "0.47.0"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0860f9250b6db66c5a4b46e00b381f063c58ad06a90f95f9ef701dd8679bb2c6"
|
||||
checksum = "03c4c16af97628682471056f83897a89e84238cc422a2af37c367acb3206a4b8"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"base64",
|
||||
"bytes",
|
||||
"cargo_metadata",
|
||||
"cfg-if",
|
||||
"chrono",
|
||||
"either",
|
||||
"futures",
|
||||
"futures-util",
|
||||
"getrandom 0.2.16",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
@@ -1975,6 +2193,30 @@ version = "4.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e"
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "p384"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
|
||||
dependencies = [
|
||||
"ecdsa",
|
||||
"elliptic-curve",
|
||||
"primeorder",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "palette"
|
||||
version = "0.7.6"
|
||||
@@ -2041,6 +2283,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@@ -2175,6 +2426,27 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs1"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
|
||||
dependencies = [
|
||||
"der",
|
||||
"pkcs8",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.7"
|
||||
@@ -2261,6 +2533,15 @@ dependencies = [
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "primeorder"
|
||||
version = "0.13.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
|
||||
dependencies = [
|
||||
"elliptic-curve",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.3.0"
|
||||
@@ -2317,31 +2598,14 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
dependencies = [
|
||||
"autocfg 0.1.8",
|
||||
"libc",
|
||||
"rand_chacha 0.1.1",
|
||||
"rand_core 0.4.2",
|
||||
"rand_hc",
|
||||
"rand_isaac",
|
||||
"rand_jitter",
|
||||
"rand_os",
|
||||
"rand_pcg",
|
||||
"rand_xorshift",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
@@ -2357,12 +2621,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.1.1"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"autocfg 0.1.8",
|
||||
"rand_core 0.3.1",
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2395,6 +2659,9 @@ name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
@@ -2405,68 +2672,6 @@ dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_isaac"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_jitter"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_os"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||
dependencies = [
|
||||
"cloudabi",
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"rdrand",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_pcg"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||
dependencies = [
|
||||
"autocfg 0.1.8",
|
||||
"rand_core 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||
dependencies = [
|
||||
"rand_core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.30.0-beta.0"
|
||||
@@ -2507,7 +2712,7 @@ dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"compact_str",
|
||||
"document-features",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"indoc",
|
||||
"itertools 0.14.0",
|
||||
"kasuari",
|
||||
@@ -2585,7 +2790,7 @@ dependencies = [
|
||||
"color-eyre",
|
||||
"crossterm 0.29.0",
|
||||
"document-features",
|
||||
"hashbrown 0.16.0",
|
||||
"hashbrown 0.16.1",
|
||||
"indoc",
|
||||
"instability",
|
||||
"itertools 0.14.0",
|
||||
@@ -2639,12 +2844,6 @@ dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.11.1"
|
||||
@@ -2688,6 +2887,16 @@ dependencies = [
|
||||
"ratatui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
|
||||
dependencies = [
|
||||
"hmac",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
@@ -2702,6 +2911,26 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsa"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"digest",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"pkcs1",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.4",
|
||||
"signature",
|
||||
"spki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest"
|
||||
version = "0.26.1"
|
||||
@@ -2864,6 +3093,20 @@ dependencies = [
|
||||
"ratatui",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"der",
|
||||
"generic-array",
|
||||
"pkcs8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "secrecy"
|
||||
version = "0.10.3"
|
||||
@@ -2901,6 +3144,9 @@ name = "semver"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -3060,6 +3306,16 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.6.3"
|
||||
@@ -3130,6 +3386,22 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@@ -3228,9 +3500,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "target-triple"
|
||||
version = "0.1.4"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790"
|
||||
checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b"
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
@@ -3255,14 +3527,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "4.0.5"
|
||||
version = "4.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3669a69de26799d6321a5aa713f55f7e2cd37bd47be044b50f2acafc42c122bb"
|
||||
checksum = "f44138a9ae08f0f502f24104d82517ef4da7330c35acd638f1f29d3cd5475ecb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"libredox",
|
||||
"numtoa",
|
||||
"redox_termios",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -3696,9 +3966,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "trybuild"
|
||||
version = "1.0.112"
|
||||
version = "1.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d66678374d835fe847e0dc8348fde2ceb5be4a7ec204437d8367f0d8df266a5"
|
||||
checksum = "3e17e807bff86d2a06b52bca4276746584a78375055b6e45843925ce2802b335"
|
||||
dependencies = [
|
||||
"dissimilar",
|
||||
"glob",
|
||||
|
||||
@@ -25,7 +25,7 @@ readme = "README.md"
|
||||
license = "MIT"
|
||||
exclude = ["assets/*", ".github", "Makefile.toml", "CONTRIBUTING.md", "*.log", "tags"]
|
||||
edition = "2024"
|
||||
rust-version = "1.85.0"
|
||||
rust-version = "1.86.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
anstyle = "1"
|
||||
@@ -45,7 +45,7 @@ itertools = { version = "0.14", default-features = false, features = ["use_alloc
|
||||
kasuari = { version = "0.4", default-features = false }
|
||||
line-clipping = "0.3"
|
||||
lru = "0.16"
|
||||
octocrab = "0.47"
|
||||
octocrab = "0.48"
|
||||
palette = "0.7"
|
||||
pretty_assertions = "1"
|
||||
rand = "0.9"
|
||||
@@ -107,6 +107,7 @@ empty_line_after_doc_comments = "warn"
|
||||
equatable_if_let = "warn"
|
||||
fn_to_numeric_cast_any = "warn"
|
||||
format_push_string = "warn"
|
||||
implicit_clone = "warn"
|
||||
map_err_ignore = "warn"
|
||||
missing_const_for_fn = "warn"
|
||||
mixed_read_write_in_expression = "warn"
|
||||
@@ -118,6 +119,5 @@ 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"
|
||||
|
||||
@@ -206,7 +206,7 @@ Shows how to use the [tracing](https://crates.io/crates/tracing) crate to log to
|
||||
|
||||
## User Input
|
||||
|
||||
Shows how to handle user input. [Source](./apps/user-input/). [Source](./apps/user-input/).
|
||||
Shows how to handle user input. [Source](./apps/user-input/).
|
||||
|
||||
![User input demo][user-input.gif]
|
||||
|
||||
|
||||
@@ -122,8 +122,12 @@ impl App {
|
||||
Marker::Dot => Marker::Braille,
|
||||
Marker::Braille => Marker::Block,
|
||||
Marker::Block => Marker::HalfBlock,
|
||||
Marker::HalfBlock => Marker::Bar,
|
||||
Marker::HalfBlock => Marker::Quadrant,
|
||||
Marker::Quadrant => Marker::Sextant,
|
||||
Marker::Sextant => Marker::Octant,
|
||||
Marker::Octant => Marker::Bar,
|
||||
Marker::Bar => Marker::Dot,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -123,9 +123,9 @@ impl InputForm {
|
||||
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()),
|
||||
Focus::FirstName => first_name_area + self.first_name.cursor_offset(),
|
||||
Focus::LastName => last_name_area + self.last_name.cursor_offset(),
|
||||
Focus::Age => age_area + self.age.cursor_offset(),
|
||||
};
|
||||
frame.set_cursor_position(cursor_position);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ Set Theme "Aardvark Blue"
|
||||
# The reason for this strange size is that the social preview image for this
|
||||
# demo is 1280x64 with 80 pixels of padding on each side. We want a version
|
||||
# without the padding for README.md, etc.
|
||||
Set Width 1120
|
||||
# Please note that based on the width the demo may wrap and look corrupted.
|
||||
Set Width 1160
|
||||
Set Height 480
|
||||
Set Padding 0
|
||||
Hide
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
[package]
|
||||
name = "ratatui-core"
|
||||
version = "0.1.0-beta.0"
|
||||
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-beta.0"
|
||||
documentation = "https://docs.rs/ratatui-core"
|
||||
readme = "README.md"
|
||||
authors.workspace = true
|
||||
documentation.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
keywords.workspace = true
|
||||
|
||||
@@ -216,6 +216,9 @@ impl Buffer {
|
||||
///
|
||||
/// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
|
||||
///
|
||||
/// Usage discouraged, as it exposes `self.content` as a linearly indexable array, which limits
|
||||
/// potential future abstractions. See <https://github.com/ratatui/ratatui/issues/1122>.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -269,10 +272,13 @@ impl Buffer {
|
||||
Some(y * width + x)
|
||||
}
|
||||
|
||||
/// Returns the (global) coordinates of a cell given its index
|
||||
/// Returns the (global) coordinates of a cell given its index.
|
||||
///
|
||||
/// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
|
||||
///
|
||||
/// Usage discouraged, as it exposes `self.content` as a linearly indexable array, which limits
|
||||
/// potential future abstractions. See <https://github.com/ratatui/ratatui/issues/1122>.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
//! - [`Position`] - Represents a point in the terminal coordinate system
|
||||
//! - [`Size`] - Represents dimensions (width and height)
|
||||
//! - [`Margin`] - Defines spacing around rectangular areas
|
||||
//! - [`Inset`] - Defines side-specific spacing inside a rectangle
|
||||
//! - [`Offset`] - Represents relative movement in the coordinate system
|
||||
//! - [`Spacing`] - Controls spacing or overlap between layout segments
|
||||
//!
|
||||
@@ -314,8 +315,10 @@ mod alignment;
|
||||
mod constraint;
|
||||
mod direction;
|
||||
mod flex;
|
||||
mod inset;
|
||||
mod layout;
|
||||
mod margin;
|
||||
mod offset;
|
||||
mod position;
|
||||
mod rect;
|
||||
mod size;
|
||||
@@ -324,8 +327,10 @@ pub use alignment::{Alignment, HorizontalAlignment, VerticalAlignment};
|
||||
pub use constraint::Constraint;
|
||||
pub use direction::Direction;
|
||||
pub use flex::Flex;
|
||||
pub use inset::Inset;
|
||||
pub use layout::{Layout, Spacing};
|
||||
pub use margin::Margin;
|
||||
pub use offset::Offset;
|
||||
pub use position::Position;
|
||||
pub use rect::{Columns, Offset, Positions, Rect, Rows};
|
||||
pub use rect::{Columns, Positions, Rect, Rows};
|
||||
pub use size::Size;
|
||||
|
||||
@@ -12,11 +12,27 @@ use strum::{Display, EnumString};
|
||||
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Direction {
|
||||
/// Layout segments are arranged side by side (left to right).
|
||||
Horizontal,
|
||||
/// Layout segments are arranged top to bottom (default).
|
||||
#[default]
|
||||
Vertical,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
/// The perpendicular direction to this direction.
|
||||
///
|
||||
/// `Horizontal` returns `Vertical`, and `Vertical` returns `Horizontal`.
|
||||
#[inline]
|
||||
#[must_use = "returns the perpendicular direction"]
|
||||
pub const fn perpendicular(self) -> Self {
|
||||
match self {
|
||||
Self::Horizontal => Self::Vertical,
|
||||
Self::Vertical => Self::Horizontal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::string::ToString;
|
||||
@@ -37,4 +53,11 @@ mod tests {
|
||||
assert_eq!("Vertical".parse::<Direction>(), Ok(Direction::Vertical));
|
||||
assert_eq!("".parse::<Direction>(), Err(ParseError::VariantNotFound));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other() {
|
||||
use Direction::*;
|
||||
assert_eq!(Horizontal.perpendicular(), Vertical);
|
||||
assert_eq!(Vertical.perpendicular(), Horizontal);
|
||||
}
|
||||
}
|
||||
|
||||
189
ratatui-core/src/layout/inset.rs
Normal file
189
ratatui-core/src/layout/inset.rs
Normal file
@@ -0,0 +1,189 @@
|
||||
#![warn(missing_docs)]
|
||||
use core::fmt;
|
||||
|
||||
use crate::layout::Margin;
|
||||
|
||||
/// Represents side-specific spacing inside rectangular areas.
|
||||
///
|
||||
/// `Inset` defines how much space to remove from each side of a rectangle. Unlike [`Margin`], which
|
||||
/// applies uniform spacing horizontally and vertically, `Inset` lets you specify independent
|
||||
/// amounts for the top, right, bottom, and left edges. The default constructor order is
|
||||
/// top-right-bottom-left (often remembered as “trbl” or “trouble”), matching the CSS spec and
|
||||
/// offering an easy clockwise mnemonic.
|
||||
///
|
||||
/// Use `Inset` when you need per-side control; choose [`Margin`](crate::layout::Margin) for the
|
||||
/// common symmetric case.
|
||||
///
|
||||
/// # Construction
|
||||
///
|
||||
/// - [`trbl`](Self::trbl) - Create a new inset with top/right/bottom/left values
|
||||
/// - [`default`](Default::default) - Create with zero inset on all sides
|
||||
/// - [`symmetric`](Self::symmetric) - Create with shared horizontal and vertical values
|
||||
/// - [`horizontal`](Self::horizontal) - Create with equal left and right values
|
||||
/// - [`vertical`](Self::vertical) - Create with equal top and bottom values
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Inset, Rect};
|
||||
///
|
||||
/// let inset = Inset::trbl(1, 2, 3, 4);
|
||||
/// let rect = Rect::new(0, 0, 10, 10).inner(inset);
|
||||
/// assert_eq!(rect, Rect::new(4, 1, 4, 6));
|
||||
/// ```
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Inset {
|
||||
/// Space to remove from the top edge
|
||||
pub top: u16,
|
||||
/// Space to remove from the right edge
|
||||
pub right: u16,
|
||||
/// Space to remove from the bottom edge
|
||||
pub bottom: u16,
|
||||
/// Space to remove from the left edge
|
||||
pub left: u16,
|
||||
}
|
||||
|
||||
impl Inset {
|
||||
/// Creates a new inset with explicit top/right/bottom/left values.
|
||||
pub const fn trbl(top: u16, right: u16, bottom: u16, left: u16) -> Self {
|
||||
Self {
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new inset with shared horizontal and vertical values.
|
||||
///
|
||||
/// The `horizontal` value is applied to `left` and `right`; the `vertical` value is applied to
|
||||
/// `top` and `bottom`. Note the order is `horizontal, vertical` (x then y), opposite of the CSS
|
||||
/// ordering; we keep a single ordering across helpers to avoid mixing patterns in code.
|
||||
pub const fn symmetric(horizontal: u16, vertical: u16) -> Self {
|
||||
Self {
|
||||
right: horizontal,
|
||||
left: horizontal,
|
||||
top: vertical,
|
||||
bottom: vertical,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new inset with equal left and right values.
|
||||
pub const fn horizontal(horizontal: u16) -> Self {
|
||||
Self {
|
||||
top: 0,
|
||||
right: horizontal,
|
||||
bottom: 0,
|
||||
left: horizontal,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new inset with equal top and bottom values.
|
||||
pub const fn vertical(vertical: u16) -> Self {
|
||||
Self {
|
||||
top: vertical,
|
||||
right: 0,
|
||||
bottom: vertical,
|
||||
left: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Inset {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"t{} r{} b{} l{}",
|
||||
self.top, self.right, self.bottom, self.left
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Margin> for Inset {
|
||||
fn from(margin: Margin) -> Self {
|
||||
Self {
|
||||
top: margin.vertical,
|
||||
right: margin.horizontal,
|
||||
bottom: margin.vertical,
|
||||
left: margin.horizontal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use alloc::string::ToString;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
assert_eq!(
|
||||
Inset::trbl(1, 2, 3, 4),
|
||||
Inset {
|
||||
top: 1,
|
||||
right: 2,
|
||||
bottom: 3,
|
||||
left: 4
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display() {
|
||||
assert_eq!(Inset::trbl(1, 2, 3, 4).to_string(), "t1 r2 b3 l4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symmetric() {
|
||||
assert_eq!(
|
||||
Inset::symmetric(2, 3),
|
||||
Inset {
|
||||
top: 3,
|
||||
right: 2,
|
||||
bottom: 3,
|
||||
left: 2
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn horizontal() {
|
||||
assert_eq!(
|
||||
Inset::horizontal(2),
|
||||
Inset {
|
||||
top: 0,
|
||||
right: 2,
|
||||
bottom: 0,
|
||||
left: 2
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vertical() {
|
||||
assert_eq!(
|
||||
Inset::vertical(3),
|
||||
Inset {
|
||||
top: 3,
|
||||
right: 0,
|
||||
bottom: 3,
|
||||
left: 0
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_margin() {
|
||||
assert_eq!(
|
||||
Inset::from(Margin::new(2, 3)),
|
||||
Inset {
|
||||
top: 3,
|
||||
right: 2,
|
||||
bottom: 3,
|
||||
left: 2
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@ use core::fmt;
|
||||
/// margin, the space is applied to both the left and right sides. For vertical margin, the space
|
||||
/// is applied to both the top and bottom sides.
|
||||
///
|
||||
/// Use [`Inset`](crate::layout::Inset) when you need different values for each side; `Margin`
|
||||
/// provides the symmetric case.
|
||||
///
|
||||
/// # Construction
|
||||
///
|
||||
/// - [`new`](Self::new) - Create a new margin with horizontal and vertical spacing
|
||||
|
||||
66
ratatui-core/src/layout/offset.rs
Normal file
66
ratatui-core/src/layout/offset.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use crate::layout::Position;
|
||||
|
||||
/// Amounts by which to move a [`Rect`](crate::layout::Rect).
|
||||
///
|
||||
/// Positive numbers move to the right/bottom and negative to the left/top.
|
||||
///
|
||||
/// See [`Rect::offset`](crate::layout::Rect::offset) for usage.
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Offset {
|
||||
/// How much to move on the X axis
|
||||
pub x: i32,
|
||||
|
||||
/// How much to move on the Y axis
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
impl Offset {
|
||||
/// A zero offset
|
||||
pub const ZERO: Self = Self::new(0, 0);
|
||||
|
||||
/// The minimum offset
|
||||
pub const MIN: Self = Self::new(i32::MIN, i32::MIN);
|
||||
|
||||
/// The maximum offset
|
||||
pub const MAX: Self = Self::new(i32::MAX, i32::MAX);
|
||||
|
||||
/// Creates a new `Offset` with the given values.
|
||||
pub const fn new(x: i32, y: i32) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Position> for Offset {
|
||||
fn from(position: Position) -> Self {
|
||||
Self {
|
||||
x: i32::from(position.x),
|
||||
y: i32::from(position.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn new_sets_components() {
|
||||
assert_eq!(Offset::new(-3, 7), Offset { x: -3, y: 7 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constants_match_expected_values() {
|
||||
assert_eq!(Offset::ZERO, Offset::new(0, 0));
|
||||
assert_eq!(Offset::MIN, Offset::new(i32::MIN, i32::MIN));
|
||||
assert_eq!(Offset::MAX, Offset::new(i32::MAX, i32::MAX));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_position_converts_coordinates() {
|
||||
let position = Position::new(4, 9);
|
||||
let offset = Offset::from(position);
|
||||
|
||||
assert_eq!(offset, Offset::new(4, 9));
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,13 @@ pub struct Position {
|
||||
|
||||
impl Position {
|
||||
/// Position at the origin, the top left edge at 0,0
|
||||
pub const ORIGIN: Self = Self { x: 0, y: 0 };
|
||||
pub const ORIGIN: Self = Self::new(0, 0);
|
||||
|
||||
/// Position at the minimum x and y values
|
||||
pub const MIN: Self = Self::ORIGIN;
|
||||
|
||||
/// Position at the maximum x and y values
|
||||
pub const MAX: Self = Self::new(u16::MAX, u16::MAX);
|
||||
|
||||
/// Create a new position
|
||||
pub const fn new(x: u16, y: u16) -> Self {
|
||||
|
||||
@@ -3,10 +3,11 @@ use core::array::TryFromSliceError;
|
||||
use core::cmp::{max, min};
|
||||
use core::fmt;
|
||||
|
||||
use crate::layout::{Margin, Position, Size};
|
||||
pub use self::iter::{Columns, Positions, Rows};
|
||||
use crate::layout::{Inset, Margin, Offset, Position, Size};
|
||||
|
||||
mod iter;
|
||||
pub use iter::*;
|
||||
mod ops;
|
||||
|
||||
use super::{Constraint, Flex, Layout};
|
||||
|
||||
@@ -43,8 +44,9 @@ use super::{Constraint, Flex, Layout};
|
||||
///
|
||||
/// # Spatial Operations
|
||||
///
|
||||
/// - [`inner`](Self::inner), [`outer`](Self::outer) - Apply margins to shrink or expand
|
||||
/// - [`inner`](Self::inner), [`outer`](Self::outer) - Apply insets or margins to shrink or expand
|
||||
/// - [`offset`](Self::offset) - Move the rectangle by a relative amount
|
||||
/// - [`resize`](Self::resize) - Change the rectangle size while keeping the bottom/right in range
|
||||
/// - [`union`](Self::union) - Combine with another rectangle to create a bounding box
|
||||
/// - [`intersection`](Self::intersection) - Find the overlapping area with another rectangle
|
||||
/// - [`clamp`](Self::clamp) - Constrain the rectangle to fit within another
|
||||
@@ -66,21 +68,76 @@ use super::{Constraint, Flex, Layout};
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// To create a new `Rect`, use [`Rect::new`]. The size of the `Rect` will be clamped to keep the
|
||||
/// right and bottom coordinates within `u16`. Note that this clamping does not occur when creating
|
||||
/// a `Rect` directly.
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::Rect;
|
||||
///
|
||||
/// let rect = Rect::new(1, 2, 3, 4);
|
||||
/// assert_eq!(
|
||||
/// rect,
|
||||
/// Rect {
|
||||
/// x: 1,
|
||||
/// y: 2,
|
||||
/// width: 3,
|
||||
/// height: 4
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// You can also create a `Rect` from a [`Position`] and a [`Size`].
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Position, Rect, Size};
|
||||
///
|
||||
/// // Create a rectangle manually
|
||||
/// let rect = Rect::new(10, 5, 80, 20);
|
||||
/// assert_eq!(rect.x, 10);
|
||||
/// assert_eq!(rect.y, 5);
|
||||
/// assert_eq!(rect.width, 80);
|
||||
/// assert_eq!(rect.height, 20);
|
||||
/// let position = Position::new(1, 2);
|
||||
/// let size = Size::new(3, 4);
|
||||
/// let rect = Rect::from((position, size));
|
||||
/// assert_eq!(
|
||||
/// rect,
|
||||
/// Rect {
|
||||
/// x: 1,
|
||||
/// y: 2,
|
||||
/// width: 3,
|
||||
/// height: 4
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// // Create from position and size
|
||||
/// let rect = Rect::from((Position::new(10, 5), Size::new(80, 20)));
|
||||
/// To move a `Rect` without modifying its size, add or subtract an [`Offset`] to it.
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Offset, Rect};
|
||||
///
|
||||
/// let rect = Rect::new(1, 2, 3, 4);
|
||||
/// let offset = Offset::new(5, 6);
|
||||
/// let moved_rect = rect + offset;
|
||||
/// assert_eq!(moved_rect, Rect::new(6, 8, 3, 4));
|
||||
/// ```
|
||||
///
|
||||
/// To resize a `Rect` while ensuring it stays within bounds, use [`Rect::resize`]. The size is
|
||||
/// clamped so that `right()` and `bottom()` do not exceed `u16::MAX`.
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Rect, Size};
|
||||
///
|
||||
/// let rect = Rect::new(u16::MAX - 1, u16::MAX - 1, 1, 1).resize(Size::new(10, 10));
|
||||
/// assert_eq!(rect, Rect::new(u16::MAX - 1, u16::MAX - 1, 1, 1));
|
||||
/// ```
|
||||
///
|
||||
/// To inset a `Rect` with different values on each side, use [`Rect::inner`] with an [`Inset`].
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Inset, Rect};
|
||||
///
|
||||
/// let rect = Rect::new(0, 0, 10, 10).inner(Inset::trbl(1, 2, 3, 4));
|
||||
/// assert_eq!(rect, Rect::new(4, 1, 4, 6));
|
||||
/// ```
|
||||
///
|
||||
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Rect {
|
||||
@@ -94,30 +151,6 @@ pub struct Rect {
|
||||
pub height: u16,
|
||||
}
|
||||
|
||||
/// Amounts by which to move a [`Rect`](crate::layout::Rect).
|
||||
///
|
||||
/// Positive numbers move to the right/bottom and negative to the left/top.
|
||||
///
|
||||
/// See [`Rect::offset`]
|
||||
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Offset {
|
||||
/// How much to move on the X axis
|
||||
pub x: i32,
|
||||
/// How much to move on the Y axis
|
||||
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)
|
||||
@@ -133,6 +166,12 @@ impl Rect {
|
||||
height: 0,
|
||||
};
|
||||
|
||||
/// The minimum possible Rect
|
||||
pub const MIN: Self = Self::ZERO;
|
||||
|
||||
/// The maximum possible Rect
|
||||
pub const MAX: Self = Self::new(0, 0, u16::MAX, u16::MAX);
|
||||
|
||||
/// Creates a new `Rect`, with width and height limited to keep both bounds within `u16`.
|
||||
///
|
||||
/// If the width or height would cause the right or bottom coordinate to be larger than the
|
||||
@@ -147,15 +186,8 @@ impl Rect {
|
||||
/// let rect = Rect::new(1, 2, 3, 4);
|
||||
/// ```
|
||||
pub const fn new(x: u16, y: u16, width: u16, height: u16) -> Self {
|
||||
// these calculations avoid using min so that this function can be const
|
||||
let max_width = u16::MAX - x;
|
||||
let max_height = u16::MAX - y;
|
||||
let width = if width > max_width { max_width } else { width };
|
||||
let height = if height > max_height {
|
||||
max_height
|
||||
} else {
|
||||
height
|
||||
};
|
||||
let width = x.saturating_add(width) - x;
|
||||
let height = y.saturating_add(height) - y;
|
||||
Self {
|
||||
x,
|
||||
y,
|
||||
@@ -202,22 +234,48 @@ impl Rect {
|
||||
self.y.saturating_add(self.height)
|
||||
}
|
||||
|
||||
/// Returns a new `Rect` inside the current one, with the given margin on each side.
|
||||
/// Returns a new `Rect` inside the current one, with the given inset on each side.
|
||||
///
|
||||
/// If the margin is larger than the `Rect`, the returned `Rect` will have no area.
|
||||
/// Accepts any type that can convert into an [`Inset`], including [`Margin`]. If the inset is
|
||||
/// larger than the `Rect`, the returned `Rect` will have no area.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Using a margin (shared horizontal and vertical):
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Margin, Rect};
|
||||
///
|
||||
/// let area = Rect::new(0, 0, 10, 6);
|
||||
/// let inner = area.inner(Margin::new(1, 2));
|
||||
///
|
||||
/// assert_eq!(inner, Rect::new(1, 2, 8, 2));
|
||||
/// ```
|
||||
///
|
||||
/// Using an inset with side-specific values:
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Inset, Rect};
|
||||
///
|
||||
/// let area = Rect::new(0, 0, 10, 6);
|
||||
/// let inner = area.inner(Inset::trbl(1, 2, 3, 4));
|
||||
///
|
||||
/// assert_eq!(inner, Rect::new(4, 1, 4, 2));
|
||||
/// ```
|
||||
#[must_use = "method returns the modified value"]
|
||||
pub const fn inner(self, margin: Margin) -> Self {
|
||||
let doubled_margin_horizontal = margin.horizontal.saturating_mul(2);
|
||||
let doubled_margin_vertical = margin.vertical.saturating_mul(2);
|
||||
pub fn inner<I: Into<Inset>>(self, inset: I) -> Self {
|
||||
let inset = inset.into();
|
||||
let total_horizontal = inset.left.saturating_add(inset.right);
|
||||
let total_vertical = inset.top.saturating_add(inset.bottom);
|
||||
|
||||
if self.width < doubled_margin_horizontal || self.height < doubled_margin_vertical {
|
||||
if self.width < total_horizontal || self.height < total_vertical {
|
||||
Self::ZERO
|
||||
} else {
|
||||
Self {
|
||||
x: self.x.saturating_add(margin.horizontal),
|
||||
y: self.y.saturating_add(margin.vertical),
|
||||
width: self.width.saturating_sub(doubled_margin_horizontal),
|
||||
height: self.height.saturating_sub(doubled_margin_vertical),
|
||||
x: self.x.saturating_add(inset.left),
|
||||
y: self.y.saturating_add(inset.top),
|
||||
width: self.width.saturating_sub(total_horizontal),
|
||||
height: self.height.saturating_sub(total_vertical),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -260,13 +318,19 @@ impl Rect {
|
||||
/// See [`Offset`] for details.
|
||||
#[must_use = "method returns the modified value"]
|
||||
pub fn offset(self, offset: Offset) -> Self {
|
||||
self + offset
|
||||
}
|
||||
|
||||
/// Resizes the `Rect`, clamping to keep the right and bottom within `u16::MAX`.
|
||||
///
|
||||
/// The position is preserved. If the requested size would push the `Rect` beyond the bounds of
|
||||
/// `u16`, the width or height is reduced so that [`right`](Self::right) and
|
||||
/// [`bottom`](Self::bottom) remain within range.
|
||||
#[must_use = "method returns the modified value"]
|
||||
pub const fn resize(self, size: Size) -> Self {
|
||||
Self {
|
||||
x: i32::from(self.x)
|
||||
.saturating_add(offset.x)
|
||||
.clamp(0, i32::from(u16::MAX - self.width)) as u16,
|
||||
y: i32::from(self.y)
|
||||
.saturating_add(offset.y)
|
||||
.clamp(0, i32::from(u16::MAX - self.height)) as u16,
|
||||
width: self.x.saturating_add(size.width).saturating_sub(self.x),
|
||||
height: self.y.saturating_add(size.height).saturating_sub(self.y),
|
||||
..self
|
||||
}
|
||||
}
|
||||
@@ -748,6 +812,22 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_supports_inset() {
|
||||
assert_eq!(
|
||||
Rect::new(10, 20, 50, 60).inner(Inset::trbl(1, 2, 3, 4)),
|
||||
Rect::new(14, 21, 44, 56),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inner_inset_zero_on_overflow() {
|
||||
assert_eq!(
|
||||
Rect::new(0, 0, 2, 2).inner(Inset::trbl(1, 1, 2, 2)),
|
||||
Rect::ZERO,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn offset() {
|
||||
assert_eq!(
|
||||
@@ -857,6 +937,18 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resize_updates_size() {
|
||||
let rect = Rect::new(10, 20, 5, 5).resize(Size::new(30, 40));
|
||||
assert_eq!(rect, Rect::new(10, 20, 30, 40));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resize_clamps_at_bounds() {
|
||||
let rect = Rect::new(u16::MAX - 2, u16::MAX - 3, 1, 1).resize(Size::new(10, 10));
|
||||
assert_eq!(rect, Rect::new(u16::MAX - 2, u16::MAX - 3, 2, 3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_const() {
|
||||
const RECT: Rect = Rect {
|
||||
|
||||
136
ratatui-core/src/layout/rect/ops.rs
Normal file
136
ratatui-core/src/layout/rect/ops.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
||||
|
||||
use super::{Offset, Rect};
|
||||
|
||||
impl Neg for Offset {
|
||||
type Output = Self;
|
||||
|
||||
/// Negates the offset.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the negated value overflows (i.e. `x` or `y` is `i32::MIN`).
|
||||
fn neg(self) -> Self {
|
||||
Self {
|
||||
x: self.x.neg(),
|
||||
y: self.y.neg(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Offset> for Rect {
|
||||
type Output = Self;
|
||||
|
||||
/// Moves the rect by an offset without changing its size.
|
||||
///
|
||||
/// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
|
||||
/// rect's position is clamped to the nearest edge.
|
||||
fn add(self, offset: Offset) -> Self {
|
||||
let max_x = i32::from(u16::MAX - self.width);
|
||||
let max_y = i32::from(u16::MAX - self.height);
|
||||
let x = i32::from(self.x).saturating_add(offset.x).clamp(0, max_x) as u16;
|
||||
let y = i32::from(self.y).saturating_add(offset.y).clamp(0, max_y) as u16;
|
||||
Self { x, y, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Rect> for Offset {
|
||||
type Output = Rect;
|
||||
|
||||
/// Moves the rect by an offset without changing its size.
|
||||
///
|
||||
/// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
|
||||
/// rect's position is clamped to the nearest edge.
|
||||
fn add(self, rect: Rect) -> Rect {
|
||||
rect + self
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Offset> for Rect {
|
||||
type Output = Self;
|
||||
|
||||
/// Subtracts an offset from the rect without changing its size.
|
||||
///
|
||||
/// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
|
||||
/// rect's position is clamped to the nearest
|
||||
fn sub(self, offset: Offset) -> Self {
|
||||
// Note this cannot be simplified to `self + -offset` because `Offset::MIN` would overflow
|
||||
let max_x = i32::from(u16::MAX - self.width);
|
||||
let max_y = i32::from(u16::MAX - self.height);
|
||||
let x = i32::from(self.x).saturating_sub(offset.x).clamp(0, max_x) as u16;
|
||||
let y = i32::from(self.y).saturating_sub(offset.y).clamp(0, max_y) as u16;
|
||||
Self { x, y, ..self }
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Offset> for Rect {
|
||||
/// Moves the rect by an offset in place without changing its size.
|
||||
///
|
||||
/// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
|
||||
/// rect's position is clamped to the nearest edge.
|
||||
fn add_assign(&mut self, offset: Offset) {
|
||||
*self = *self + offset;
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<Offset> for Rect {
|
||||
/// Moves the rect by an offset in place without changing its size.
|
||||
///
|
||||
/// If the offset would move the any of the rect's edges outside the bounds of `u16`, the
|
||||
/// rect's position is clamped to the nearest edge.
|
||||
fn sub_assign(&mut self, offset: Offset) {
|
||||
*self = *self - offset;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[rstest]
|
||||
#[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
|
||||
#[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(4, 6, 5, 6))]
|
||||
#[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(2, 2, 5, 6))]
|
||||
#[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MIN, Rect::new(0, 0, 5, 6))]
|
||||
#[case::saturate_positive(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(u16::MAX- 5, u16::MAX - 6, 5, 6))]
|
||||
fn add_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
|
||||
assert_eq!(rect + offset, expected);
|
||||
assert_eq!(offset + rect, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
|
||||
#[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(2, 2, 5, 6))]
|
||||
#[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(4, 6, 5, 6))]
|
||||
#[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(0, 0, 5, 6))]
|
||||
#[case::saturate_positive(Rect::new(3, 4, 5, 6), -Offset::MAX, Rect::new(u16::MAX - 5, u16::MAX - 6, 5, 6))]
|
||||
fn sub_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
|
||||
assert_eq!(rect - offset, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
|
||||
#[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(4, 6, 5, 6))]
|
||||
#[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(2, 2, 5, 6))]
|
||||
#[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MIN, Rect::new(0, 0, 5, 6))]
|
||||
#[case::saturate_positive(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(u16::MAX - 5, u16::MAX - 6, 5, 6))]
|
||||
fn add_assign_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
|
||||
let mut rect = rect;
|
||||
rect += offset;
|
||||
assert_eq!(rect, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::zero(Rect::new(3, 4, 5, 6), Offset::ZERO, Rect::new(3, 4, 5, 6))]
|
||||
#[case::positive(Rect::new(3, 4, 5, 6), Offset::new(1, 2), Rect::new(2, 2, 5, 6))]
|
||||
#[case::negative(Rect::new(3, 4, 5, 6), Offset::new(-1, -2), Rect::new(4, 6, 5, 6))]
|
||||
#[case::saturate_negative(Rect::new(3, 4, 5, 6), Offset::MAX, Rect::new(0, 0, 5, 6))]
|
||||
#[case::saturate_positive(Rect::new(3, 4, 5, 6), -Offset::MAX, Rect::new(u16::MAX - 5, u16::MAX - 6, 5, 6))]
|
||||
fn sub_assign_offset(#[case] rect: Rect, #[case] offset: Offset, #[case] expected: Rect) {
|
||||
let mut rect = rect;
|
||||
rect -= offset;
|
||||
assert_eq!(rect, expected);
|
||||
}
|
||||
}
|
||||
@@ -24,14 +24,20 @@ use crate::layout::Rect;
|
||||
/// - [`from(Rect)`](Self::from) - Create from [`Rect`] (uses width and height)
|
||||
/// - [`into((u16, u16))`] - Convert to `(u16, u16)` tuple
|
||||
///
|
||||
/// # Computation
|
||||
///
|
||||
/// - [`area`](Self::area) - Compute the total number of cells covered by the size
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use ratatui_core::layout::{Rect, Size};
|
||||
///
|
||||
/// let size = Size::new(80, 24);
|
||||
/// assert_eq!(size.area(), 1920);
|
||||
/// let size = Size::from((80, 24));
|
||||
/// let size = Size::from(Rect::new(0, 0, 80, 24));
|
||||
/// assert_eq!(size.area(), 1920);
|
||||
/// ```
|
||||
///
|
||||
/// For comprehensive layout documentation and examples, see the [`layout`](crate::layout) module.
|
||||
@@ -48,10 +54,24 @@ impl Size {
|
||||
/// A zero sized Size
|
||||
pub const ZERO: Self = Self::new(0, 0);
|
||||
|
||||
/// The minimum possible Size
|
||||
pub const MIN: Self = Self::ZERO;
|
||||
|
||||
/// The maximum possible Size
|
||||
pub const MAX: Self = Self::new(u16::MAX, u16::MAX);
|
||||
|
||||
/// Create a new `Size` struct
|
||||
pub const fn new(width: u16, height: u16) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
|
||||
/// Compute the total area of the size as a `u32`.
|
||||
///
|
||||
/// The multiplication uses `u32` to avoid overflow when the width and height are at their
|
||||
/// `u16` maximum values.
|
||||
pub const fn area(self) -> u32 {
|
||||
self.width as u32 * self.height as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(u16, u16)> for Size {
|
||||
@@ -60,6 +80,12 @@ impl From<(u16, u16)> for Size {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Size> for (u16, u16) {
|
||||
fn from(size: Size) -> Self {
|
||||
(size.width, size.height)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rect> for Size {
|
||||
fn from(rect: Rect) -> Self {
|
||||
rect.as_size()
|
||||
@@ -92,6 +118,14 @@ mod tests {
|
||||
assert_eq!(size.height, 20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_tuple() {
|
||||
let size = Size::from((10, 20));
|
||||
let (width, height) = size.into();
|
||||
assert_eq!(size.width, width);
|
||||
assert_eq!(size.height, height);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_rect() {
|
||||
let size = Size::from(Rect::new(0, 0, 10, 20));
|
||||
@@ -103,4 +137,11 @@ mod tests {
|
||||
fn display() {
|
||||
assert_eq!(Size::new(10, 20).to_string(), "10x20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn area() {
|
||||
assert_eq!(Size::new(10, 20).area(), 200);
|
||||
assert_eq!(Size::new(0, 0).area(), 0);
|
||||
assert_eq!(Size::new(u16::MAX, u16::MAX).area(), 4_294_836_225_u32);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,17 +250,41 @@ pub struct Style {
|
||||
/// The modifiers to add.
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(default, skip_serializing_if = "Modifier::is_empty")
|
||||
serde(
|
||||
default,
|
||||
skip_serializing_if = "Modifier::is_empty",
|
||||
deserialize_with = "deserialize_modifier"
|
||||
)
|
||||
)]
|
||||
pub add_modifier: Modifier,
|
||||
/// The modifiers to remove.
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
serde(default, skip_serializing_if = "Modifier::is_empty")
|
||||
serde(
|
||||
default,
|
||||
skip_serializing_if = "Modifier::is_empty",
|
||||
deserialize_with = "deserialize_modifier"
|
||||
)
|
||||
)]
|
||||
pub sub_modifier: Modifier,
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
/// Deserialize a [`Modifier`] while treating missing or `null` values as empty.
|
||||
///
|
||||
/// This helper is used with serde to coerce absent or `null` modifier fields to
|
||||
/// [`Modifier::empty`], allowing configuration files to omit these fields
|
||||
/// without triggering deserialization errors.
|
||||
fn deserialize_modifier<'de, D>(deserializer: D) -> Result<Modifier, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
use serde::Deserialize;
|
||||
|
||||
Option::<Modifier>::deserialize(deserializer)
|
||||
.map(|modifier| modifier.unwrap_or_else(Modifier::empty))
|
||||
}
|
||||
|
||||
/// A custom debug implementation that prints only the fields that are not the default, and unwraps
|
||||
/// the `Option`s.
|
||||
impl fmt::Debug for Style {
|
||||
@@ -989,4 +1013,19 @@ mod tests {
|
||||
let deserialized: Style = serde_json::from_str(&json_str).unwrap();
|
||||
assert_eq!(deserialized, style);
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[test]
|
||||
fn deserialize_null_modifiers() {
|
||||
let json_value = serde_json::json!({
|
||||
"add_modifier": serde_json::Value::Null,
|
||||
"sub_modifier": serde_json::Value::Null
|
||||
});
|
||||
let json_str = serde_json::to_string(&json_value).unwrap();
|
||||
|
||||
let style: Style = serde_json::from_str(&json_str).unwrap();
|
||||
|
||||
assert!(style.add_modifier.is_empty());
|
||||
assert!(style.sub_modifier.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@ pub mod half_block;
|
||||
pub mod line;
|
||||
pub mod marker;
|
||||
pub mod merge;
|
||||
pub mod pixel;
|
||||
pub mod scrollbar;
|
||||
pub mod shade;
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
pub const BLANK: u16 = 0x2800;
|
||||
pub const DOTS: [[u16; 2]; 4] = [
|
||||
[0x0001, 0x0008],
|
||||
[0x0002, 0x0010],
|
||||
[0x0004, 0x0020],
|
||||
[0x0040, 0x0080],
|
||||
//! Braille symbols.
|
||||
//!
|
||||
//! Note that the symbols are not listed according to their unicode codepoint but according to the
|
||||
//! corresponding bit pattern in row-major order.
|
||||
|
||||
pub const BRAILLE: [char; 256] = [
|
||||
'⠀', '⠁', '⠈', '⠉', '⠂', '⠃', '⠊', '⠋', '⠐', '⠑', '⠘', '⠙', '⠒', '⠓', '⠚', '⠛', '⠄', '⠅', '⠌',
|
||||
'⠍', '⠆', '⠇', '⠎', '⠏', '⠔', '⠕', '⠜', '⠝', '⠖', '⠗', '⠞', '⠟', '⠠', '⠡', '⠨', '⠩', '⠢', '⠣',
|
||||
'⠪', '⠫', '⠰', '⠱', '⠸', '⠹', '⠲', '⠳', '⠺', '⠻', '⠤', '⠥', '⠬', '⠭', '⠦', '⠧', '⠮', '⠯', '⠴',
|
||||
'⠵', '⠼', '⠽', '⠶', '⠷', '⠾', '⠿', '⡀', '⡁', '⡈', '⡉', '⡂', '⡃', '⡊', '⡋', '⡐', '⡑', '⡘', '⡙',
|
||||
'⡒', '⡓', '⡚', '⡛', '⡄', '⡅', '⡌', '⡍', '⡆', '⡇', '⡎', '⡏', '⡔', '⡕', '⡜', '⡝', '⡖', '⡗', '⡞',
|
||||
'⡟', '⡠', '⡡', '⡨', '⡩', '⡢', '⡣', '⡪', '⡫', '⡰', '⡱', '⡸', '⡹', '⡲', '⡳', '⡺', '⡻', '⡤', '⡥',
|
||||
'⡬', '⡭', '⡦', '⡧', '⡮', '⡯', '⡴', '⡵', '⡼', '⡽', '⡶', '⡷', '⡾', '⡿', '⢀', '⢁', '⢈', '⢉', '⢂',
|
||||
'⢃', '⢊', '⢋', '⢐', '⢑', '⢘', '⢙', '⢒', '⢓', '⢚', '⢛', '⢄', '⢅', '⢌', '⢍', '⢆', '⢇', '⢎', '⢏',
|
||||
'⢔', '⢕', '⢜', '⢝', '⢖', '⢗', '⢞', '⢟', '⢠', '⢡', '⢨', '⢩', '⢢', '⢣', '⢪', '⢫', '⢰', '⢱', '⢸',
|
||||
'⢹', '⢲', '⢳', '⢺', '⢻', '⢤', '⢥', '⢬', '⢭', '⢦', '⢧', '⢮', '⢯', '⢴', '⢵', '⢼', '⢽', '⢶', '⢷',
|
||||
'⢾', '⢿', '⣀', '⣁', '⣈', '⣉', '⣂', '⣃', '⣊', '⣋', '⣐', '⣑', '⣘', '⣙', '⣒', '⣓', '⣚', '⣛', '⣄',
|
||||
'⣅', '⣌', '⣍', '⣆', '⣇', '⣎', '⣏', '⣔', '⣕', '⣜', '⣝', '⣖', '⣗', '⣞', '⣟', '⣠', '⣡', '⣨', '⣩',
|
||||
'⣢', '⣣', '⣪', '⣫', '⣰', '⣱', '⣸', '⣹', '⣲', '⣳', '⣺', '⣻', '⣤', '⣥', '⣬', '⣭', '⣦', '⣧', '⣮',
|
||||
'⣯', '⣴', '⣵', '⣼', '⣽', '⣶', '⣷', '⣾', '⣿',
|
||||
];
|
||||
|
||||
@@ -4,6 +4,7 @@ pub const DOT: &str = "•";
|
||||
|
||||
/// Marker to use when plotting data points
|
||||
#[derive(Debug, Default, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum Marker {
|
||||
/// One point per cell in shape of dot (`•`)
|
||||
#[default]
|
||||
@@ -25,6 +26,33 @@ pub enum Marker {
|
||||
/// 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,
|
||||
/// Use quadrant characters to represent data points.
|
||||
///
|
||||
/// Quadrant characters display densely packed and regularly spaced pseudo-pixels with a 2x2
|
||||
/// resolution per character, without visible bands between cells.
|
||||
Quadrant,
|
||||
/// Use sextant characters from the [Unicode Symbols for Legacy Computing
|
||||
/// Supplement](https://en.wikipedia.org/wiki/Symbols_for_Legacy_Computing_Supplement) to
|
||||
/// represent data points.
|
||||
///
|
||||
/// Sextant characters display densely packed and regularly spaced pseudo-pixels with a 2x3
|
||||
/// resolution per character, without visible bands between cells.
|
||||
///
|
||||
/// Note: the Symbols for Legacy Computing Supplement block is a relatively recent addition to
|
||||
/// unicode that is less broadly supported than Braille dots. If your terminal does not support
|
||||
/// this, you will see unicode replacement characters (`<60>`) instead of sextants (`🬌`, `🬲`, `🬑`).
|
||||
Sextant,
|
||||
/// Use octant characters from the [Unicode Symbols for Legacy Computing
|
||||
/// Supplement](https://en.wikipedia.org/wiki/Symbols_for_Legacy_Computing_Supplement) to
|
||||
/// represent data points.
|
||||
///
|
||||
/// Octant characters have the same 2x4 resolution as Braille characters but display densely
|
||||
/// packed and regularly spaced pseudo-pixels, without visible bands between cells.
|
||||
///
|
||||
/// Note: the Symbols for Legacy Computing Supplement block is a relatively recent addition to
|
||||
/// unicode that is less broadly supported than Braille dots. If your terminal does not support
|
||||
/// this, you will see unicode replacement characters (`<60>`) instead of octants (``, ``, ``).
|
||||
Octant,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
30
ratatui-core/src/symbols/pixel.rs
Normal file
30
ratatui-core/src/symbols/pixel.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
//! Pseudo-pixel symbols: quadrant, sextant and octant characters.
|
||||
//!
|
||||
//! Note that the symbols are not listed according to their unicode codepoint but according to the
|
||||
//! corresponding bit pattern in row-major order.
|
||||
|
||||
pub const QUADRANTS: [char; 16] = [
|
||||
' ', '▘', '▝', '▀', '▖', '▌', '▞', '▛', '▗', '▚', '▐', '▜', '▄', '▙', '▟', '█',
|
||||
];
|
||||
pub const SEXTANTS: [char; 64] = [
|
||||
' ', '🬀', '🬁', '🬂', '🬃', '🬄', '🬅', '🬆', '🬇', '🬈', '🬉', '🬊', '🬋', '🬌', '🬍', '🬎', '🬏', '🬐', '🬑',
|
||||
'🬒', '🬓', '▌', '🬔', '🬕', '🬖', '🬗', '🬘', '🬙', '🬚', '🬛', '🬜', '🬝', '🬞', '🬟', '🬠', '🬡', '🬢', '🬣',
|
||||
'🬤', '🬥', '🬦', '🬧', '▐', '🬨', '🬩', '🬪', '🬫', '🬬', '🬭', '🬮', '🬯', '🬰', '🬱', '🬲', '🬳', '🬴', '🬵',
|
||||
'🬶', '🬷', '🬸', '🬹', '🬺', '🬻', '█',
|
||||
];
|
||||
pub const OCTANTS: [char; 256] = [
|
||||
' ', '', '', '🮂', '', '▘', '', '', '', '', '▝', '', '', '', '', '▀', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '🮅', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '▖', '', '', '', '', '▌', '', '', '', '', '▞', '', '', '', '',
|
||||
'▛', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '▗', '', '', '', '', '▚', '', '', '', '', '▐',
|
||||
'', '', '', '', '▜', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '▂', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||
'', '', '', '', '', '', '', '', '', '', '', '', '▄', '', '', '', '', '▙', '',
|
||||
'', '', '', '▟', '', '▆', '', '', '█',
|
||||
];
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "ratatui-crossterm"
|
||||
version = "0.1.0-beta.0"
|
||||
description = "Crossterm backend for the Ratatui Terminal UI library."
|
||||
documentation = "https://docs.rs/ratatui-crossterm/"
|
||||
documentation = "https://docs.rs/ratatui-crossterm"
|
||||
readme = "README.md"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
[package]
|
||||
name = "ratatui-macros"
|
||||
version = "0.7.0-beta.0"
|
||||
description = "Macros for Ratatui"
|
||||
edition.workspace = true
|
||||
authors = ["The Ratatui Developers"]
|
||||
description = "Macros for Ratatui"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/ratatui/ratatui"
|
||||
documentation = "https://docs.rs/ratatui-macros"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "ratatui-termion"
|
||||
version = "0.1.0-beta.0"
|
||||
description = "Termion backend for the Ratatui Terminal UI library."
|
||||
documentation = "https://docs.rs/ratatui-termion/"
|
||||
documentation = "https://docs.rs/ratatui-termion"
|
||||
readme = "README.md"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "ratatui-termwiz"
|
||||
version = "0.1.0-beta.0"
|
||||
description = "Termwiz backend for the Ratatui Terminal UI library."
|
||||
documentation = "https://docs.rs/ratatui-termwiz/"
|
||||
documentation = "https://docs.rs/ratatui-termwiz"
|
||||
readme = "README.md"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
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-beta.0"
|
||||
description = "A collection of Ratatui widgets for building terminal user interfaces using Ratatui."
|
||||
documentation = "https://docs.rs/ratatui-widgets"
|
||||
readme = "README.md"
|
||||
authors.workspace = true
|
||||
documentation.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
keywords.workspace = true
|
||||
@@ -68,7 +68,7 @@ unicode-width.workspace = true
|
||||
color-eyre.workspace = true
|
||||
crossterm.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
ratatui = { path = "../ratatui" }
|
||||
ratatui.workspace = true
|
||||
rstest.workspace = true
|
||||
|
||||
[lints]
|
||||
|
||||
@@ -53,7 +53,7 @@ fn render(frame: &mut Frame, selected_tab: usize) {
|
||||
frame.render_widget(title.centered(), top);
|
||||
|
||||
render_content(frame, main, selected_tab);
|
||||
render_tabs(frame, main.offset(Offset { x: 1, y: 0 }), selected_tab);
|
||||
render_tabs(frame, main + Offset::new(1, 0), selected_tab);
|
||||
}
|
||||
|
||||
/// Render the tabs.
|
||||
|
||||
@@ -104,6 +104,36 @@ impl<'a, DS: DateStyler> Monthly<'a, DS> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Return the width required to render the calendar.
|
||||
#[must_use]
|
||||
pub fn width(&self) -> u16 {
|
||||
const DAYS_PER_WEEK: u16 = 7;
|
||||
const GUTTER_WIDTH: u16 = 1;
|
||||
const DAY_WIDTH: u16 = 2;
|
||||
|
||||
let mut width = DAYS_PER_WEEK * (GUTTER_WIDTH + DAY_WIDTH);
|
||||
if let Some(block) = &self.block {
|
||||
let (left, right) = block.horizontal_space();
|
||||
width = width.saturating_add(left).saturating_add(right);
|
||||
}
|
||||
width
|
||||
}
|
||||
|
||||
/// Return the height required to render the calendar.
|
||||
#[must_use]
|
||||
pub fn height(&self) -> u16 {
|
||||
let mut height = u16::from(sunday_based_weeks(self.display_date))
|
||||
.saturating_add(u16::from(self.show_month.is_some()))
|
||||
.saturating_add(u16::from(self.show_weekday.is_some()));
|
||||
|
||||
if let Some(block) = &self.block {
|
||||
let (top, bottom) = block.vertical_space();
|
||||
height = height.saturating_add(top).saturating_add(bottom);
|
||||
}
|
||||
|
||||
height
|
||||
}
|
||||
|
||||
/// Return a style with only the background from the default style
|
||||
const fn default_bg(&self) -> Style {
|
||||
match self.default_style.bg {
|
||||
@@ -200,6 +230,22 @@ impl<DS: DateStyler> Monthly<'_, DS> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute how many Sunday-based week rows are needed to render `display_date`.
|
||||
///
|
||||
/// Mirrors the rendering logic by taking the difference between the first and last day
|
||||
/// Sunday-based week numbers (inclusive).
|
||||
fn sunday_based_weeks(display_date: Date) -> u8 {
|
||||
let first_of_month = display_date
|
||||
.replace_day(1)
|
||||
.expect("valid first day of month");
|
||||
let last_of_month = first_of_month
|
||||
.replace_day(first_of_month.month().length(first_of_month.year()))
|
||||
.expect("valid last of month");
|
||||
let first_week = first_of_month.sunday_based_week();
|
||||
let last_week = last_of_month.sunday_based_week();
|
||||
last_week.saturating_sub(first_week) + 1
|
||||
}
|
||||
|
||||
/// Provides a method for styling a given date. [Monthly] is generic on this trait, so any type
|
||||
/// that implements this trait can be used.
|
||||
pub trait DateStyler {
|
||||
@@ -268,10 +314,11 @@ impl Default for CalendarEventStore {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ratatui_core::style::Color;
|
||||
use ratatui_core::style::{Color, Style};
|
||||
use time::Month;
|
||||
|
||||
use super::*;
|
||||
use crate::block::{Block, Padding};
|
||||
|
||||
#[test]
|
||||
fn event_store() {
|
||||
@@ -325,4 +372,87 @@ mod tests {
|
||||
// This should not panic, even if the buffer has zero size.
|
||||
calendar.render(buffer.area, &mut buffer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calendar_width_reflects_grid_layout() {
|
||||
let date = Date::from_calendar_date(2023, Month::January, 1).unwrap();
|
||||
let calendar = Monthly::new(date, CalendarEventStore::default());
|
||||
assert_eq!(calendar.width(), 21);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calendar_height_counts_weeks_and_headers() {
|
||||
let date = Date::from_calendar_date(2015, Month::February, 1).unwrap();
|
||||
let base_calendar = Monthly::new(date, CalendarEventStore::default());
|
||||
assert_eq!(base_calendar.height(), 4);
|
||||
|
||||
let decorated_calendar = Monthly::new(date, CalendarEventStore::default())
|
||||
.show_month_header(Style::default())
|
||||
.show_weekdays_header(Style::default());
|
||||
assert_eq!(decorated_calendar.height(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calendar_dimensions_examples() {
|
||||
// Feb 2015 starts Sunday and spans 4 rows.
|
||||
let feb_2015 = Date::from_calendar_date(2015, Month::February, 1).unwrap();
|
||||
let cal = Monthly::new(feb_2015, CalendarEventStore::default());
|
||||
assert_eq!(cal.width(), 21, "4w base width");
|
||||
assert_eq!(cal.height(), 4, "Feb 2015 rows");
|
||||
|
||||
let cal = Monthly::new(feb_2015, CalendarEventStore::default())
|
||||
.show_month_header(Style::default())
|
||||
.show_weekdays_header(Style::default());
|
||||
assert_eq!(cal.height(), 6, "Headers add 2 rows");
|
||||
|
||||
let block = Block::bordered().padding(Padding::new(2, 3, 1, 2));
|
||||
let cal = Monthly::new(feb_2015, CalendarEventStore::default()).block(block);
|
||||
assert_eq!(cal.width(), 28, "Padding widens width");
|
||||
assert_eq!(cal.height(), 9, "Padding grows height");
|
||||
|
||||
// Feb 2024 starts Thursday and spans 5 rows.
|
||||
let feb_2024 = Date::from_calendar_date(2024, Month::February, 1).unwrap();
|
||||
let cal = Monthly::new(feb_2024, CalendarEventStore::default());
|
||||
assert_eq!(cal.width(), 21, "5w base width");
|
||||
assert_eq!(cal.height(), 5, "Feb 2024 rows");
|
||||
|
||||
let cal = Monthly::new(feb_2024, CalendarEventStore::default())
|
||||
.show_month_header(Style::default())
|
||||
.show_weekdays_header(Style::default());
|
||||
assert_eq!(cal.height(), 7, "Headers add 2 rows (5w)");
|
||||
|
||||
let cal = Monthly::new(feb_2024, CalendarEventStore::default()).block(Block::bordered());
|
||||
assert_eq!(cal.width(), 23, "Border adds 2 cols");
|
||||
assert_eq!(cal.height(), 7, "Border adds 2 rows");
|
||||
|
||||
// Apr 2023 starts Saturday and spans 6 rows.
|
||||
let apr_2023 = Date::from_calendar_date(2023, Month::April, 1).unwrap();
|
||||
let cal = Monthly::new(apr_2023, CalendarEventStore::default());
|
||||
assert_eq!(cal.width(), 21, "6w base width");
|
||||
assert_eq!(cal.height(), 6, "Apr 2023 rows");
|
||||
|
||||
let cal = Monthly::new(apr_2023, CalendarEventStore::default())
|
||||
.show_month_header(Style::default())
|
||||
.show_weekdays_header(Style::default());
|
||||
assert_eq!(cal.height(), 8, "Headers add 2 rows (6w)");
|
||||
|
||||
let block = Block::bordered().padding(Padding::symmetric(1, 1));
|
||||
let cal = Monthly::new(apr_2023, CalendarEventStore::default()).block(block);
|
||||
assert_eq!(cal.width(), 25, "Symmetric padding width");
|
||||
assert_eq!(cal.height(), 10, "Symmetric padding height");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sunday_based_weeks_shapes() {
|
||||
let sunday_start =
|
||||
Date::from_calendar_date(2015, Month::February, 11).expect("valid test date");
|
||||
let saturday_start =
|
||||
Date::from_calendar_date(2023, Month::April, 9).expect("valid test date");
|
||||
let leap_year =
|
||||
Date::from_calendar_date(2024, Month::February, 29).expect("valid test date");
|
||||
|
||||
assert_eq!(sunday_based_weeks(sunday_start), 4);
|
||||
assert_eq!(sunday_based_weeks(saturday_start), 6);
|
||||
assert_eq!(sunday_based_weeks(leap_year), 5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
//! You can also implement your own custom [`Shape`]s.
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
@@ -24,6 +23,8 @@ use itertools::Itertools;
|
||||
use ratatui_core::buffer::Buffer;
|
||||
use ratatui_core::layout::Rect;
|
||||
use ratatui_core::style::{Color, Style};
|
||||
use ratatui_core::symbols::braille::BRAILLE;
|
||||
use ratatui_core::symbols::pixel::{OCTANTS, QUADRANTS, SEXTANTS};
|
||||
use ratatui_core::symbols::{self, Marker};
|
||||
use ratatui_core::text::Line as TextLine;
|
||||
use ratatui_core::widgets::Widget;
|
||||
@@ -69,11 +70,20 @@ pub struct Label<'a> {
|
||||
/// multiple shapes on the canvas in specific order.
|
||||
#[derive(Debug)]
|
||||
struct Layer {
|
||||
// A string of characters representing the grid. This will be wrapped to the width of the grid
|
||||
// when rendering
|
||||
string: String,
|
||||
// Colors for foreground and background of each cell
|
||||
colors: Vec<(Color, Color)>,
|
||||
contents: Vec<LayerCell>,
|
||||
}
|
||||
|
||||
/// A cell within a layer.
|
||||
///
|
||||
/// If a [`Context`] contains multiple layers, then the symbol, foreground, and background colors
|
||||
/// for a character will be determined by the top-most layer that provides a value for that
|
||||
/// character. For example, a chart drawn with [`Marker::Block`] may provide the background color,
|
||||
/// and a later chart drawn with [`Marker::Braille`] may provide the symbol and foreground color.
|
||||
#[derive(Debug)]
|
||||
struct LayerCell {
|
||||
symbol: Option<char>,
|
||||
fg: Option<Color>,
|
||||
bg: Option<Color>,
|
||||
}
|
||||
|
||||
/// A grid of cells that can be painted on.
|
||||
@@ -100,73 +110,111 @@ trait Grid: fmt::Debug {
|
||||
fn reset(&mut self);
|
||||
}
|
||||
|
||||
/// The `BrailleGrid` is a grid made up of cells each containing a Braille pattern.
|
||||
/// The pattern and color of a `PatternGrid` cell.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
struct PatternCell {
|
||||
/// The pattern of a grid character.
|
||||
///
|
||||
/// The pattern is stored in the lower bits in a row-major order. For instance, for a 2x4
|
||||
/// pattern marker, bits 0 to 7 of this field should represent the following pseudo-pixels:
|
||||
///
|
||||
/// | 0 1 |
|
||||
/// | 2 3 |
|
||||
/// | 4 5 |
|
||||
/// | 6 7 |
|
||||
pattern: u8,
|
||||
/// The color of a cell only supports foreground colors for now as there's no way to
|
||||
/// individually set the background color of each pseudo-pixel in a pattern character.
|
||||
color: Option<Color>,
|
||||
}
|
||||
|
||||
/// The `PatternGrid` is a grid made up of cells each containing a `W`x`H` pattern character.
|
||||
///
|
||||
/// This makes it possible to draw shapes with a resolution of 2x4 dots per cell. This is useful
|
||||
/// when you want to draw shapes with a high resolution. Font support for Braille patterns is
|
||||
/// required to see the dots. If your terminal or font does not support this unicode block, you
|
||||
/// will see unicode replacement characters (<28>) instead of braille dots.
|
||||
/// This makes it possible to draw shapes with a resolution of e.g. 2x4 (Braille or unicode octant)
|
||||
/// per cell.
|
||||
/// Font support for the relevant pattern character is required. If your terminal or font does not
|
||||
/// support the relevant unicode block, you will see unicode replacement characters (<28>) instead.
|
||||
///
|
||||
/// This grid type only supports a single foreground color for each 2x4 dots cell. There is no way
|
||||
/// to set the individual color of each dot in the braille pattern.
|
||||
/// This grid type only supports a single foreground color for each `W`x`H` pattern character.
|
||||
/// There is no way to set the individual color of each pseudo-pixel.
|
||||
#[derive(Debug)]
|
||||
struct BrailleGrid {
|
||||
struct PatternGrid<const W: usize, const H: usize> {
|
||||
/// Width of the grid in number of terminal columns
|
||||
width: u16,
|
||||
/// Height of the grid in number of terminal rows
|
||||
height: u16,
|
||||
/// Represents the unicode braille patterns. Will take a value between `0x2800` and `0x28FF`
|
||||
/// this is converted to an utf16 string when converting to a layer. See
|
||||
/// <https://en.wikipedia.org/wiki/Braille_Patterns> for more info.
|
||||
utf16_code_points: Vec<u16>,
|
||||
/// The color of each cell only supports foreground colors for now as there's no way to
|
||||
/// individually set the background color of each dot in the braille pattern.
|
||||
colors: Vec<Color>,
|
||||
/// Pattern and color of the cells.
|
||||
cells: Vec<PatternCell>,
|
||||
/// Lookup table mapping patterns to characters.
|
||||
char_table: &'static [char],
|
||||
}
|
||||
|
||||
impl BrailleGrid {
|
||||
/// Create a new `BrailleGrid` with the given width and height measured in terminal columns and
|
||||
/// rows respectively.
|
||||
fn new(width: u16, height: u16) -> Self {
|
||||
impl<const W: usize, const H: usize> PatternGrid<W, H> {
|
||||
/// Statically check that the dimension of the pattern is supported.
|
||||
const _PATTERN_DIMENSION_CHECK: usize = u8::BITS as usize - W * H;
|
||||
|
||||
/// Create a new `PatternGrid` with the given width and height measured in terminal columns
|
||||
/// and rows respectively.
|
||||
fn new(width: u16, height: u16, char_table: &'static [char]) -> Self {
|
||||
// Cause a static error if the pattern doesn't fit within 8 bits.
|
||||
let _ = Self::_PATTERN_DIMENSION_CHECK;
|
||||
|
||||
let length = usize::from(width) * usize::from(height);
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
utf16_code_points: vec![symbols::braille::BLANK; length],
|
||||
colors: vec![Color::Reset; length],
|
||||
cells: vec![PatternCell::default(); length],
|
||||
char_table,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Grid for BrailleGrid {
|
||||
impl<const W: usize, const H: usize> Grid for PatternGrid<W, H> {
|
||||
fn resolution(&self) -> (f64, f64) {
|
||||
(f64::from(self.width) * 2.0, f64::from(self.height) * 4.0)
|
||||
(
|
||||
f64::from(self.width) * W as f64,
|
||||
f64::from(self.height) * H as f64,
|
||||
)
|
||||
}
|
||||
|
||||
fn save(&self) -> Layer {
|
||||
let string = String::from_utf16(&self.utf16_code_points).unwrap();
|
||||
// the background color is always reset for braille patterns
|
||||
let colors = self.colors.iter().map(|c| (*c, Color::Reset)).collect();
|
||||
Layer { string, colors }
|
||||
let contents = self
|
||||
.cells
|
||||
.iter()
|
||||
.map(|&cell| {
|
||||
let symbol = match cell.pattern {
|
||||
// Skip rendering blank patterns to allow layers underneath
|
||||
// to show through.
|
||||
0 => None,
|
||||
idx => Some(self.char_table[idx as usize]),
|
||||
};
|
||||
|
||||
LayerCell {
|
||||
symbol,
|
||||
fg: cell.color,
|
||||
// Patterns only affect foreground.
|
||||
bg: None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Layer { contents }
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.utf16_code_points.fill(symbols::braille::BLANK);
|
||||
self.colors.fill(Color::Reset);
|
||||
self.cells.fill_with(Default::default);
|
||||
}
|
||||
|
||||
fn paint(&mut self, x: usize, y: usize, color: Color) {
|
||||
let index = y
|
||||
.saturating_div(4)
|
||||
.saturating_div(H)
|
||||
.saturating_mul(self.width as usize)
|
||||
.saturating_add(x.saturating_div(2));
|
||||
.saturating_add(x.saturating_div(W));
|
||||
// using get_mut here because we are indexing the vector with usize values
|
||||
// and we want to make sure we don't panic if the index is out of bounds
|
||||
if let Some(c) = self.utf16_code_points.get_mut(index) {
|
||||
*c |= symbols::braille::DOTS[y % 4][x % 2];
|
||||
}
|
||||
if let Some(c) = self.colors.get_mut(index) {
|
||||
*c = color;
|
||||
if let Some(cell) = self.cells.get_mut(index) {
|
||||
cell.pattern |= 1u8 << ((x % W) + W * (y % H));
|
||||
cell.color = Some(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,12 +229,16 @@ struct CharGrid {
|
||||
width: u16,
|
||||
/// Height of the grid in number of terminal rows
|
||||
height: u16,
|
||||
/// Represents a single character for each cell
|
||||
cells: Vec<char>,
|
||||
/// The color of each cell
|
||||
colors: Vec<Color>,
|
||||
cells: Vec<Option<Color>>,
|
||||
|
||||
/// The character to use for every cell - e.g. a block, dot, etc.
|
||||
cell_char: char,
|
||||
|
||||
/// If true, apply the color to the background as well as the foreground. This is used for
|
||||
/// [`Marker::Block`], so that it will overwrite any previous foreground character, but also
|
||||
/// leave a background that can be overlaid with an additional foreground character.
|
||||
apply_color_to_bg: bool,
|
||||
}
|
||||
|
||||
impl CharGrid {
|
||||
@@ -197,9 +249,16 @@ impl CharGrid {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
cells: vec![' '; length],
|
||||
colors: vec![Color::Reset; length],
|
||||
cells: vec![None; length],
|
||||
cell_char,
|
||||
apply_color_to_bg: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_color_to_bg(self) -> Self {
|
||||
Self {
|
||||
apply_color_to_bg: true,
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,14 +270,20 @@ impl Grid for CharGrid {
|
||||
|
||||
fn save(&self) -> Layer {
|
||||
Layer {
|
||||
string: self.cells.iter().collect(),
|
||||
colors: self.colors.iter().map(|c| (*c, Color::Reset)).collect(),
|
||||
contents: self
|
||||
.cells
|
||||
.iter()
|
||||
.map(|&color| LayerCell {
|
||||
symbol: color.map(|_| self.cell_char),
|
||||
fg: color,
|
||||
bg: color.filter(|_| self.apply_color_to_bg),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.cells.fill(' ');
|
||||
self.colors.fill(Color::Reset);
|
||||
self.cells.fill(None);
|
||||
}
|
||||
|
||||
fn paint(&mut self, x: usize, y: usize, color: Color) {
|
||||
@@ -226,10 +291,7 @@ impl Grid for CharGrid {
|
||||
// using get_mut here because we are indexing the vector with usize values
|
||||
// and we want to make sure we don't panic if the index is out of bounds
|
||||
if let Some(c) = self.cells.get_mut(index) {
|
||||
*c = self.cell_char;
|
||||
}
|
||||
if let Some(c) = self.colors.get_mut(index) {
|
||||
*c = color;
|
||||
*c = Some(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,7 +306,7 @@ impl Grid for CharGrid {
|
||||
/// and lower half of each cell. This allows us to draw shapes with a resolution of 1x2 "pixels" per
|
||||
/// cell.
|
||||
///
|
||||
/// This allows for more flexibility than the `BrailleGrid` which only supports a single
|
||||
/// This allows for more flexibility than the `PatternGrid` which only supports a single
|
||||
/// foreground color for each 2x4 dots cell, and the `CharGrid` which only supports a single
|
||||
/// character for each cell.
|
||||
#[derive(Debug)]
|
||||
@@ -254,7 +316,7 @@ struct HalfBlockGrid {
|
||||
/// Height of the grid in number of terminal rows
|
||||
height: u16,
|
||||
/// Represents a single color for each "pixel" arranged in column, row order
|
||||
pixels: Vec<Vec<Color>>,
|
||||
pixels: Vec<Vec<Option<Color>>>,
|
||||
}
|
||||
|
||||
impl HalfBlockGrid {
|
||||
@@ -264,7 +326,7 @@ impl HalfBlockGrid {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
pixels: vec![vec![Color::Reset; width as usize]; (height as usize) * 2],
|
||||
pixels: vec![vec![None; width as usize]; (height as usize) * 2],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -302,45 +364,34 @@ impl Grid for HalfBlockGrid {
|
||||
.tuples()
|
||||
.flat_map(|(upper_row, lower_row)| zip(upper_row, lower_row));
|
||||
|
||||
// then we work out what character to print for each pair of pixels
|
||||
let string = vertical_color_pairs
|
||||
.clone()
|
||||
.map(|(upper, lower)| match (upper, lower) {
|
||||
(Color::Reset, Color::Reset) => ' ',
|
||||
(Color::Reset, _) => symbols::half_block::LOWER,
|
||||
(_, Color::Reset) => symbols::half_block::UPPER,
|
||||
(&lower, &upper) => {
|
||||
if lower == upper {
|
||||
symbols::half_block::FULL
|
||||
} else {
|
||||
symbols::half_block::UPPER
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// then we convert these each vertical pair of pixels into a foreground and background color
|
||||
let colors = vertical_color_pairs
|
||||
// Then we determine the character to print for each pair, along with the color of the
|
||||
// foreground and background.
|
||||
let contents = vertical_color_pairs
|
||||
.map(|(upper, lower)| {
|
||||
let (fg, bg) = match (upper, lower) {
|
||||
(Color::Reset, Color::Reset) => (Color::Reset, Color::Reset),
|
||||
(Color::Reset, &lower) => (lower, Color::Reset),
|
||||
(&upper, Color::Reset) => (upper, Color::Reset),
|
||||
(&upper, &lower) => (upper, lower),
|
||||
let (symbol, fg, bg) = match (upper, lower) {
|
||||
(None, None) => (None, None, None),
|
||||
(None, Some(lower)) => (Some(symbols::half_block::LOWER), Some(*lower), None),
|
||||
(Some(upper), None) => (Some(symbols::half_block::UPPER), Some(*upper), None),
|
||||
(Some(upper), Some(lower)) if lower == upper => {
|
||||
(Some(symbols::half_block::FULL), Some(*upper), Some(*lower))
|
||||
}
|
||||
(Some(upper), Some(lower)) => {
|
||||
(Some(symbols::half_block::UPPER), Some(*upper), Some(*lower))
|
||||
}
|
||||
};
|
||||
(fg, bg)
|
||||
LayerCell { symbol, fg, bg }
|
||||
})
|
||||
.collect();
|
||||
|
||||
Layer { string, colors }
|
||||
Layer { contents }
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.pixels.fill(vec![Color::Reset; self.width as usize]);
|
||||
self.pixels.fill(vec![None; self.width as usize]);
|
||||
}
|
||||
|
||||
fn paint(&mut self, x: usize, y: usize, color: Color) {
|
||||
self.pixels[y][x] = color;
|
||||
self.pixels[y][x] = Some(color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,7 +513,17 @@ impl<'a, 'b> From<&'a mut Context<'b>> for Painter<'a, 'b> {
|
||||
/// this as similar to the `Frame` struct that is used to draw widgets on the terminal.
|
||||
#[derive(Debug)]
|
||||
pub struct Context<'a> {
|
||||
// Width of the canvas in cells.
|
||||
//
|
||||
// This is NOT the resolution in dots/pixels as this varies by marker type.
|
||||
width: u16,
|
||||
// Height of the canvas in cells.
|
||||
//
|
||||
// This is NOT the resolution in dots/pixels as this varies by marker type.
|
||||
height: u16,
|
||||
// Canvas coordinate system width
|
||||
x_bounds: [f64; 2],
|
||||
// Canvas coordinate system height
|
||||
y_bounds: [f64; 2],
|
||||
grid: Box<dyn Grid>,
|
||||
dirty: bool,
|
||||
@@ -501,17 +562,10 @@ impl<'a> Context<'a> {
|
||||
y_bounds: [f64; 2],
|
||||
marker: Marker,
|
||||
) -> Self {
|
||||
let dot = symbols::DOT.chars().next().unwrap();
|
||||
let block = symbols::block::FULL.chars().next().unwrap();
|
||||
let bar = symbols::bar::HALF.chars().next().unwrap();
|
||||
let grid: Box<dyn Grid> = match marker {
|
||||
Marker::Dot => Box::new(CharGrid::new(width, height, dot)),
|
||||
Marker::Block => Box::new(CharGrid::new(width, height, block)),
|
||||
Marker::Bar => Box::new(CharGrid::new(width, height, bar)),
|
||||
Marker::Braille => Box::new(BrailleGrid::new(width, height)),
|
||||
Marker::HalfBlock => Box::new(HalfBlockGrid::new(width, height)),
|
||||
};
|
||||
let grid = Self::marker_to_grid(width, height, marker);
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
x_bounds,
|
||||
y_bounds,
|
||||
grid,
|
||||
@@ -521,6 +575,30 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn marker_to_grid(width: u16, height: u16, marker: Marker) -> Box<dyn Grid> {
|
||||
let dot = symbols::DOT.chars().next().unwrap();
|
||||
let block = symbols::block::FULL.chars().next().unwrap();
|
||||
let bar = symbols::bar::HALF.chars().next().unwrap();
|
||||
match marker {
|
||||
Marker::Block => Box::new(CharGrid::new(width, height, block).apply_color_to_bg()),
|
||||
Marker::Bar => Box::new(CharGrid::new(width, height, bar)),
|
||||
Marker::Braille => Box::new(PatternGrid::<2, 4>::new(width, height, &BRAILLE)),
|
||||
Marker::HalfBlock => Box::new(HalfBlockGrid::new(width, height)),
|
||||
Marker::Quadrant => Box::new(PatternGrid::<2, 2>::new(width, height, &QUADRANTS)),
|
||||
Marker::Sextant => Box::new(PatternGrid::<2, 3>::new(width, height, &SEXTANTS)),
|
||||
Marker::Octant => Box::new(PatternGrid::<2, 4>::new(width, height, &OCTANTS)),
|
||||
Marker::Dot | _ => Box::new(CharGrid::new(width, height, dot)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Change the marker being used in this context.
|
||||
///
|
||||
/// This will save the last layer if necessary and reset the grid to use the new marker.
|
||||
pub fn marker(&mut self, marker: Marker) {
|
||||
self.finish();
|
||||
self.grid = Self::marker_to_grid(self.width, self.height, marker);
|
||||
}
|
||||
|
||||
/// Draw the given [`Shape`] in this context
|
||||
pub fn draw<S>(&mut self, shape: &S)
|
||||
where
|
||||
@@ -573,16 +651,25 @@ impl<'a> Context<'a> {
|
||||
///
|
||||
/// By default the grid is made of Braille patterns but you may change the marker to use a different
|
||||
/// set of symbols. If your terminal or font does not support this unicode block, you will see
|
||||
/// unicode replacement characters (<28>) instead of braille dots. The Braille patterns provide a more
|
||||
/// fine grained result (2x4 dots) but you might want to use a simple dot, block, or bar instead by
|
||||
/// calling the [`marker`] method if your target environment does not support those symbols.
|
||||
/// unicode replacement characters (<28>) instead of braille dots. The Braille patterns (as well the
|
||||
/// octant character patterns) provide a more fine grained result with a 2x4 resolution per
|
||||
/// character, but you might want to use a simple dot, block, or bar instead by calling the
|
||||
/// [`marker`] method if your target environment does not support those symbols.
|
||||
///
|
||||
/// See [Unicode Braille Patterns](https://en.wikipedia.org/wiki/Braille_Patterns) for more info.
|
||||
///
|
||||
/// The `Octant` marker is similar to the `Braille` marker but, instead of sparse dots, displays
|
||||
/// densely packed and regularly spaced pseudo-pixels, without visible bands between rows and
|
||||
/// columns. However, it uses characters that are not yet as widely supported as the Braille
|
||||
/// unicode block.
|
||||
///
|
||||
/// The `Quadrant` and `Sextant` markers are in turn akin to the `Octant` marker, but with a 2x2
|
||||
/// and 2x3 resolution, respectively.
|
||||
///
|
||||
/// The `HalfBlock` marker is useful when you want to draw shapes with a higher resolution than a
|
||||
/// `CharGrid` but lower than a `BrailleGrid`. This grid type supports a foreground and background
|
||||
/// color for each terminal cell. This allows for more flexibility than the `BrailleGrid` which only
|
||||
/// supports a single foreground color for each 2x4 dots cell.
|
||||
/// `CharGrid` but lower than a `PatternGrid`. This grid type supports a foreground and background
|
||||
/// color for each terminal cell. This allows for more flexibility than the `PatternGrid` which
|
||||
/// only supports a single foreground color for each 2x4 dots cell.
|
||||
///
|
||||
/// The Canvas widget is used by calling the [`Canvas::paint`] method and passing a closure that
|
||||
/// will be used to draw on the canvas. The closure will be passed a [`Context`] object that can be
|
||||
@@ -723,7 +810,7 @@ where
|
||||
/// The [`HalfBlock`] marker is useful when you want to draw shapes with a higher resolution
|
||||
/// than with a grid of characters (e.g. with [`Block`] or [`Dot`]) but lower than with
|
||||
/// [`Braille`]. This grid type supports a foreground and background color for each terminal
|
||||
/// cell. This allows for more flexibility than the `BrailleGrid` which only supports a single
|
||||
/// cell. This allows for more flexibility than the `PatternGrid` which only supports a single
|
||||
/// foreground color for each 2x4 dots cell.
|
||||
///
|
||||
/// [`Braille`]: ratatui_core::symbols::Marker::Braille
|
||||
@@ -802,19 +889,21 @@ where
|
||||
|
||||
// Retrieve painted points for each layer
|
||||
for layer in ctx.layers {
|
||||
for (index, (ch, colors)) in layer.string.chars().zip(layer.colors).enumerate() {
|
||||
if ch != ' ' && ch != '\u{2800}' {
|
||||
let (x, y) = (
|
||||
(index % width) as u16 + canvas_area.left(),
|
||||
(index / width) as u16 + canvas_area.top(),
|
||||
);
|
||||
let cell = buf[(x, y)].set_char(ch);
|
||||
if colors.0 != Color::Reset {
|
||||
cell.set_fg(colors.0);
|
||||
}
|
||||
if colors.1 != Color::Reset {
|
||||
cell.set_bg(colors.1);
|
||||
}
|
||||
for (index, layer_cell) in layer.contents.iter().enumerate() {
|
||||
let (x, y) = (
|
||||
(index % width) as u16 + canvas_area.left(),
|
||||
(index / width) as u16 + canvas_area.top(),
|
||||
);
|
||||
let cell = &mut buf[(x, y)];
|
||||
|
||||
if let Some(symbol) = layer_cell.symbol {
|
||||
cell.set_char(symbol);
|
||||
}
|
||||
if let Some(fg) = layer_cell.fg {
|
||||
cell.set_fg(fg);
|
||||
}
|
||||
if let Some(bg) = layer_cell.bg {
|
||||
cell.set_bg(bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -847,12 +936,76 @@ where
|
||||
mod tests {
|
||||
use indoc::indoc;
|
||||
use ratatui_core::buffer::Cell;
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
|
||||
// helper to test the canvas checks that drawing a vertical and horizontal line
|
||||
// results in the expected output
|
||||
fn test_marker(marker: Marker, expected: &str) {
|
||||
#[rstest]
|
||||
#[case::block(Marker::Block, indoc!(
|
||||
"
|
||||
█xxxx
|
||||
█xxxx
|
||||
█xxxx
|
||||
█xxxx
|
||||
█████"
|
||||
))]
|
||||
#[case::half_block(Marker::HalfBlock, indoc!(
|
||||
"
|
||||
█xxxx
|
||||
█xxxx
|
||||
█xxxx
|
||||
█xxxx
|
||||
█▄▄▄▄"
|
||||
))]
|
||||
#[case::bar(Marker::Bar, indoc!(
|
||||
"
|
||||
▄xxxx
|
||||
▄xxxx
|
||||
▄xxxx
|
||||
▄xxxx
|
||||
▄▄▄▄▄"
|
||||
))]
|
||||
#[case::braille(Marker::Braille, indoc!(
|
||||
"
|
||||
⡇xxxx
|
||||
⡇xxxx
|
||||
⡇xxxx
|
||||
⡇xxxx
|
||||
⣇⣀⣀⣀⣀"
|
||||
))]
|
||||
#[case::quadrant(Marker::Quadrant, indoc!(
|
||||
"
|
||||
▌xxxx
|
||||
▌xxxx
|
||||
▌xxxx
|
||||
▌xxxx
|
||||
▙▄▄▄▄"
|
||||
))]
|
||||
#[case::sextant(Marker::Sextant, indoc!(
|
||||
"
|
||||
▌xxxx
|
||||
▌xxxx
|
||||
▌xxxx
|
||||
▌xxxx
|
||||
🬲🬭🬭🬭🬭"
|
||||
))]
|
||||
#[case::octant(Marker::Octant, indoc!(
|
||||
"
|
||||
▌xxxx
|
||||
▌xxxx
|
||||
▌xxxx
|
||||
▌xxxx
|
||||
▂▂▂▂"
|
||||
))]
|
||||
#[case::dot(Marker::Dot, indoc!(
|
||||
"
|
||||
•xxxx
|
||||
•xxxx
|
||||
•xxxx
|
||||
•xxxx
|
||||
•••••"
|
||||
))]
|
||||
fn test_horizontal_with_vertical(#[case] marker: Marker, #[case] expected: &'static str) {
|
||||
let area = Rect::new(0, 0, 5, 5);
|
||||
let mut buf = Buffer::filled(area, Cell::new("x"));
|
||||
let horizontal_line = Line {
|
||||
@@ -881,71 +1034,104 @@ mod tests {
|
||||
assert_eq!(buf, Buffer::with_lines(expected.lines()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bar_marker() {
|
||||
test_marker(
|
||||
Marker::Bar,
|
||||
indoc!(
|
||||
#[rstest]
|
||||
#[case::block(Marker::Block, indoc!(
|
||||
"
|
||||
▄xxxx
|
||||
▄xxxx
|
||||
▄xxxx
|
||||
▄xxxx
|
||||
▄▄▄▄▄"
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_marker() {
|
||||
test_marker(
|
||||
Marker::Block,
|
||||
indoc!(
|
||||
█xxx█
|
||||
x█x█x
|
||||
xx█xx
|
||||
x█x█x
|
||||
█xxx█"))]
|
||||
#[case::half_block(Marker::HalfBlock,
|
||||
indoc!(
|
||||
"
|
||||
█xxxx
|
||||
█xxxx
|
||||
█xxxx
|
||||
█xxxx
|
||||
█████"
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_braille_marker() {
|
||||
test_marker(
|
||||
Marker::Braille,
|
||||
indoc!(
|
||||
█xxx█
|
||||
x█x█x
|
||||
xx█xx
|
||||
x█x█x
|
||||
█xxx█")
|
||||
)]
|
||||
#[case::bar(Marker::Bar, indoc!(
|
||||
"
|
||||
⡇xxxx
|
||||
⡇xxxx
|
||||
⡇xxxx
|
||||
⡇xxxx
|
||||
⣇⣀⣀⣀⣀"
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dot_marker() {
|
||||
test_marker(
|
||||
Marker::Dot,
|
||||
indoc!(
|
||||
▄xxx▄
|
||||
x▄x▄x
|
||||
xx▄xx
|
||||
x▄x▄x
|
||||
▄xxx▄"))]
|
||||
#[case::braille(Marker::Braille, indoc!(
|
||||
"
|
||||
•xxxx
|
||||
•xxxx
|
||||
•xxxx
|
||||
•xxxx
|
||||
•••••"
|
||||
),
|
||||
);
|
||||
⢣xxx⡜
|
||||
x⢣x⡜x
|
||||
xx⣿xx
|
||||
x⡜x⢣x
|
||||
⡜xxx⢣"
|
||||
))]
|
||||
#[case::quadrant(Marker::Quadrant, indoc!(
|
||||
"
|
||||
▚xxx▞
|
||||
x▚x▞x
|
||||
xx█xx
|
||||
x▞x▚x
|
||||
▞xxx▚"
|
||||
))]
|
||||
#[case::sextant(Marker::Sextant, indoc!(
|
||||
"
|
||||
🬧xxx🬔
|
||||
x🬧x🬔x
|
||||
xx█xx
|
||||
x🬘x🬣x
|
||||
🬘xxx🬣"
|
||||
))]
|
||||
#[case::octant(Marker::Octant, indoc!(
|
||||
"
|
||||
▚xxx▞
|
||||
x▚x▞x
|
||||
xx█xx
|
||||
x▞x▚x
|
||||
▞xxx▚"
|
||||
))]
|
||||
#[case::dot(Marker::Dot, indoc!(
|
||||
"
|
||||
•xxx•
|
||||
x•x•x
|
||||
xx•xx
|
||||
x•x•x
|
||||
•xxx•"
|
||||
))]
|
||||
fn test_diagonal_lines(#[case] marker: Marker, #[case] expected: &'static str) {
|
||||
let area = Rect::new(0, 0, 5, 5);
|
||||
let mut buf = Buffer::filled(area, Cell::new("x"));
|
||||
let diagonal_up = Line {
|
||||
x1: 0.0,
|
||||
y1: 0.0,
|
||||
x2: 10.0,
|
||||
y2: 10.0,
|
||||
color: Color::Reset,
|
||||
};
|
||||
let diagonal_down = Line {
|
||||
x1: 0.0,
|
||||
y1: 10.0,
|
||||
x2: 10.0,
|
||||
y2: 0.0,
|
||||
color: Color::Reset,
|
||||
};
|
||||
Canvas::default()
|
||||
.marker(marker)
|
||||
.paint(|ctx| {
|
||||
ctx.draw(&diagonal_down);
|
||||
ctx.draw(&diagonal_up);
|
||||
})
|
||||
.x_bounds([0.0, 10.0])
|
||||
.y_bounds([0.0, 10.0])
|
||||
.render(area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(expected.lines()));
|
||||
}
|
||||
|
||||
// The canvas methods work a lot with arithmetic so here we enter various width and height
|
||||
// values to check if there are any integer overflows we just initialize the canvas painters
|
||||
#[test]
|
||||
fn check_canvas_paint_max() {
|
||||
let mut b_grid = BrailleGrid::new(u16::MAX, 2);
|
||||
let mut b_grid = PatternGrid::<2, 4>::new(u16::MAX, 2, &OCTANTS);
|
||||
let mut c_grid = CharGrid::new(u16::MAX, 2, 'd');
|
||||
|
||||
let max = u16::MAX as usize;
|
||||
@@ -964,7 +1150,7 @@ mod tests {
|
||||
// We delibately cause integer overflow to check if we don't panic and don't get weird behavior
|
||||
#[test]
|
||||
fn check_canvas_paint_overflow() {
|
||||
let mut b_grid = BrailleGrid::new(u16::MAX, 3);
|
||||
let mut b_grid = PatternGrid::<2, 4>::new(u16::MAX, 3, &BRAILLE);
|
||||
let mut c_grid = CharGrid::new(u16::MAX, 3, 'd');
|
||||
|
||||
let max = u16::MAX as usize + 10;
|
||||
|
||||
@@ -115,7 +115,7 @@ mod tests {
|
||||
"█ █",
|
||||
"██████████",
|
||||
]);
|
||||
expected.set_style(buffer.area, Style::new().red());
|
||||
expected.set_style(buffer.area, Style::new().red().on_red());
|
||||
expected.set_style(buffer.area.inner(Margin::new(1, 1)), Style::reset());
|
||||
assert_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
@@ -1017,16 +1017,18 @@ impl Widget for &Chart<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
for dataset in &self.datasets {
|
||||
Canvas::default()
|
||||
.background_color(self.style.bg.unwrap_or(Color::Reset))
|
||||
.x_bounds(self.x_axis.bounds)
|
||||
.y_bounds(self.y_axis.bounds)
|
||||
.marker(dataset.marker)
|
||||
.paint(|ctx| {
|
||||
Canvas::default()
|
||||
.background_color(self.style.bg.unwrap_or(Color::Reset))
|
||||
.x_bounds(self.x_axis.bounds)
|
||||
.y_bounds(self.y_axis.bounds)
|
||||
.paint(|ctx| {
|
||||
for dataset in &self.datasets {
|
||||
ctx.marker(dataset.marker);
|
||||
|
||||
let color = dataset.style.fg.unwrap_or(Color::Reset);
|
||||
ctx.draw(&Points {
|
||||
coords: dataset.data,
|
||||
color: dataset.style.fg.unwrap_or(Color::Reset),
|
||||
color,
|
||||
});
|
||||
match dataset.graph_type {
|
||||
GraphType::Line => {
|
||||
@@ -1036,7 +1038,7 @@ impl Widget for &Chart<'_> {
|
||||
y1: data[0].1,
|
||||
x2: data[1].0,
|
||||
y2: data[1].1,
|
||||
color: dataset.style.fg.unwrap_or(Color::Reset),
|
||||
color,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1047,15 +1049,15 @@ impl Widget for &Chart<'_> {
|
||||
y1: 0.0,
|
||||
x2: *x,
|
||||
y2: *y,
|
||||
color: dataset.style.fg.unwrap_or(Color::Reset),
|
||||
color,
|
||||
});
|
||||
}
|
||||
}
|
||||
GraphType::Scatter => {}
|
||||
}
|
||||
})
|
||||
.render(graph_area, buf);
|
||||
}
|
||||
}
|
||||
})
|
||||
.render(graph_area, buf);
|
||||
|
||||
if let Some(Position { x, y }) = layout.title_x {
|
||||
let title = self.x_axis.title.as_ref().unwrap();
|
||||
@@ -1548,6 +1550,62 @@ mod tests {
|
||||
assert_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case::dot(symbols::Marker::Dot, '•')]
|
||||
#[case::dot(symbols::Marker::Braille, '⢣')]
|
||||
fn overlapping_lines(#[case] marker: symbols::Marker, #[case] symbol: char) {
|
||||
let data_diagonal_up = [(0.0, 0.0), (5.0, 5.0)];
|
||||
let data_diagonal_down = [(0.0, 5.0), (5.0, 0.0)];
|
||||
let lines = vec![
|
||||
Dataset::default()
|
||||
.data(&data_diagonal_up)
|
||||
.marker(symbols::Marker::Block)
|
||||
.graph_type(GraphType::Line)
|
||||
.blue(),
|
||||
Dataset::default()
|
||||
.data(&data_diagonal_down)
|
||||
.marker(marker)
|
||||
.graph_type(GraphType::Line)
|
||||
.red(),
|
||||
];
|
||||
let chart = Chart::new(lines)
|
||||
.x_axis(Axis::default().bounds([0.0, 5.0]))
|
||||
.y_axis(Axis::default().bounds([0.0, 5.0]));
|
||||
let area = Rect::new(0, 0, 5, 5);
|
||||
let mut buffer = Buffer::empty(area);
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
#[rustfmt::skip]
|
||||
let mut expected = Buffer::with_lines([
|
||||
format!("{symbol} █"),
|
||||
format!(" {symbol} █ "),
|
||||
format!(" {symbol} "),
|
||||
format!(" █ {symbol} "),
|
||||
format!("█ {symbol}"),
|
||||
]);
|
||||
for i in 0..5 {
|
||||
// The Marker::Dot and Marker::Braille tiles have the
|
||||
// foreground set to Red.
|
||||
expected.set_style(Rect::new(i, i, 1, 1), Style::new().fg(Color::Red));
|
||||
// The Marker::Block tiles have both the foreground and
|
||||
// background set to Blue.
|
||||
expected.set_style(
|
||||
Rect::new(i, 4 - i, 1, 1),
|
||||
Style::new().fg(Color::Blue).bg(Color::Blue),
|
||||
);
|
||||
}
|
||||
// Where the Marker::Dot/Braille overlaps with Marker::Block,
|
||||
// the background is set to blue from the Block, but the
|
||||
// foreground is set to red from the Dot/Braille. This allows
|
||||
// two line plots to overlap, so long as one of them is a
|
||||
// Block.
|
||||
expected.set_style(
|
||||
Rect::new(2, 2, 1, 1),
|
||||
Style::new().fg(Color::Red).bg(Color::Blue),
|
||||
);
|
||||
|
||||
assert_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_in_minimal_buffer() {
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 1));
|
||||
|
||||
@@ -3,6 +3,7 @@ pub mod main {
|
||||
pub mod block;
|
||||
pub mod buffer;
|
||||
pub mod constraints;
|
||||
pub mod gauge;
|
||||
pub mod line;
|
||||
pub mod list;
|
||||
pub mod paragraph;
|
||||
@@ -25,4 +26,5 @@ criterion::criterion_main!(
|
||||
table::benches,
|
||||
text::benches,
|
||||
constraints::benches,
|
||||
gauge::benches,
|
||||
);
|
||||
|
||||
@@ -52,7 +52,7 @@ fn barchart(c: &mut Criterion) {
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Render the widget in a classical size buffer
|
||||
/// Render the widget in a classical size buffer.
|
||||
fn render(bencher: &mut Bencher, barchart: &BarChart) {
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 200, 50));
|
||||
// We use `iter_batched` to clone the value in the setup function.
|
||||
|
||||
@@ -36,7 +36,7 @@ fn block(c: &mut Criterion) {
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// render the block into a buffer of the given `size`
|
||||
/// Render the block into a buffer of the given `size`.
|
||||
fn render(bencher: &mut Bencher, block: &Block, size: Rect) {
|
||||
let mut buffer = Buffer::empty(size);
|
||||
// We use `iter_batched` to clone the value in the setup function.
|
||||
|
||||
58
ratatui/benches/main/gauge.rs
Normal file
58
ratatui/benches/main/gauge.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use criterion::{BatchSize, Criterion, criterion_group};
|
||||
use ratatui::buffer::Buffer;
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::style::Style;
|
||||
use ratatui::widgets::{Block, Gauge, Widget};
|
||||
|
||||
/// Benchmark for rendering a gauge.
|
||||
fn gauge(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("gauge");
|
||||
|
||||
let (width, height) = (200, 50); // 1080p fullscreen with medium font
|
||||
let buffer_size = Rect::new(0, 0, width, height);
|
||||
|
||||
// Render an empty gauge
|
||||
group.bench_with_input(
|
||||
format!("render_empty/{width}x{height}"),
|
||||
&Gauge::default(),
|
||||
|b, gauge| {
|
||||
let mut buffer = Buffer::empty(buffer_size);
|
||||
// We use `iter_batched` to clone the value in the setup function because
|
||||
// `Widget::render` consumes the widget.
|
||||
b.iter_batched(
|
||||
|| gauge.to_owned(),
|
||||
|bench_gauge| {
|
||||
bench_gauge.render(buffer.area, &mut buffer);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// Render with all features
|
||||
group.bench_with_input(
|
||||
format!("render_all_feature/{width}x{height}"),
|
||||
&Gauge::default()
|
||||
.block(Block::bordered().title("Progress"))
|
||||
.gauge_style(Style::new().white().on_black().italic())
|
||||
.percent(20)
|
||||
.label("20%")
|
||||
.use_unicode(true),
|
||||
|b, gauge| {
|
||||
let mut buffer = Buffer::empty(buffer_size);
|
||||
// We use `iter_batched` to clone the value in the setup function because
|
||||
// `Widget::render` consumes the widget.
|
||||
b.iter_batched(
|
||||
|| gauge.to_owned(),
|
||||
|bench_gauge| {
|
||||
bench_gauge.render(buffer.area, &mut buffer);
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(benches, gauge);
|
||||
@@ -39,7 +39,7 @@ fn list(c: &mut Criterion) {
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// render the list into a common size buffer
|
||||
/// Render the list into a common size buffer.
|
||||
fn render(bencher: &mut Bencher, list: &List) {
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 200, 50));
|
||||
// We use `iter_batched` to clone the value in the setup function.
|
||||
|
||||
@@ -67,7 +67,7 @@ fn paragraph(c: &mut Criterion) {
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// render the paragraph into a buffer with the given width
|
||||
/// Render the paragraph into a buffer with the given width.
|
||||
fn render(bencher: &mut Bencher, paragraph: &Paragraph, width: u16) {
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, width, PARAGRAPH_DEFAULT_HEIGHT));
|
||||
// We use `iter_batched` to clone the value in the setup function.
|
||||
|
||||
@@ -25,7 +25,7 @@ fn sparkline(c: &mut Criterion) {
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// render the block into a buffer of the given `size`
|
||||
/// Render the block into a buffer of the given `size`.
|
||||
fn render(bencher: &mut Bencher, sparkline: &Sparkline) {
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 200, 50));
|
||||
// We use `iter_batched` to clone the value in the setup function.
|
||||
|
||||
@@ -46,7 +46,7 @@ fn text(c: &mut Criterion) {
|
||||
group.finish();
|
||||
}
|
||||
|
||||
/// Renders the text into a buffer of the given `size`
|
||||
/// Render the text into a buffer of the given `size`.
|
||||
fn render(bencher: &mut Bencher, text: &Text, size: Rect) {
|
||||
let mut buffer = Buffer::empty(size);
|
||||
// We use `iter_batched` to clone the value in the setup function.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
[default.extend-words]
|
||||
ratatui = "ratatui"
|
||||
sectore = "sectore" # https://github.com/sectore
|
||||
|
||||
[type.md]
|
||||
extend-ignore-re = [
|
||||
|
||||
Reference in New Issue
Block a user