mirror of
https://github.com/ratatui/ratatui.git
synced 2025-09-30 06:21:31 +00:00
chore(core): move core types to ratatui-core (#1460)
The buffer, layout, style, symbols, text, and the top level of widgets modules are moved to ratatui-core. This is the first step in modularizing the library so that the core types can be used in other projects without the need for the backend / widgets types. This helps reduce the need for updating other crates as often due to semver changes outside of the core types. --------- Co-authored-by: Orhun Parmaksız <orhun@archlinux.org> Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
This commit is contained in:
parent
0a47ebd94b
commit
98df774d7f
@ -10,6 +10,8 @@ GitHub with a [breaking change] label.
|
|||||||
|
|
||||||
This is a quick summary of the sections below:
|
This is a quick summary of the sections below:
|
||||||
|
|
||||||
|
- [Unreleased](#unreleased)
|
||||||
|
- The `From` impls for backend types are now replaced with more specific traits
|
||||||
- [v0.29.0](#v0290)
|
- [v0.29.0](#v0290)
|
||||||
- `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const
|
- `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const
|
||||||
- Removed public fields from `Rect` iterators
|
- Removed public fields from `Rect` iterators
|
||||||
@ -104,7 +106,7 @@ let crossterm_attribute = crossterm::style::types::Attribute::Bold;
|
|||||||
+ let crossterm_attribute = ratatui_modifier.into_crossterm();
|
+ let crossterm_attribute = ratatui_modifier.into_crossterm();
|
||||||
```
|
```
|
||||||
|
|
||||||
Similar conversions for `ContentStyle` -> `Style` and `Attributes` -> `Modifier` exist for
|
Similar conversions for `ContentStyle` -> `Style` and `Attributes` -> `Modifier` exist for
|
||||||
Crossterm and the various Termion and Termwiz types as well.
|
Crossterm and the various Termion and Termwiz types as well.
|
||||||
|
|
||||||
## [v0.29.0](https://github.com/ratatui/ratatui/releases/tag/v0.29.0)
|
## [v0.29.0](https://github.com/ratatui/ratatui/releases/tag/v0.29.0)
|
||||||
|
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -2090,9 +2090,7 @@ version = "0.29.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"argh",
|
"argh",
|
||||||
"bitflags 2.6.0",
|
"bitflags 2.6.0",
|
||||||
"cassowary",
|
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"compact_str",
|
|
||||||
"criterion",
|
"criterion",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"document-features",
|
"document-features",
|
||||||
@ -2102,13 +2100,12 @@ dependencies = [
|
|||||||
"indoc",
|
"indoc",
|
||||||
"instability",
|
"instability",
|
||||||
"itertools 0.13.0",
|
"itertools 0.13.0",
|
||||||
"lru",
|
|
||||||
"octocrab",
|
"octocrab",
|
||||||
"palette",
|
"palette",
|
||||||
"paste",
|
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rand_chacha 0.3.1",
|
"rand_chacha 0.3.1",
|
||||||
|
"ratatui-core",
|
||||||
"rstest",
|
"rstest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -2121,6 +2118,28 @@ dependencies = [
|
|||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
|
"unicode-width 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ratatui-core"
|
||||||
|
version = "0.3.0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.6.0",
|
||||||
|
"cassowary",
|
||||||
|
"compact_str",
|
||||||
|
"indoc",
|
||||||
|
"instability",
|
||||||
|
"itertools 0.13.0",
|
||||||
|
"lru",
|
||||||
|
"palette",
|
||||||
|
"paste",
|
||||||
|
"pretty_assertions",
|
||||||
|
"rstest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"strum",
|
||||||
|
"unicode-segmentation",
|
||||||
"unicode-truncate",
|
"unicode-truncate",
|
||||||
"unicode-width 0.2.0",
|
"unicode-width 0.2.0",
|
||||||
]
|
]
|
||||||
@ -2454,18 +2473,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.213"
|
version = "1.0.214"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.213"
|
version = "1.0.214"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
18
Cargo.toml
18
Cargo.toml
@ -1,7 +1,7 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = ["ratatui", "xtask"]
|
members = ["ratatui", "ratatui-core", "xtask"]
|
||||||
default-members = ["ratatui"]
|
default-members = ["ratatui", "ratatui-core"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
authors = ["Florian Dehau <work@fdehau.com>", "The Ratatui Developers"]
|
authors = ["Florian Dehau <work@fdehau.com>", "The Ratatui Developers"]
|
||||||
@ -23,6 +23,20 @@ exclude = [
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.74.0"
|
rust-version = "1.74.0"
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
bitflags = "2.6.0"
|
||||||
|
indoc = "2.0.5"
|
||||||
|
instability = "0.3.1"
|
||||||
|
itertools = "0.13.0"
|
||||||
|
pretty_assertions = "1.4.1"
|
||||||
|
ratatui-core = { path = "ratatui-core" }
|
||||||
|
rstest = "0.23.0"
|
||||||
|
serde = { version = "1.0.214", features = ["derive"] }
|
||||||
|
strum = { version = "0.26.3", features = ["derive"] }
|
||||||
|
unicode-segmentation = "1.12.0"
|
||||||
|
# See <https://github.com/ratatui/ratatui/issues/1271> for information about why we pin unicode-width
|
||||||
|
unicode-width = "=0.2.0"
|
||||||
|
|
||||||
# Improve benchmark consistency
|
# Improve benchmark consistency
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
70
ratatui-core/Cargo.toml
Normal file
70
ratatui-core/Cargo.toml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
[package]
|
||||||
|
name = "ratatui-core"
|
||||||
|
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.3.0"
|
||||||
|
authors.workspace = true
|
||||||
|
documentation.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
keywords.workspace = true
|
||||||
|
categories.workspace = true
|
||||||
|
readme.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
exclude.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
## enables conversions from colors in the [`palette`] crate to [`Color`](crate::style::Color).
|
||||||
|
palette = ["dep:palette"]
|
||||||
|
|
||||||
|
## enables the backend code that sets the underline color. Underline color is only supported by
|
||||||
|
## the Crossterm backend, and is not supported on Windows 7.
|
||||||
|
underline-color = []
|
||||||
|
|
||||||
|
## enables serialization and deserialization of style and color types using the [`serde`] crate.
|
||||||
|
## This is useful if you want to save themes to a file.
|
||||||
|
serde = ["dep:serde", "bitflags/serde", "compact_str/serde"]
|
||||||
|
|
||||||
|
## enable all unstable features.
|
||||||
|
unstable = ["unstable-widget-ref"]
|
||||||
|
|
||||||
|
## enables the [`WidgetRef`] and [`StatefulWidgetRef`] traits which are experimental and may change
|
||||||
|
## in the future.
|
||||||
|
##
|
||||||
|
## [`WidgetRef`]: widgets::WidgetRef
|
||||||
|
## [`StatefulWidgetRef`]: widgets::StatefulWidgetRef
|
||||||
|
unstable-widget-ref = []
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitflags = "2.3"
|
||||||
|
cassowary = "0.3"
|
||||||
|
compact_str = "0.8.0"
|
||||||
|
instability.workspace = true
|
||||||
|
indoc.workspace = true
|
||||||
|
itertools.workspace = true
|
||||||
|
lru = "0.12.0"
|
||||||
|
palette = { version = "0.7.6", optional = true }
|
||||||
|
paste = "1.0.2"
|
||||||
|
serde = { workspace = true, optional = true }
|
||||||
|
strum.workspace = true
|
||||||
|
unicode-segmentation.workspace = true
|
||||||
|
unicode-truncate = "1"
|
||||||
|
unicode-width.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rstest.workspace = true
|
||||||
|
pretty_assertions.workspace = true
|
||||||
|
serde_json = "1.0.132"
|
||||||
|
|
||||||
|
[lints.clippy]
|
||||||
|
# we often split up a module into multiple files with the main type in a file named after the
|
||||||
|
# module, so we want to allow this pattern
|
||||||
|
module_inception = "allow"
|
34
ratatui-core/README.md
Normal file
34
ratatui-core/README.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# ratatui-core
|
||||||
|
|
||||||
|
[](https://crates.io/crates/ratatui-core)
|
||||||
|
[](https://docs.rs/ratatui-core)
|
||||||
|
[](../LICENSE)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
**ratatui-core** is the core library of the [ratatui](https://github.com/ratatui/ratatui) project,
|
||||||
|
providing the essential building blocks for creating rich terminal user interfaces in Rust.
|
||||||
|
|
||||||
|
### Why ratatui-core?
|
||||||
|
|
||||||
|
The `ratatui-core` crate is split from the main [`ratatui`](https://crates.io/crates/ratatui) crate
|
||||||
|
to offer better stability for widget library authors. Widget libraries should generally depend on
|
||||||
|
`ratatui-core`, benefiting from a stable API and reducing the need for frequent updates.
|
||||||
|
Applications, on the other hand, should depend on the main `ratatui` crate, which includes built-in
|
||||||
|
widgets and additional features.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add `ratatui-core` to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo add ratatui-core
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We welcome contributions from the community! Please see our [CONTRIBUTING](../CONTRIBUTING.md) guide for more details on how to get involved.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is licensed under the MIT License. See the [LICENSE](../LICENSE) file for details.
|
@ -23,7 +23,7 @@ use crate::{
|
|||||||
/// # Examples:
|
/// # Examples:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// buffer::{Buffer, Cell},
|
/// buffer::{Buffer, Cell},
|
||||||
/// layout::{Position, Rect},
|
/// layout::{Position, Rect},
|
||||||
/// style::{Color, Style},
|
/// style::{Color, Style},
|
||||||
@ -168,7 +168,7 @@ impl Buffer {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// buffer::{Buffer, Cell},
|
/// buffer::{Buffer, Cell},
|
||||||
/// layout::{Position, Rect},
|
/// layout::{Position, Rect},
|
||||||
/// };
|
/// };
|
||||||
@ -199,7 +199,7 @@ impl Buffer {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// buffer::{Buffer, Cell},
|
/// buffer::{Buffer, Cell},
|
||||||
/// layout::{Position, Rect},
|
/// layout::{Position, Rect},
|
||||||
/// style::{Color, Style},
|
/// style::{Color, Style},
|
||||||
@ -227,7 +227,7 @@ impl Buffer {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{buffer::Buffer, layout::Rect};
|
/// use ratatui_core::{buffer::Buffer, layout::Rect};
|
||||||
///
|
///
|
||||||
/// let buffer = Buffer::empty(Rect::new(200, 100, 10, 10));
|
/// let buffer = Buffer::empty(Rect::new(200, 100, 10, 10));
|
||||||
/// // Global coordinates to the top corner of this buffer's area
|
/// // Global coordinates to the top corner of this buffer's area
|
||||||
@ -239,7 +239,7 @@ impl Buffer {
|
|||||||
/// Panics when given an coordinate that is outside of this Buffer's area.
|
/// Panics when given an coordinate that is outside of this Buffer's area.
|
||||||
///
|
///
|
||||||
/// ```should_panic
|
/// ```should_panic
|
||||||
/// use ratatui::{buffer::Buffer, layout::Rect};
|
/// use ratatui_core::{buffer::Buffer, layout::Rect};
|
||||||
///
|
///
|
||||||
/// let buffer = Buffer::empty(Rect::new(200, 100, 10, 10));
|
/// let buffer = Buffer::empty(Rect::new(200, 100, 10, 10));
|
||||||
/// // Top coordinate is outside of the buffer in global coordinate space, as the Buffer's area
|
/// // Top coordinate is outside of the buffer in global coordinate space, as the Buffer's area
|
||||||
@ -282,7 +282,7 @@ impl Buffer {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{buffer::Buffer, layout::Rect};
|
/// use ratatui_core::{buffer::Buffer, layout::Rect};
|
||||||
///
|
///
|
||||||
/// let rect = Rect::new(200, 100, 10, 10);
|
/// let rect = Rect::new(200, 100, 10, 10);
|
||||||
/// let buffer = Buffer::empty(rect);
|
/// let buffer = Buffer::empty(rect);
|
||||||
@ -295,7 +295,7 @@ impl Buffer {
|
|||||||
/// Panics when given an index that is outside the Buffer's content.
|
/// Panics when given an index that is outside the Buffer's content.
|
||||||
///
|
///
|
||||||
/// ```should_panic
|
/// ```should_panic
|
||||||
/// use ratatui::{buffer::Buffer, layout::Rect};
|
/// use ratatui_core::{buffer::Buffer, layout::Rect};
|
||||||
///
|
///
|
||||||
/// let rect = Rect::new(0, 0, 10, 10); // 100 cells in total
|
/// let rect = Rect::new(0, 0, 10, 10); // 100 cells in total
|
||||||
/// let buffer = Buffer::empty(rect);
|
/// let buffer = Buffer::empty(rect);
|
||||||
@ -526,7 +526,7 @@ impl<P: Into<Position>> Index<P> for Buffer {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// buffer::{Buffer, Cell},
|
/// buffer::{Buffer, Cell},
|
||||||
/// layout::{Position, Rect},
|
/// layout::{Position, Rect},
|
||||||
/// };
|
/// };
|
||||||
@ -556,7 +556,7 @@ impl<P: Into<Position>> IndexMut<P> for Buffer {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// buffer::{Buffer, Cell},
|
/// buffer::{Buffer, Cell},
|
||||||
/// layout::{Position, Rect},
|
/// layout::{Position, Rect},
|
||||||
/// };
|
/// };
|
@ -26,7 +26,7 @@ use strum::EnumIs;
|
|||||||
/// `Constraint` provides helper methods to create lists of constraints from various input formats.
|
/// `Constraint` provides helper methods to create lists of constraints from various input formats.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::Constraint;
|
/// use ratatui_core::layout::Constraint;
|
||||||
///
|
///
|
||||||
/// // Create a layout with specified lengths for each element
|
/// // Create a layout with specified lengths for each element
|
||||||
/// let constraints = Constraint::from_lengths([10, 20, 10]);
|
/// let constraints = Constraint::from_lengths([10, 20, 10]);
|
||||||
@ -224,7 +224,7 @@ impl Constraint {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// # let area = Rect::default();
|
/// # let area = Rect::default();
|
||||||
/// let constraints = Constraint::from_lengths([1, 2, 3]);
|
/// let constraints = Constraint::from_lengths([1, 2, 3]);
|
||||||
@ -242,7 +242,7 @@ impl Constraint {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// # let area = Rect::default();
|
/// # let area = Rect::default();
|
||||||
/// let constraints = Constraint::from_ratios([(1, 4), (1, 2), (1, 4)]);
|
/// let constraints = Constraint::from_ratios([(1, 4), (1, 2), (1, 4)]);
|
||||||
@ -260,7 +260,7 @@ impl Constraint {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// # let area = Rect::default();
|
/// # let area = Rect::default();
|
||||||
/// let constraints = Constraint::from_percentages([25, 50, 25]);
|
/// let constraints = Constraint::from_percentages([25, 50, 25]);
|
||||||
@ -278,7 +278,7 @@ impl Constraint {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// # let area = Rect::default();
|
/// # let area = Rect::default();
|
||||||
/// let constraints = Constraint::from_maxes([1, 2, 3]);
|
/// let constraints = Constraint::from_maxes([1, 2, 3]);
|
||||||
@ -296,7 +296,7 @@ impl Constraint {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// # let area = Rect::default();
|
/// # let area = Rect::default();
|
||||||
/// let constraints = Constraint::from_mins([1, 2, 3]);
|
/// let constraints = Constraint::from_mins([1, 2, 3]);
|
||||||
@ -314,7 +314,7 @@ impl Constraint {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// # let area = Rect::default();
|
/// # let area = Rect::default();
|
||||||
/// let constraints = Constraint::from_fills([1, 2, 3]);
|
/// let constraints = Constraint::from_fills([1, 2, 3]);
|
||||||
@ -337,7 +337,7 @@ impl From<u16> for Constraint {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// # let area = Rect::default();
|
/// # let area = Rect::default();
|
||||||
/// let layout = Layout::new(Direction::Vertical, [1, 2, 3]).split(area);
|
/// let layout = Layout::new(Direction::Vertical, [1, 2, 3]).split(area);
|
@ -148,20 +148,18 @@ impl From<i16> for Spacing {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
|
/// buffer::Buffer,
|
||||||
/// layout::{Constraint, Direction, Layout, Rect},
|
/// layout::{Constraint, Direction, Layout, Rect},
|
||||||
/// widgets::Paragraph,
|
/// text::Text,
|
||||||
/// Frame,
|
/// widgets::Widget,
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// fn render(frame: &mut Frame, area: Rect) {
|
/// fn render(area: Rect, buf: &mut ratatui_core::buffer::Buffer) {
|
||||||
/// let layout = Layout::new(
|
/// let layout = Layout::vertical([Constraint::Length(5), Constraint::Min(0)]);
|
||||||
/// Direction::Vertical,
|
/// let [left, right] = layout.areas(area);
|
||||||
/// [Constraint::Length(5), Constraint::Min(0)],
|
/// Text::from("foo").render(left, buf);
|
||||||
/// )
|
/// Text::from("bar").render(right, buf);
|
||||||
/// .split(Rect::new(0, 0, 10, 10));
|
|
||||||
/// frame.render_widget(Paragraph::new("foo"), layout[0]);
|
|
||||||
/// frame.render_widget(Paragraph::new("bar"), layout[1]);
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@ -206,7 +204,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Direction, Layout};
|
/// use ratatui_core::layout::{Constraint, Direction, Layout};
|
||||||
///
|
///
|
||||||
/// Layout::new(
|
/// Layout::new(
|
||||||
/// Direction::Horizontal,
|
/// Direction::Horizontal,
|
||||||
@ -240,7 +238,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout};
|
/// use ratatui_core::layout::{Constraint, Layout};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::vertical([Constraint::Length(5), Constraint::Min(0)]);
|
/// let layout = Layout::vertical([Constraint::Length(5), Constraint::Min(0)]);
|
||||||
/// ```
|
/// ```
|
||||||
@ -260,7 +258,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout};
|
/// use ratatui_core::layout::{Constraint, Layout};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::horizontal([Constraint::Length(5), Constraint::Min(0)]);
|
/// let layout = Layout::horizontal([Constraint::Length(5), Constraint::Min(0)]);
|
||||||
/// ```
|
/// ```
|
||||||
@ -289,7 +287,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::default()
|
/// let layout = Layout::default()
|
||||||
/// .direction(Direction::Horizontal)
|
/// .direction(Direction::Horizontal)
|
||||||
@ -324,7 +322,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::default()
|
/// let layout = Layout::default()
|
||||||
/// .constraints([
|
/// .constraints([
|
||||||
@ -369,7 +367,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::default()
|
/// let layout = Layout::default()
|
||||||
/// .constraints([Constraint::Min(0)])
|
/// .constraints([Constraint::Min(0)])
|
||||||
@ -391,7 +389,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::default()
|
/// let layout = Layout::default()
|
||||||
/// .constraints([Constraint::Min(0)])
|
/// .constraints([Constraint::Min(0)])
|
||||||
@ -410,7 +408,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::default()
|
/// let layout = Layout::default()
|
||||||
/// .constraints([Constraint::Min(0)])
|
/// .constraints([Constraint::Min(0)])
|
||||||
@ -442,7 +440,7 @@ impl Layout {
|
|||||||
/// In this example, the items in the layout will be aligned to the start.
|
/// In this example, the items in the layout will be aligned to the start.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint::*, Flex, Layout};
|
/// use ratatui_core::layout::{Constraint::*, Flex, Layout};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).flex(Flex::Start);
|
/// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).flex(Flex::Start);
|
||||||
/// ```
|
/// ```
|
||||||
@ -451,7 +449,7 @@ impl Layout {
|
|||||||
/// space.
|
/// space.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint::*, Flex, Layout};
|
/// use ratatui_core::layout::{Constraint::*, Flex, Layout};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).flex(Flex::Legacy);
|
/// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).flex(Flex::Legacy);
|
||||||
/// ```
|
/// ```
|
||||||
@ -479,7 +477,7 @@ impl Layout {
|
|||||||
/// In this example, the spacing between each item in the layout is set to 2 cells.
|
/// In this example, the spacing between each item in the layout is set to 2 cells.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint::*, Layout};
|
/// use ratatui_core::layout::{Constraint::*, Layout};
|
||||||
///
|
///
|
||||||
/// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).spacing(2);
|
/// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).spacing(2);
|
||||||
/// ```
|
/// ```
|
||||||
@ -488,7 +486,7 @@ impl Layout {
|
|||||||
/// three segments will have an overlapping border.
|
/// three segments will have an overlapping border.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Constraint::*, Layout};
|
/// use ratatui_core::layout::{Constraint::*, Layout};
|
||||||
/// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).spacing(-1);
|
/// let layout = Layout::horizontal([Length(20), Length(20), Length(20)]).spacing(-1);
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use = "method moves the value of self and returns the modified value"]
|
#[must_use = "method moves the value of self and returns the modified value"]
|
||||||
@ -515,16 +513,14 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{layout::{Layout, Constraint}, Frame};
|
/// use ratatui_core::layout::{Layout, Constraint, Rect};
|
||||||
///
|
///
|
||||||
/// # fn render(frame: &mut Frame) {
|
/// let area = Rect::new(0, 0, 10, 10);
|
||||||
/// let area = frame.area();
|
|
||||||
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
||||||
/// let [top, main] = layout.areas(area);
|
/// let [top, main] = layout.areas(area);
|
||||||
///
|
///
|
||||||
/// // or explicitly specify the number of constraints:
|
/// // or explicitly specify the number of constraints:
|
||||||
/// let areas = layout.areas::<2>(area);
|
/// let areas = layout.areas::<2>(area);
|
||||||
/// # }
|
|
||||||
pub fn areas<const N: usize>(&self, area: Rect) -> [Rect; N] {
|
pub fn areas<const N: usize>(&self, area: Rect) -> [Rect; N] {
|
||||||
let (areas, _) = self.split_with_spacers(area);
|
let (areas, _) = self.split_with_spacers(area);
|
||||||
areas.as_ref().try_into().expect("invalid number of rects")
|
areas.as_ref().try_into().expect("invalid number of rects")
|
||||||
@ -548,17 +544,16 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{layout::{Layout, Constraint}, Frame};
|
/// use ratatui_core::layout::{Constraint, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// # fn render(frame: &mut Frame) {
|
/// let area = Rect::new(0, 0, 10, 10);
|
||||||
/// let area = frame.area();
|
|
||||||
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
||||||
/// let [top, main] = layout.areas(area);
|
/// let [top, main] = layout.areas(area);
|
||||||
/// let [before, inbetween, after] = layout.spacers(area);
|
/// let [before, inbetween, after] = layout.spacers(area);
|
||||||
///
|
///
|
||||||
/// // or explicitly specify the number of constraints:
|
/// // or explicitly specify the number of constraints:
|
||||||
/// let spacers = layout.spacers::<2>(area);
|
/// let spacers = layout.spacers::<3>(area);
|
||||||
/// # }
|
/// ```
|
||||||
pub fn spacers<const N: usize>(&self, area: Rect) -> [Rect; N] {
|
pub fn spacers<const N: usize>(&self, area: Rect) -> [Rect; N] {
|
||||||
let (_, spacers) = self.split_with_spacers(area);
|
let (_, spacers) = self.split_with_spacers(area);
|
||||||
spacers
|
spacers
|
||||||
@ -588,7 +583,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
|
||||||
/// let layout = Layout::default()
|
/// let layout = Layout::default()
|
||||||
/// .direction(Direction::Vertical)
|
/// .direction(Direction::Vertical)
|
||||||
/// .constraints([Constraint::Length(5), Constraint::Min(0)])
|
/// .constraints([Constraint::Length(5), Constraint::Min(0)])
|
||||||
@ -620,7 +615,7 @@ impl Layout {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
/// use ratatui_core::layout::{Constraint, Direction, Layout, Rect};
|
||||||
///
|
///
|
||||||
/// let (areas, spacers) = Layout::default()
|
/// let (areas, spacers) = Layout::default()
|
||||||
/// .direction(Direction::Vertical)
|
/// .direction(Direction::Vertical)
|
||||||
@ -1413,8 +1408,12 @@ mod tests {
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::{Constraint, Constraint::*, Direction, Flex, Layout, Rect},
|
layout::{
|
||||||
widgets::{Paragraph, Widget},
|
Constraint::{self, *},
|
||||||
|
Direction, Flex, Layout, Rect,
|
||||||
|
},
|
||||||
|
text::Text,
|
||||||
|
widgets::Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Test that the given constraints applied to the given area result in the expected layout.
|
/// Test that the given constraints applied to the given area result in the expected layout.
|
||||||
@ -1436,7 +1435,7 @@ mod tests {
|
|||||||
let mut buffer = Buffer::empty(area);
|
let mut buffer = Buffer::empty(area);
|
||||||
for (c, &area) in ('a'..='z').take(constraints.len()).zip(layout.iter()) {
|
for (c, &area) in ('a'..='z').take(constraints.len()).zip(layout.iter()) {
|
||||||
let s = c.to_string().repeat(area.width as usize);
|
let s = c.to_string().repeat(area.width as usize);
|
||||||
Paragraph::new(s).render(area, &mut buffer);
|
Text::from(s).render(area, &mut buffer);
|
||||||
}
|
}
|
||||||
assert_eq!(buffer, Buffer::with_lines([expected]));
|
assert_eq!(buffer, Buffer::with_lines([expected]));
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ use crate::layout::Rect;
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::layout::{Position, Rect};
|
/// use ratatui_core::layout::{Position, Rect};
|
||||||
///
|
///
|
||||||
/// // the following are all equivalent
|
/// // the following are all equivalent
|
||||||
/// let position = Position { x: 1, y: 2 };
|
/// let position = Position { x: 1, y: 2 };
|
@ -64,7 +64,7 @@ impl Rect {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::layout::Rect;
|
/// use ratatui_core::layout::Rect;
|
||||||
///
|
///
|
||||||
/// let rect = Rect::new(1, 2, 3, 4);
|
/// let rect = Rect::new(1, 2, 3, 4);
|
||||||
/// ```
|
/// ```
|
||||||
@ -213,7 +213,7 @@ impl Rect {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::layout::{Position, Rect};
|
/// use ratatui_core::layout::{Position, Rect};
|
||||||
///
|
///
|
||||||
/// let rect = Rect::new(1, 2, 3, 4);
|
/// let rect = Rect::new(1, 2, 3, 4);
|
||||||
/// assert!(rect.contains(Position { x: 1, y: 2 }));
|
/// assert!(rect.contains(Position { x: 1, y: 2 }));
|
||||||
@ -243,12 +243,11 @@ impl Rect {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{layout::Rect, Frame};
|
/// use ratatui_core::layout::Rect;
|
||||||
///
|
///
|
||||||
/// # fn render(frame: &mut Frame) {
|
/// let area = Rect::new(0, 0, 100, 100);
|
||||||
/// let area = frame.area();
|
/// let rect = Rect::new(80, 80, 30, 30).clamp(area);
|
||||||
/// let rect = Rect::new(0, 0, 100, 100).clamp(area);
|
/// assert_eq!(rect, Rect::new(70, 70, 30, 30));
|
||||||
/// # }
|
|
||||||
/// ```
|
/// ```
|
||||||
#[must_use = "method returns the modified value"]
|
#[must_use = "method returns the modified value"]
|
||||||
pub fn clamp(self, other: Self) -> Self {
|
pub fn clamp(self, other: Self) -> Self {
|
||||||
@ -264,7 +263,7 @@ impl Rect {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{buffer::Buffer, layout::Rect, text::Line, widgets::Widget};
|
/// use ratatui_core::{buffer::Buffer, layout::Rect, text::Line, widgets::Widget};
|
||||||
///
|
///
|
||||||
/// fn render(area: Rect, buf: &mut Buffer) {
|
/// fn render(area: Rect, buf: &mut Buffer) {
|
||||||
/// for row in area.rows() {
|
/// for row in area.rows() {
|
||||||
@ -281,14 +280,11 @@ impl Rect {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{buffer::Buffer, layout::Rect, text::Text, widgets::Widget};
|
||||||
/// buffer::Buffer,
|
///
|
||||||
/// layout::Rect,
|
|
||||||
/// widgets::{Block, Borders, Widget},
|
|
||||||
/// };
|
|
||||||
/// fn render(area: Rect, buf: &mut Buffer) {
|
/// fn render(area: Rect, buf: &mut Buffer) {
|
||||||
/// if let Some(left) = area.columns().next() {
|
/// for (i, column) in area.columns().enumerate() {
|
||||||
/// Block::new().borders(Borders::LEFT).render(left, buf);
|
/// Text::from(format!("{}", i)).render(column, buf);
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
@ -303,7 +299,7 @@ impl Rect {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{buffer::Buffer, layout::Rect};
|
/// use ratatui_core::{buffer::Buffer, layout::Rect};
|
||||||
///
|
///
|
||||||
/// fn render(area: Rect, buf: &mut Buffer) {
|
/// fn render(area: Rect, buf: &mut Buffer) {
|
||||||
/// for position in area.positions() {
|
/// for position in area.positions() {
|
||||||
@ -320,7 +316,7 @@ impl Rect {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::layout::Rect;
|
/// use ratatui_core::layout::Rect;
|
||||||
///
|
///
|
||||||
/// let rect = Rect::new(1, 2, 3, 4);
|
/// let rect = Rect::new(1, 2, 3, 4);
|
||||||
/// let position = rect.as_position();
|
/// let position = rect.as_position();
|
7
ratatui-core/src/lib.rs
Normal file
7
ratatui-core/src/lib.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
pub mod buffer;
|
||||||
|
pub mod layout;
|
||||||
|
pub mod style;
|
||||||
|
pub mod symbols;
|
||||||
|
pub mod text;
|
||||||
|
pub mod widgets;
|
@ -13,7 +13,7 @@
|
|||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use ratatui::{
|
//! use ratatui_core::{
|
||||||
//! style::{Color, Modifier, Style},
|
//! style::{Color, Modifier, Style},
|
||||||
//! text::Span,
|
//! text::Span,
|
||||||
//! };
|
//! };
|
||||||
@ -43,10 +43,9 @@
|
|||||||
//! ## Example
|
//! ## Example
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use ratatui::{
|
//! use ratatui_core::{
|
||||||
//! style::{Color, Modifier, Style, Stylize},
|
//! style::{Color, Modifier, Style, Stylize},
|
||||||
//! text::Span,
|
//! text::{Span, Text},
|
||||||
//! widgets::Paragraph,
|
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(
|
//! assert_eq!(
|
||||||
@ -61,8 +60,8 @@
|
|||||||
//! );
|
//! );
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(
|
//! assert_eq!(
|
||||||
//! Paragraph::new("hello").red().on_blue().bold(),
|
//! Text::from("hello").red().on_blue().bold(),
|
||||||
//! Paragraph::new("hello").style(
|
//! Text::from("hello").style(
|
||||||
//! Style::default()
|
//! Style::default()
|
||||||
//! .fg(Color::Red)
|
//! .fg(Color::Red)
|
||||||
//! .bg(Color::Blue)
|
//! .bg(Color::Blue)
|
||||||
@ -97,7 +96,7 @@ bitflags! {
|
|||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::Modifier;
|
/// use ratatui_core::style::Modifier;
|
||||||
///
|
///
|
||||||
/// let m = Modifier::BOLD | Modifier::ITALIC;
|
/// let m = Modifier::BOLD | Modifier::ITALIC;
|
||||||
/// ```
|
/// ```
|
||||||
@ -133,7 +132,7 @@ impl fmt::Debug for Modifier {
|
|||||||
/// Style lets you control the main characteristics of the displayed elements.
|
/// Style lets you control the main characteristics of the displayed elements.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Color, Modifier, Style};
|
/// use ratatui_core::style::{Color, Modifier, Style};
|
||||||
///
|
///
|
||||||
/// Style::default()
|
/// Style::default()
|
||||||
/// .fg(Color::Black)
|
/// .fg(Color::Black)
|
||||||
@ -144,7 +143,7 @@ impl fmt::Debug for Modifier {
|
|||||||
/// Styles can also be created with a [shorthand notation](crate::style#using-style-shorthands).
|
/// Styles can also be created with a [shorthand notation](crate::style#using-style-shorthands).
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Style, Stylize};
|
/// use ratatui_core::style::{Style, Stylize};
|
||||||
///
|
///
|
||||||
/// Style::new().black().on_green().italic().bold();
|
/// Style::new().black().on_green().italic().bold();
|
||||||
/// ```
|
/// ```
|
||||||
@ -155,7 +154,7 @@ impl fmt::Debug for Modifier {
|
|||||||
/// anywhere that accepts `Into<Style>`.
|
/// anywhere that accepts `Into<Style>`.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier, Style},
|
/// style::{Color, Modifier, Style},
|
||||||
/// text::Line,
|
/// text::Line,
|
||||||
/// };
|
/// };
|
||||||
@ -174,7 +173,7 @@ impl fmt::Debug for Modifier {
|
|||||||
/// just S3.
|
/// just S3.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// buffer::Buffer,
|
/// buffer::Buffer,
|
||||||
/// layout::Rect,
|
/// layout::Rect,
|
||||||
/// style::{Color, Modifier, Style},
|
/// style::{Color, Modifier, Style},
|
||||||
@ -214,7 +213,7 @@ impl fmt::Debug for Modifier {
|
|||||||
/// reset all properties until that point use [`Style::reset`].
|
/// reset all properties until that point use [`Style::reset`].
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// buffer::Buffer,
|
/// buffer::Buffer,
|
||||||
/// layout::Rect,
|
/// layout::Rect,
|
||||||
/// style::{Color, Modifier, Style},
|
/// style::{Color, Modifier, Style},
|
||||||
@ -304,7 +303,7 @@ impl Style {
|
|||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Color, Style};
|
/// use ratatui_core::style::{Color, Style};
|
||||||
///
|
///
|
||||||
/// let style = Style::default().fg(Color::Blue);
|
/// let style = Style::default().fg(Color::Blue);
|
||||||
/// let diff = Style::default().fg(Color::Red);
|
/// let diff = Style::default().fg(Color::Red);
|
||||||
@ -321,7 +320,7 @@ impl Style {
|
|||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Color, Style};
|
/// use ratatui_core::style::{Color, Style};
|
||||||
///
|
///
|
||||||
/// let style = Style::default().bg(Color::Blue);
|
/// let style = Style::default().bg(Color::Blue);
|
||||||
/// let diff = Style::default().bg(Color::Red);
|
/// let diff = Style::default().bg(Color::Red);
|
||||||
@ -346,7 +345,7 @@ impl Style {
|
|||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Color, Modifier, Style};
|
/// use ratatui_core::style::{Color, Modifier, Style};
|
||||||
///
|
///
|
||||||
/// let style = Style::default()
|
/// let style = Style::default()
|
||||||
/// .underline_color(Color::Blue)
|
/// .underline_color(Color::Blue)
|
||||||
@ -375,7 +374,7 @@ impl Style {
|
|||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Modifier, Style};
|
/// use ratatui_core::style::{Modifier, Style};
|
||||||
///
|
///
|
||||||
/// let style = Style::default().add_modifier(Modifier::BOLD);
|
/// let style = Style::default().add_modifier(Modifier::BOLD);
|
||||||
/// let diff = Style::default().add_modifier(Modifier::ITALIC);
|
/// let diff = Style::default().add_modifier(Modifier::ITALIC);
|
||||||
@ -397,7 +396,7 @@ impl Style {
|
|||||||
/// ## Examples
|
/// ## Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Modifier, Style};
|
/// use ratatui_core::style::{Modifier, Style};
|
||||||
///
|
///
|
||||||
/// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC);
|
/// let style = Style::default().add_modifier(Modifier::BOLD | Modifier::ITALIC);
|
||||||
/// let diff = Style::default().remove_modifier(Modifier::ITALIC);
|
/// let diff = Style::default().remove_modifier(Modifier::ITALIC);
|
||||||
@ -420,7 +419,7 @@ impl Style {
|
|||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::style::{Color, Modifier, Style};
|
/// use ratatui_core::style::{Color, Modifier, Style};
|
||||||
///
|
///
|
||||||
/// let style_1 = Style::default().fg(Color::Yellow);
|
/// let style_1 = Style::default().fg(Color::Yellow);
|
||||||
/// let style_2 = Style::default().bg(Color::Red);
|
/// let style_2 = Style::default().bg(Color::Red);
|
||||||
@ -506,7 +505,7 @@ impl From<Color> for Style {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Color, Style};
|
/// use ratatui_core::style::{Color, Style};
|
||||||
///
|
///
|
||||||
/// let style = Style::from(Color::Red);
|
/// let style = Style::from(Color::Red);
|
||||||
/// ```
|
/// ```
|
||||||
@ -521,7 +520,7 @@ impl From<(Color, Color)> for Style {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Color, Style};
|
/// use ratatui_core::style::{Color, Style};
|
||||||
///
|
///
|
||||||
/// // red foreground, blue background
|
/// // red foreground, blue background
|
||||||
/// let style = Style::from((Color::Red, Color::Blue));
|
/// let style = Style::from((Color::Red, Color::Blue));
|
||||||
@ -544,7 +543,7 @@ impl From<Modifier> for Style {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Style, Modifier};
|
/// use ratatui_core::style::{Style, Modifier};
|
||||||
///
|
///
|
||||||
/// // add bold and italic
|
/// // add bold and italic
|
||||||
/// let style = Style::from(Modifier::BOLD|Modifier::ITALIC);
|
/// let style = Style::from(Modifier::BOLD|Modifier::ITALIC);
|
||||||
@ -559,7 +558,7 @@ impl From<(Modifier, Modifier)> for Style {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Modifier, Style};
|
/// use ratatui_core::style::{Modifier, Style};
|
||||||
///
|
///
|
||||||
/// // add bold and italic, remove dim
|
/// // add bold and italic, remove dim
|
||||||
/// let style = Style::from((Modifier::BOLD | Modifier::ITALIC, Modifier::DIM));
|
/// let style = Style::from((Modifier::BOLD | Modifier::ITALIC, Modifier::DIM));
|
||||||
@ -579,7 +578,7 @@ impl From<(Color, Modifier)> for Style {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Color, Modifier, Style};
|
/// use ratatui_core::style::{Color, Modifier, Style};
|
||||||
///
|
///
|
||||||
/// // red foreground, add bold and italic
|
/// // red foreground, add bold and italic
|
||||||
/// let style = Style::from((Color::Red, Modifier::BOLD | Modifier::ITALIC));
|
/// let style = Style::from((Color::Red, Modifier::BOLD | Modifier::ITALIC));
|
||||||
@ -597,7 +596,7 @@ impl From<(Color, Color, Modifier)> for Style {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Color, Modifier, Style};
|
/// use ratatui_core::style::{Color, Modifier, Style};
|
||||||
///
|
///
|
||||||
/// // red foreground, blue background, add bold and italic
|
/// // red foreground, blue background, add bold and italic
|
||||||
/// let style = Style::from((Color::Red, Color::Blue, Modifier::BOLD | Modifier::ITALIC));
|
/// let style = Style::from((Color::Red, Color::Blue, Modifier::BOLD | Modifier::ITALIC));
|
||||||
@ -614,7 +613,7 @@ impl From<(Color, Color, Modifier, Modifier)> for Style {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::{Color, Modifier, Style};
|
/// use ratatui_core::style::{Color, Modifier, Style};
|
||||||
///
|
///
|
||||||
/// // red foreground, blue background, add bold and italic, remove dim
|
/// // red foreground, blue background, add bold and italic, remove dim
|
||||||
/// let style = Style::from((
|
/// let style = Style::from((
|
@ -44,7 +44,7 @@ use crate::style::stylize::{ColorDebug, ColorDebugKind};
|
|||||||
/// ```
|
/// ```
|
||||||
/// use std::str::FromStr;
|
/// use std::str::FromStr;
|
||||||
///
|
///
|
||||||
/// use ratatui::style::Color;
|
/// use ratatui_core::style::Color;
|
||||||
///
|
///
|
||||||
/// assert_eq!(Color::from_str("red"), Ok(Color::Red));
|
/// assert_eq!(Color::from_str("red"), Ok(Color::Red));
|
||||||
/// assert_eq!("red".parse(), Ok(Color::Red));
|
/// assert_eq!("red".parse(), Ok(Color::Red));
|
||||||
@ -112,14 +112,12 @@ pub enum Color {
|
|||||||
/// Notably versions of Windows Terminal prior to Windows 10 and macOS Terminal.app do not
|
/// Notably versions of Windows Terminal prior to Windows 10 and macOS Terminal.app do not
|
||||||
/// support this.
|
/// support this.
|
||||||
///
|
///
|
||||||
/// If the terminal does not support true color, code using the [`TermwizBackend`] will
|
/// If the terminal does not support true color, code using the `TermwizBackend` will
|
||||||
/// fallback to the default text color. Crossterm and Termion do not have this capability and
|
/// fallback to the default text color. Crossterm and Termion do not have this capability and
|
||||||
/// the display will be unpredictable (e.g. Terminal.app may display glitched blinking text).
|
/// the display will be unpredictable (e.g. Terminal.app may display glitched blinking text).
|
||||||
/// See <https://github.com/ratatui/ratatui/issues/475> for an example of this problem.
|
/// See <https://github.com/ratatui/ratatui/issues/475> for an example of this problem.
|
||||||
///
|
///
|
||||||
/// See also: <https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit>
|
/// See also: <https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit>
|
||||||
///
|
|
||||||
/// [`TermwizBackend`]: crate::backend::TermwizBackend
|
|
||||||
Rgb(u8, u8, u8),
|
Rgb(u8, u8, u8),
|
||||||
/// An 8-bit 256 color.
|
/// An 8-bit 256 color.
|
||||||
///
|
///
|
||||||
@ -170,7 +168,7 @@ impl<'de> serde::Deserialize<'de> for Color {
|
|||||||
/// ```
|
/// ```
|
||||||
/// use std::str::FromStr;
|
/// use std::str::FromStr;
|
||||||
///
|
///
|
||||||
/// use ratatui::style::Color;
|
/// use ratatui_core::style::Color;
|
||||||
///
|
///
|
||||||
/// #[derive(Debug, serde::Deserialize)]
|
/// #[derive(Debug, serde::Deserialize)]
|
||||||
/// struct Theme {
|
/// struct Theme {
|
||||||
@ -265,7 +263,7 @@ impl std::error::Error for ParseColorError {}
|
|||||||
/// ```
|
/// ```
|
||||||
/// use std::str::FromStr;
|
/// use std::str::FromStr;
|
||||||
///
|
///
|
||||||
/// use ratatui::style::Color;
|
/// use ratatui_core::style::Color;
|
||||||
///
|
///
|
||||||
/// let color: Color = Color::from_str("blue").unwrap();
|
/// let color: Color = Color::from_str("blue").unwrap();
|
||||||
/// assert_eq!(color, Color::Blue);
|
/// assert_eq!(color, Color::Blue);
|
||||||
@ -385,7 +383,8 @@ impl Color {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{palette::Hsl, style::Color};
|
/// use palette::Hsl;
|
||||||
|
/// use ratatui_core::style::Color;
|
||||||
///
|
///
|
||||||
/// // Minimum Lightness is black
|
/// // Minimum Lightness is black
|
||||||
/// let color: Color = Color::from_hsl(Hsl::new(0.0, 0.0, 0.0));
|
/// let color: Color = Color::from_hsl(Hsl::new(0.0, 0.0, 0.0));
|
||||||
@ -437,7 +436,8 @@ impl Color {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ratatui::{palette::Hsluv, style::Color};
|
/// use palette::Hsluv;
|
||||||
|
/// use ratatui_core::style::Color;
|
||||||
///
|
///
|
||||||
/// // Minimum Lightness is black
|
/// // Minimum Lightness is black
|
||||||
/// let color: Color = Color::from_hsluv(Hsluv::new(0.0, 100.0, 0.0));
|
/// let color: Color = Color::from_hsluv(Hsluv::new(0.0, 100.0, 0.0));
|
@ -403,7 +403,7 @@
|
|||||||
//! # Example
|
//! # Example
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use ratatui::style::{
|
//! use ratatui_core::style::{
|
||||||
//! palette::material::{BLUE, RED},
|
//! palette::material::{BLUE, RED},
|
||||||
//! Color,
|
//! Color,
|
||||||
//! };
|
//! };
|
@ -268,7 +268,7 @@
|
|||||||
//! # Example
|
//! # Example
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use ratatui::style::{
|
//! use ratatui_core::style::{
|
||||||
//! palette::tailwind::{BLUE, RED},
|
//! palette::tailwind::{BLUE, RED},
|
||||||
//! Color,
|
//! Color,
|
||||||
//! };
|
//! };
|
@ -15,7 +15,7 @@ use crate::style::Color;
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use palette::Srgb;
|
/// use palette::Srgb;
|
||||||
/// use ratatui::style::Color;
|
/// use ratatui_core::style::Color;
|
||||||
///
|
///
|
||||||
/// let color = Color::from(Srgb::new(1.0f32, 0.0, 0.0));
|
/// let color = Color::from(Srgb::new(1.0f32, 0.0, 0.0));
|
||||||
/// assert_eq!(color, Color::Rgb(255, 0, 0));
|
/// assert_eq!(color, Color::Rgb(255, 0, 0));
|
||||||
@ -36,7 +36,7 @@ impl<T: IntoStimulus<u8>> From<Srgb<T>> for Color {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use palette::LinSrgb;
|
/// use palette::LinSrgb;
|
||||||
/// use ratatui::style::Color;
|
/// use ratatui_core::style::Color;
|
||||||
///
|
///
|
||||||
/// let color = Color::from(LinSrgb::new(1.0f32, 0.0, 0.0));
|
/// let color = Color::from(LinSrgb::new(1.0f32, 0.0, 0.0));
|
||||||
/// assert_eq!(color, Color::Rgb(255, 0, 0));
|
/// assert_eq!(color, Color::Rgb(255, 0, 0));
|
@ -195,8 +195,8 @@ macro_rules! modifier {
|
|||||||
/// by `not_`). The `reset()` method is also provided to reset the style.
|
/// by `not_`). The `reset()` method is also provided to reset the style.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```ignore
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier, Style, Stylize},
|
/// style::{Color, Modifier, Style, Stylize},
|
||||||
/// text::Line,
|
/// text::Line,
|
||||||
/// widgets::{Block, Paragraph},
|
/// widgets::{Block, Paragraph},
|
@ -1,25 +1,25 @@
|
|||||||
//! Primitives for styled text.
|
//! Primitives for styled text.
|
||||||
//!
|
//!
|
||||||
//! A terminal UI is at its root a lot of strings. In order to make it accessible and stylish,
|
//! A terminal UI is at its root a lot of strings. In order to make it accessible and stylish, those
|
||||||
//! those strings may be associated to a set of styles. `ratatui` has three ways to represent them:
|
//! strings may be associated to a set of styles. `ratatui` has three ways to represent them:
|
||||||
//! - A single line string where all graphemes have the same style is represented by a [`Span`].
|
//! - A single line string where all graphemes have the same style is represented by a [`Span`].
|
||||||
//! - A single line string where each grapheme may have its own style is represented by [`Line`].
|
//! - A single line string where each grapheme may have its own style is represented by [`Line`].
|
||||||
//! - A multiple line string where each grapheme may have its own style is represented by a
|
//! - A multiple line string where each grapheme may have its own style is represented by a
|
||||||
//! [`Text`].
|
//! [`Text`].
|
||||||
//!
|
//!
|
||||||
//! These types form a hierarchy: [`Line`] is a collection of [`Span`] and each line of [`Text`]
|
//! These types form a hierarchy: [`Line`] is a collection of [`Span`] and each line of [`Text`] is
|
||||||
//! is a [`Line`].
|
//! a [`Line`].
|
||||||
//!
|
//!
|
||||||
//! Keep it mind that a lot of widgets will use those types to advertise what kind of string is
|
//! Keep it mind that a lot of widgets will use those types to advertise what kind of string is
|
||||||
//! supported for their properties. Moreover, `ratatui` provides convenient `From` implementations
|
//! supported for their properties. Moreover, `ratatui` provides convenient `From` implementations
|
||||||
//! so that you can start by using simple `String` or `&str` and then promote them to the previous
|
//! so that you can start by using simple `String` or `&str` and then promote them to the previous
|
||||||
//! primitives when you need additional styling capabilities.
|
//! primitives when you need additional styling capabilities.
|
||||||
//!
|
//!
|
||||||
//! For example, for the [`crate::widgets::Block`] widget, all the following calls are valid to set
|
//! For example, for the `Block` widget, all the following calls are valid to set its `title`
|
||||||
//! its `title` property (which is a [`Line`] under the hood):
|
//! property (which is a [`Line`] under the hood):
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust,ignore
|
||||||
//! use ratatui::{
|
//! use ratatui_core::{
|
||||||
//! style::{Color, Style},
|
//! style::{Color, Style},
|
||||||
//! text::{Line, Span},
|
//! text::{Line, Span},
|
||||||
//! widgets::Block,
|
//! widgets::Block,
|
@ -28,7 +28,7 @@ impl<'a> StyledGrapheme<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_whitespace(&self) -> bool {
|
pub fn is_whitespace(&self) -> bool {
|
||||||
let symbol = self.symbol;
|
let symbol = self.symbol;
|
||||||
symbol == ZWSP || symbol.chars().all(char::is_whitespace) && symbol != NBSP
|
symbol == ZWSP || symbol.chars().all(char::is_whitespace) && symbol != NBSP
|
||||||
}
|
}
|
@ -75,7 +75,7 @@ use crate::{
|
|||||||
/// [`Style`].
|
/// [`Style`].
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier, Style, Stylize},
|
/// style::{Color, Modifier, Style, Stylize},
|
||||||
/// text::{Line, Span},
|
/// text::{Line, Span},
|
||||||
/// };
|
/// };
|
||||||
@ -102,7 +102,7 @@ use crate::{
|
|||||||
/// methods of the [`Stylize`] trait.
|
/// methods of the [`Stylize`] trait.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier, Style, Stylize},
|
/// style::{Color, Modifier, Style, Stylize},
|
||||||
/// text::Line,
|
/// text::Line,
|
||||||
/// };
|
/// };
|
||||||
@ -121,7 +121,7 @@ use crate::{
|
|||||||
/// ignored and the line is truncated.
|
/// ignored and the line is truncated.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{layout::Alignment, text::Line};
|
/// use ratatui_core::{layout::Alignment, text::Line};
|
||||||
///
|
///
|
||||||
/// let line = Line::from("Hello world!").alignment(Alignment::Right);
|
/// let line = Line::from("Hello world!").alignment(Alignment::Right);
|
||||||
/// let line = Line::from("Hello world!").centered();
|
/// let line = Line::from("Hello world!").centered();
|
||||||
@ -134,13 +134,12 @@ use crate::{
|
|||||||
/// `Line` implements the [`Widget`] trait, which means it can be rendered to a [`Buffer`].
|
/// `Line` implements the [`Widget`] trait, which means it can be rendered to a [`Buffer`].
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// buffer::Buffer,
|
/// buffer::Buffer,
|
||||||
/// layout::Rect,
|
/// layout::Rect,
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Line,
|
/// text::Line,
|
||||||
/// widgets::Widget,
|
/// widgets::Widget,
|
||||||
/// Frame,
|
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// # fn render(area: Rect, buf: &mut Buffer) {
|
/// # fn render(area: Rect, buf: &mut Buffer) {
|
||||||
@ -148,19 +147,23 @@ use crate::{
|
|||||||
/// let line = Line::from("Hello world!").style(Style::new().yellow().italic());
|
/// let line = Line::from("Hello world!").style(Style::new().yellow().italic());
|
||||||
/// line.render(area, buf);
|
/// line.render(area, buf);
|
||||||
/// # }
|
/// # }
|
||||||
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// Or you can use the `render_widget` method on the `Frame` in a `Terminal::draw` closure.
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// # use ratatui::{Frame, layout::Rect, text::Line};
|
||||||
/// # fn draw(frame: &mut Frame, area: Rect) {
|
/// # fn draw(frame: &mut Frame, area: Rect) {
|
||||||
/// // in a terminal.draw closure
|
/// let line = Line::from("Hello world!");
|
||||||
/// let line = Line::from("Hello world!").style(Style::new().yellow().italic());
|
|
||||||
/// frame.render_widget(line, area);
|
/// frame.render_widget(line, area);
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
/// ## Rendering Lines with a Paragraph widget
|
/// ## Rendering Lines with a Paragraph widget
|
||||||
///
|
///
|
||||||
/// Usually apps will use the [`Paragraph`] widget instead of rendering a [`Line`] directly as it
|
/// Usually apps will use the `Paragraph` widget instead of rendering a [`Line`] directly as it
|
||||||
/// provides more functionality.
|
/// provides more functionality.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust,ignore
|
||||||
/// use ratatui::{
|
/// use ratatui::{
|
||||||
/// buffer::Buffer,
|
/// buffer::Buffer,
|
||||||
/// layout::Rect,
|
/// layout::Rect,
|
||||||
@ -177,7 +180,6 @@ use crate::{
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// [`Paragraph`]: crate::widgets::Paragraph
|
|
||||||
/// [`Stylize`]: crate::style::Stylize
|
/// [`Stylize`]: crate::style::Stylize
|
||||||
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Line<'a> {
|
pub struct Line<'a> {
|
||||||
@ -241,7 +243,7 @@ impl<'a> Line<'a> {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::borrow::Cow;
|
/// use std::borrow::Cow;
|
||||||
///
|
///
|
||||||
/// use ratatui::text::Line;
|
/// use ratatui_core::text::Line;
|
||||||
///
|
///
|
||||||
/// Line::raw("test content");
|
/// Line::raw("test content");
|
||||||
/// Line::raw(String::from("test content"));
|
/// Line::raw(String::from("test content"));
|
||||||
@ -272,7 +274,7 @@ impl<'a> Line<'a> {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::borrow::Cow;
|
/// use std::borrow::Cow;
|
||||||
///
|
///
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Line,
|
/// text::Line,
|
||||||
/// };
|
/// };
|
||||||
@ -304,7 +306,7 @@ impl<'a> Line<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{style::Stylize, text::Line};
|
/// use ratatui_core::{style::Stylize, text::Line};
|
||||||
///
|
///
|
||||||
/// let line = Line::default().spans(vec!["Hello".blue(), " world!".green()]);
|
/// let line = Line::default().spans(vec!["Hello".blue(), " world!".green()]);
|
||||||
/// let line = Line::default().spans([1, 2, 3].iter().map(|i| format!("Item {}", i)));
|
/// let line = Line::default().spans([1, 2, 3].iter().map(|i| format!("Item {}", i)));
|
||||||
@ -332,7 +334,7 @@ impl<'a> Line<'a> {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Line,
|
/// text::Line,
|
||||||
/// };
|
/// };
|
||||||
@ -356,7 +358,7 @@ impl<'a> Line<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{layout::Alignment, text::Line};
|
/// use ratatui_core::{layout::Alignment, text::Line};
|
||||||
///
|
///
|
||||||
/// let mut line = Line::from("Hi, what's up?");
|
/// let mut line = Line::from("Hi, what's up?");
|
||||||
/// assert_eq!(None, line.alignment);
|
/// assert_eq!(None, line.alignment);
|
||||||
@ -382,7 +384,7 @@ impl<'a> Line<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Line;
|
/// use ratatui_core::text::Line;
|
||||||
///
|
///
|
||||||
/// let line = Line::from("Hi, what's up?").left_aligned();
|
/// let line = Line::from("Hi, what's up?").left_aligned();
|
||||||
/// ```
|
/// ```
|
||||||
@ -400,7 +402,7 @@ impl<'a> Line<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Line;
|
/// use ratatui_core::text::Line;
|
||||||
///
|
///
|
||||||
/// let line = Line::from("Hi, what's up?").centered();
|
/// let line = Line::from("Hi, what's up?").centered();
|
||||||
/// ```
|
/// ```
|
||||||
@ -418,7 +420,7 @@ impl<'a> Line<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Line;
|
/// use ratatui_core::text::Line;
|
||||||
///
|
///
|
||||||
/// let line = Line::from("Hi, what's up?").right_aligned();
|
/// let line = Line::from("Hi, what's up?").right_aligned();
|
||||||
/// ```
|
/// ```
|
||||||
@ -432,7 +434,7 @@ impl<'a> Line<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{style::Stylize, text::Line};
|
/// use ratatui_core::{style::Stylize, text::Line};
|
||||||
///
|
///
|
||||||
/// let line = Line::from(vec!["Hello".blue(), " world!".green()]);
|
/// let line = Line::from(vec!["Hello".blue(), " world!".green()]);
|
||||||
/// assert_eq!(12, line.width());
|
/// assert_eq!(12, line.width());
|
||||||
@ -454,7 +456,7 @@ impl<'a> Line<'a> {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::iter::Iterator;
|
/// use std::iter::Iterator;
|
||||||
///
|
///
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Style},
|
/// style::{Color, Style},
|
||||||
/// text::{Line, StyledGrapheme},
|
/// text::{Line, StyledGrapheme},
|
||||||
/// };
|
/// };
|
||||||
@ -498,7 +500,7 @@ impl<'a> Line<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier},
|
/// style::{Color, Modifier},
|
||||||
/// text::Line,
|
/// text::Line,
|
||||||
/// };
|
/// };
|
||||||
@ -527,7 +529,7 @@ impl<'a> Line<'a> {
|
|||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # let style = Style::default().yellow();
|
/// # let style = Style::default().yellow();
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Line,
|
/// text::Line,
|
||||||
/// };
|
/// };
|
||||||
@ -559,7 +561,7 @@ impl<'a> Line<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::{Line, Span};
|
/// use ratatui_core::text::{Line, Span};
|
||||||
///
|
///
|
||||||
/// let mut line = Line::from("Hello, ");
|
/// let mut line = Line::from("Hello, ");
|
||||||
/// line.push_span(Span::raw("world!"));
|
/// line.push_span(Span::raw("world!"));
|
@ -4,23 +4,23 @@ use crate::text::Text;
|
|||||||
|
|
||||||
/// A wrapper around a string that is masked when displayed.
|
/// A wrapper around a string that is masked when displayed.
|
||||||
///
|
///
|
||||||
/// The masked string is displayed as a series of the same character.
|
/// The masked string is displayed as a series of the same character. This might be used to display
|
||||||
/// This might be used to display a password field or similar secure data.
|
/// a password field or similar secure data.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// buffer::Buffer,
|
/// buffer::Buffer,
|
||||||
/// layout::Rect,
|
/// layout::Rect,
|
||||||
/// text::Masked,
|
/// text::{Masked, Text},
|
||||||
/// widgets::{Paragraph, Widget},
|
/// widgets::Widget,
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 1));
|
/// let mut buffer = Buffer::empty(Rect::new(0, 0, 5, 1));
|
||||||
/// let password = Masked::new("12345", 'x');
|
/// let password = Masked::new("12345", 'x');
|
||||||
///
|
///
|
||||||
/// Paragraph::new(password).render(buffer.area, &mut buffer);
|
/// Text::from(password).render(buffer.area, &mut buffer);
|
||||||
/// assert_eq!(buffer, Buffer::with_lines(["xxxxx"]));
|
/// assert_eq!(buffer, Buffer::with_lines(["xxxxx"]));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
@ -42,7 +42,7 @@ use crate::{
|
|||||||
/// any type convertible to [`Cow<str>`].
|
/// any type convertible to [`Cow<str>`].
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Span;
|
/// use ratatui_core::text::Span;
|
||||||
///
|
///
|
||||||
/// let span = Span::raw("test content");
|
/// let span = Span::raw("test content");
|
||||||
/// let span = Span::raw(String::from("test content"));
|
/// let span = Span::raw(String::from("test content"));
|
||||||
@ -56,7 +56,7 @@ use crate::{
|
|||||||
/// the [`Stylize`] trait.
|
/// the [`Stylize`] trait.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Span,
|
/// text::Span,
|
||||||
/// };
|
/// };
|
||||||
@ -73,7 +73,7 @@ use crate::{
|
|||||||
/// defined in the [`Stylize`] trait.
|
/// defined in the [`Stylize`] trait.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{style::Stylize, text::Span};
|
/// use ratatui_core::{style::Stylize, text::Span};
|
||||||
///
|
///
|
||||||
/// let span = Span::raw("test content").green().on_yellow().italic();
|
/// let span = Span::raw("test content").green().on_yellow().italic();
|
||||||
/// let span = Span::raw(String::from("test content"))
|
/// let span = Span::raw(String::from("test content"))
|
||||||
@ -82,11 +82,11 @@ use crate::{
|
|||||||
/// .italic();
|
/// .italic();
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// `Span` implements the [`Widget`] trait, which allows it to be rendered to a [`Buffer`]. Usually
|
/// `Span` implements the [`Widget`] trait, which allows it to be rendered to a [`Buffer`]. Often
|
||||||
/// apps will use the [`Paragraph`] widget instead of rendering `Span` directly, as it handles text
|
/// apps will use the `Paragraph` widget instead of rendering `Span` directly, as it handles text
|
||||||
/// wrapping and alignment for you.
|
/// wrapping and alignment for you.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust,ignore
|
||||||
/// use ratatui::{style::Stylize, Frame};
|
/// use ratatui::{style::Stylize, Frame};
|
||||||
///
|
///
|
||||||
/// # fn render_frame(frame: &mut Frame) {
|
/// # fn render_frame(frame: &mut Frame) {
|
||||||
@ -94,7 +94,6 @@ use crate::{
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
/// [`Line`]: crate::text::Line
|
/// [`Line`]: crate::text::Line
|
||||||
/// [`Paragraph`]: crate::widgets::Paragraph
|
|
||||||
/// [`Stylize`]: crate::style::Stylize
|
/// [`Stylize`]: crate::style::Stylize
|
||||||
/// [`Cow<str>`]: std::borrow::Cow
|
/// [`Cow<str>`]: std::borrow::Cow
|
||||||
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
||||||
@ -125,7 +124,7 @@ impl<'a> Span<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Span;
|
/// use ratatui_core::text::Span;
|
||||||
///
|
///
|
||||||
/// Span::raw("test content");
|
/// Span::raw("test content");
|
||||||
/// Span::raw(String::from("test content"));
|
/// Span::raw(String::from("test content"));
|
||||||
@ -151,7 +150,7 @@ impl<'a> Span<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Span,
|
/// text::Span,
|
||||||
/// };
|
/// };
|
||||||
@ -183,7 +182,7 @@ impl<'a> Span<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Span;
|
/// use ratatui_core::text::Span;
|
||||||
///
|
///
|
||||||
/// let mut span = Span::default().content("content");
|
/// let mut span = Span::default().content("content");
|
||||||
/// ```
|
/// ```
|
||||||
@ -209,7 +208,7 @@ impl<'a> Span<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Span,
|
/// text::Span,
|
||||||
/// };
|
/// };
|
||||||
@ -234,7 +233,7 @@ impl<'a> Span<'a> {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Span,
|
/// text::Span,
|
||||||
/// };
|
/// };
|
||||||
@ -260,7 +259,7 @@ impl<'a> Span<'a> {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Span,
|
/// text::Span,
|
||||||
/// };
|
/// };
|
||||||
@ -295,7 +294,7 @@ impl<'a> Span<'a> {
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::iter::Iterator;
|
/// use std::iter::Iterator;
|
||||||
///
|
///
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::{Span, StyledGrapheme},
|
/// text::{Span, StyledGrapheme},
|
||||||
/// };
|
/// };
|
||||||
@ -332,7 +331,7 @@ impl<'a> Span<'a> {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::Stylize;
|
/// use ratatui_core::style::Stylize;
|
||||||
///
|
///
|
||||||
/// let line = "Test Content".green().italic().into_left_aligned_line();
|
/// let line = "Test Content".green().italic().into_left_aligned_line();
|
||||||
/// ```
|
/// ```
|
||||||
@ -352,7 +351,7 @@ impl<'a> Span<'a> {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::Stylize;
|
/// use ratatui_core::style::Stylize;
|
||||||
///
|
///
|
||||||
/// let line = "Test Content".green().italic().into_centered_line();
|
/// let line = "Test Content".green().italic().into_centered_line();
|
||||||
/// ```
|
/// ```
|
||||||
@ -372,7 +371,7 @@ impl<'a> Span<'a> {
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::style::Stylize;
|
/// use ratatui_core::style::Stylize;
|
||||||
///
|
///
|
||||||
/// let line = "Test Content".green().italic().into_right_aligned_line();
|
/// let line = "Test Content".green().italic().into_right_aligned_line();
|
||||||
/// ```
|
/// ```
|
@ -68,7 +68,7 @@ use crate::{
|
|||||||
/// ```rust
|
/// ```rust
|
||||||
/// use std::{borrow::Cow, iter};
|
/// use std::{borrow::Cow, iter};
|
||||||
///
|
///
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier, Style, Stylize},
|
/// style::{Color, Modifier, Style, Stylize},
|
||||||
/// text::{Line, Span, Text},
|
/// text::{Line, Span, Text},
|
||||||
/// };
|
/// };
|
||||||
@ -108,7 +108,7 @@ use crate::{
|
|||||||
/// [`Stylize`] trait.
|
/// [`Stylize`] trait.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier, Style, Stylize},
|
/// style::{Color, Modifier, Style, Stylize},
|
||||||
/// text::{Line, Text},
|
/// text::{Line, Text},
|
||||||
/// };
|
/// };
|
||||||
@ -129,7 +129,7 @@ use crate::{
|
|||||||
/// Lines composing the text can also be individually aligned with [`Line::alignment`].
|
/// Lines composing the text can also be individually aligned with [`Line::alignment`].
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// layout::Alignment,
|
/// layout::Alignment,
|
||||||
/// text::{Line, Text},
|
/// text::{Line, Text},
|
||||||
/// };
|
/// };
|
||||||
@ -146,19 +146,23 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// ## Rendering Text
|
/// ## Rendering Text
|
||||||
/// `Text` implements the [`Widget`] trait, which means it can be rendered to a [`Buffer`] or to a
|
/// `Text` implements the [`Widget`] trait, which means it can be rendered to a [`Buffer`] or to a
|
||||||
/// [`Frame`].
|
/// `Frame`.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use ratatui::{buffer::Buffer, layout::Rect};
|
/// # use ratatui_core::{buffer::Buffer, layout::Rect};
|
||||||
/// use ratatui::{text::Text, widgets::Widget, Frame};
|
/// use ratatui_core::{text::Text, widgets::Widget};
|
||||||
///
|
///
|
||||||
/// // within another widget's `render` method:
|
/// // within another widget's `render` method:
|
||||||
/// # fn render(area: Rect, buf: &mut Buffer) {
|
/// # fn render(area: Rect, buf: &mut Buffer) {
|
||||||
/// let text = Text::from("The first line\nThe second line");
|
/// let text = Text::from("The first line\nThe second line");
|
||||||
/// text.render(area, buf);
|
/// text.render(area, buf);
|
||||||
/// # }
|
/// # }
|
||||||
|
/// ```
|
||||||
///
|
///
|
||||||
/// // within a terminal.draw closure:
|
/// Or you can use the `render_widget` method on a `Frame` within a `Terminal::draw` closure.
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// # use ratatui::{Frame, layout::Rect, text::Text};
|
||||||
/// # fn draw(frame: &mut Frame, area: Rect) {
|
/// # fn draw(frame: &mut Frame, area: Rect) {
|
||||||
/// let text = Text::from("The first line\nThe second line");
|
/// let text = Text::from("The first line\nThe second line");
|
||||||
/// frame.render_widget(text, area);
|
/// frame.render_widget(text, area);
|
||||||
@ -167,10 +171,10 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// ## Rendering Text with a Paragraph Widget
|
/// ## Rendering Text with a Paragraph Widget
|
||||||
///
|
///
|
||||||
/// Usually apps will use the [`Paragraph`] widget instead of rendering a `Text` directly as it
|
/// Usually apps will use the `Paragraph` widget instead of rendering a `Text` directly as it
|
||||||
/// provides more functionality.
|
/// provides more functionality.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust,ignore
|
||||||
/// use ratatui::{
|
/// use ratatui::{
|
||||||
/// buffer::Buffer,
|
/// buffer::Buffer,
|
||||||
/// layout::Rect,
|
/// layout::Rect,
|
||||||
@ -189,7 +193,6 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// [`Paragraph`]: crate::widgets::Paragraph
|
/// [`Paragraph`]: crate::widgets::Paragraph
|
||||||
/// [`Stylize`]: crate::style::Stylize
|
/// [`Stylize`]: crate::style::Stylize
|
||||||
/// [`Frame`]: crate::Frame
|
|
||||||
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
#[derive(Default, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Text<'a> {
|
pub struct Text<'a> {
|
||||||
/// The alignment of this text.
|
/// The alignment of this text.
|
||||||
@ -228,7 +231,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Text;
|
/// use ratatui_core::text::Text;
|
||||||
///
|
///
|
||||||
/// Text::raw("The first line\nThe second line");
|
/// Text::raw("The first line\nThe second line");
|
||||||
/// Text::raw(String::from("The first line\nThe second line"));
|
/// Text::raw(String::from("The first line\nThe second line"));
|
||||||
@ -254,7 +257,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier, Style},
|
/// style::{Color, Modifier, Style},
|
||||||
/// text::Text,
|
/// text::Text,
|
||||||
/// };
|
/// };
|
||||||
@ -280,7 +283,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Text;
|
/// use ratatui_core::text::Text;
|
||||||
///
|
///
|
||||||
/// let text = Text::from("The first line\nThe second line");
|
/// let text = Text::from("The first line\nThe second line");
|
||||||
/// assert_eq!(15, text.width());
|
/// assert_eq!(15, text.width());
|
||||||
@ -294,7 +297,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Text;
|
/// use ratatui_core::text::Text;
|
||||||
///
|
///
|
||||||
/// let text = Text::from("The first line\nThe second line");
|
/// let text = Text::from("The first line\nThe second line");
|
||||||
/// assert_eq!(2, text.height());
|
/// assert_eq!(2, text.height());
|
||||||
@ -316,7 +319,7 @@ impl<'a> Text<'a> {
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Style, Stylize},
|
/// style::{Style, Stylize},
|
||||||
/// text::Text,
|
/// text::Text,
|
||||||
/// };
|
/// };
|
||||||
@ -348,7 +351,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier},
|
/// style::{Color, Modifier},
|
||||||
/// text::Text,
|
/// text::Text,
|
||||||
/// };
|
/// };
|
||||||
@ -381,7 +384,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// style::{Color, Modifier, Style},
|
/// style::{Color, Modifier, Style},
|
||||||
/// text::Text,
|
/// text::Text,
|
||||||
/// };
|
/// };
|
||||||
@ -412,7 +415,7 @@ impl<'a> Text<'a> {
|
|||||||
/// Set alignment to the whole text.
|
/// Set alignment to the whole text.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{layout::Alignment, text::Text};
|
/// use ratatui_core::{layout::Alignment, text::Text};
|
||||||
///
|
///
|
||||||
/// let mut text = Text::from("Hi, what's up?");
|
/// let mut text = Text::from("Hi, what's up?");
|
||||||
/// assert_eq!(None, text.alignment);
|
/// assert_eq!(None, text.alignment);
|
||||||
@ -425,7 +428,7 @@ impl<'a> Text<'a> {
|
|||||||
/// Set a default alignment and override it on a per line basis.
|
/// Set a default alignment and override it on a per line basis.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::{
|
/// use ratatui_core::{
|
||||||
/// layout::Alignment,
|
/// layout::Alignment,
|
||||||
/// text::{Line, Text},
|
/// text::{Line, Text},
|
||||||
/// };
|
/// };
|
||||||
@ -466,7 +469,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Text;
|
/// use ratatui_core::text::Text;
|
||||||
///
|
///
|
||||||
/// let text = Text::from("Hi, what's up?").left_aligned();
|
/// let text = Text::from("Hi, what's up?").left_aligned();
|
||||||
/// ```
|
/// ```
|
||||||
@ -486,7 +489,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Text;
|
/// use ratatui_core::text::Text;
|
||||||
///
|
///
|
||||||
/// let text = Text::from("Hi, what's up?").centered();
|
/// let text = Text::from("Hi, what's up?").centered();
|
||||||
/// ```
|
/// ```
|
||||||
@ -506,7 +509,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::Text;
|
/// use ratatui_core::text::Text;
|
||||||
///
|
///
|
||||||
/// let text = Text::from("Hi, what's up?").right_aligned();
|
/// let text = Text::from("Hi, what's up?").right_aligned();
|
||||||
/// ```
|
/// ```
|
||||||
@ -533,7 +536,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::{Line, Span, Text};
|
/// use ratatui_core::text::{Line, Span, Text};
|
||||||
///
|
///
|
||||||
/// let mut text = Text::from("Hello, world!");
|
/// let mut text = Text::from("Hello, world!");
|
||||||
/// text.push_line(Line::from("How are you?"));
|
/// text.push_line(Line::from("How are you?"));
|
||||||
@ -552,7 +555,7 @@ impl<'a> Text<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use ratatui::text::{Span, Text};
|
/// use ratatui_core::text::{Span, Text};
|
||||||
///
|
///
|
||||||
/// let mut text = Text::from("Hello, world!");
|
/// let mut text = Text::from("Hello, world!");
|
||||||
/// text.push_span(Span::from("How are you?"));
|
/// text.push_span(Span::from("How are you?"));
|
693
ratatui-core/src/widgets.rs
Normal file
693
ratatui-core/src/widgets.rs
Normal file
@ -0,0 +1,693 @@
|
|||||||
|
#![warn(missing_docs)]
|
||||||
|
//! The `widgets` module contains the `Widget` and `StatefulWidget` traits, which are used to
|
||||||
|
//! render UI elements on the screen.
|
||||||
|
use crate::{buffer::Buffer, layout::Rect, style::Style};
|
||||||
|
|
||||||
|
/// A `Widget` is a type that can be drawn on a [`Buffer`] in a given [`Rect`].
|
||||||
|
///
|
||||||
|
/// Prior to Ratatui 0.26.0, widgets generally were created for each frame as they were consumed
|
||||||
|
/// during rendering. This meant that they were not meant to be stored but used as *commands* to
|
||||||
|
/// draw common figures in the UI.
|
||||||
|
///
|
||||||
|
/// Starting with Ratatui 0.26.0, all the internal widgets implement Widget for a reference to
|
||||||
|
/// themselves. This allows you to store a reference to a widget and render it later. Widget crates
|
||||||
|
/// should consider also doing this to allow for more flexibility in how widgets are used.
|
||||||
|
///
|
||||||
|
/// In Ratatui 0.26.0, we also added an unstable [`WidgetRef`] trait and implemented this on all the
|
||||||
|
/// internal widgets. In addition to the above benefit of rendering references to widgets, this also
|
||||||
|
/// allows you to render boxed widgets. This is useful when you want to store a collection of
|
||||||
|
/// widgets with different types. You can then iterate over the collection and render each widget.
|
||||||
|
/// See <https://github.com/ratatui/ratatui/issues/1287> for more information.
|
||||||
|
///
|
||||||
|
/// In general where you expect a widget to immutably work on its data, we recommended to implement
|
||||||
|
/// `Widget` for a reference to the widget (`impl Widget for &MyWidget`). If you need to store state
|
||||||
|
/// between draw calls, implement `StatefulWidget` if you want the Widget to be immutable, or
|
||||||
|
/// implement `Widget` for a mutable reference to the widget (`impl Widget for &mut MyWidget`) if
|
||||||
|
/// you want the widget to be mutable. The mutable widget pattern is used infrequently in apps, but
|
||||||
|
/// can be quite useful.
|
||||||
|
///
|
||||||
|
/// A blanket implementation of `Widget` for `&W` where `W` implements `WidgetRef` is provided.
|
||||||
|
/// Widget is also implemented for `&str` and `String` types.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use ratatui::{
|
||||||
|
/// backend::TestBackend,
|
||||||
|
/// widgets::{Clear, Widget},
|
||||||
|
/// Terminal,
|
||||||
|
/// };
|
||||||
|
/// # let backend = TestBackend::new(5, 5);
|
||||||
|
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||||
|
///
|
||||||
|
/// terminal.draw(|frame| {
|
||||||
|
/// frame.render_widget(Clear, frame.area());
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// It's common to render widgets inside other widgets:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use ratatui_core::{buffer::Buffer, layout::Rect, text::Line, widgets::Widget};
|
||||||
|
///
|
||||||
|
/// struct MyWidget;
|
||||||
|
///
|
||||||
|
/// impl Widget for MyWidget {
|
||||||
|
/// fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
/// Line::raw("Hello").render(area, buf);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub trait Widget {
|
||||||
|
/// Draws the current state of the widget in the given buffer. That is the only method required
|
||||||
|
/// to implement a custom widget.
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer)
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `StatefulWidget` is a widget that can take advantage of some local state to remember things
|
||||||
|
/// between two draw calls.
|
||||||
|
///
|
||||||
|
/// Most widgets can be drawn directly based on the input parameters. However, some features may
|
||||||
|
/// require some kind of associated state to be implemented.
|
||||||
|
///
|
||||||
|
/// For example, the `List` widget can highlight the item currently selected. This can be translated
|
||||||
|
/// in an offset, which is the number of elements to skip in order to have the selected item within
|
||||||
|
/// the viewport currently allocated to this widget. The widget can therefore only provide the
|
||||||
|
/// following behavior: whenever the selected item is out of the viewport scroll to a predefined
|
||||||
|
/// position (making the selected item the last viewable item or the one in the middle for example).
|
||||||
|
/// Nonetheless, if the widget has access to the last computed offset then it can implement a
|
||||||
|
/// natural scrolling experience where the last offset is reused until the selected item is out of
|
||||||
|
/// the viewport.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// use ratatui::{
|
||||||
|
/// backend::TestBackend,
|
||||||
|
/// widgets::{List, ListItem, ListState, StatefulWidget, Widget},
|
||||||
|
/// Terminal,
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// // Let's say we have some events to display.
|
||||||
|
/// struct Events {
|
||||||
|
/// // `items` is the state managed by your application.
|
||||||
|
/// items: Vec<String>,
|
||||||
|
/// // `state` is the state that can be modified by the UI. It stores the index of the selected
|
||||||
|
/// // item as well as the offset computed during the previous draw call (used to implement
|
||||||
|
/// // natural scrolling).
|
||||||
|
/// state: ListState,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Events {
|
||||||
|
/// fn new(items: Vec<String>) -> Events {
|
||||||
|
/// Events {
|
||||||
|
/// items,
|
||||||
|
/// state: ListState::default(),
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// pub fn set_items(&mut self, items: Vec<String>) {
|
||||||
|
/// self.items = items;
|
||||||
|
/// // We reset the state as the associated items have changed. This effectively reset
|
||||||
|
/// // the selection as well as the stored offset.
|
||||||
|
/// self.state = ListState::default();
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Select the next item. This will not be reflected until the widget is drawn in the
|
||||||
|
/// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
|
||||||
|
/// pub fn next(&mut self) {
|
||||||
|
/// let i = match self.state.selected() {
|
||||||
|
/// Some(i) => {
|
||||||
|
/// if i >= self.items.len() - 1 {
|
||||||
|
/// 0
|
||||||
|
/// } else {
|
||||||
|
/// i + 1
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// None => 0,
|
||||||
|
/// };
|
||||||
|
/// self.state.select(Some(i));
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Select the previous item. This will not be reflected until the widget is drawn in the
|
||||||
|
/// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
|
||||||
|
/// pub fn previous(&mut self) {
|
||||||
|
/// let i = match self.state.selected() {
|
||||||
|
/// Some(i) => {
|
||||||
|
/// if i == 0 {
|
||||||
|
/// self.items.len() - 1
|
||||||
|
/// } else {
|
||||||
|
/// i - 1
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// None => 0,
|
||||||
|
/// };
|
||||||
|
/// self.state.select(Some(i));
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Unselect the currently selected item if any. The implementation of `ListState` makes
|
||||||
|
/// // sure that the stored offset is also reset.
|
||||||
|
/// pub fn unselect(&mut self) {
|
||||||
|
/// self.state.select(None);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # let backend = TestBackend::new(5, 5);
|
||||||
|
/// # let mut terminal = Terminal::new(backend).unwrap();
|
||||||
|
///
|
||||||
|
/// let mut events = Events::new(vec![String::from("Item 1"), String::from("Item 2")]);
|
||||||
|
///
|
||||||
|
/// loop {
|
||||||
|
/// terminal.draw(|f| {
|
||||||
|
/// // The items managed by the application are transformed to something
|
||||||
|
/// // that is understood by ratatui.
|
||||||
|
/// let items: Vec<ListItem> = events
|
||||||
|
/// .items
|
||||||
|
/// .iter()
|
||||||
|
/// .map(|i| ListItem::new(i.as_str()))
|
||||||
|
/// .collect();
|
||||||
|
/// // The `List` widget is then built with those items.
|
||||||
|
/// let list = List::new(items);
|
||||||
|
/// // Finally the widget is rendered using the associated state. `events.state` is
|
||||||
|
/// // effectively the only thing that we will "remember" from this draw call.
|
||||||
|
/// f.render_stateful_widget(list, f.size(), &mut events.state);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// // In response to some input events or an external http request or whatever:
|
||||||
|
/// events.next();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub trait StatefulWidget {
|
||||||
|
/// State associated with the stateful widget.
|
||||||
|
///
|
||||||
|
/// If you don't need this then you probably want to implement [`Widget`] instead.
|
||||||
|
type State;
|
||||||
|
/// Draws the current state of the widget in the given buffer. That is the only method required
|
||||||
|
/// to implement a custom stateful widget.
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `WidgetRef` is a trait that allows rendering a widget by reference.
|
||||||
|
///
|
||||||
|
/// This trait is useful when you want to store a reference to a widget and render it later. It also
|
||||||
|
/// allows you to render boxed widgets.
|
||||||
|
///
|
||||||
|
/// Boxed widgets allow you to store widgets with a type that is not known at compile time. This is
|
||||||
|
/// useful when you want to store a collection of widgets with different types. You can then iterate
|
||||||
|
/// over the collection and render each widget.
|
||||||
|
///
|
||||||
|
/// This trait was introduced in Ratatui 0.26.0 and is implemented for all the internal widgets. It
|
||||||
|
/// is currently marked as unstable as we are still evaluating the API and may make changes in the
|
||||||
|
/// future. See <https://github.com/ratatui/ratatui/issues/1287> for more information.
|
||||||
|
///
|
||||||
|
/// A blanket implementation of `Widget` for `&W` where `W` implements `WidgetRef` is provided.
|
||||||
|
///
|
||||||
|
/// A blanket implementation of `WidgetRef` for `Option<W>` where `W` implements `WidgetRef` is
|
||||||
|
/// provided. This is a convenience approach to make it easier to attach child widgets to parent
|
||||||
|
/// widgets. It allows you to render an optional widget by reference.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||||
|
/// use ratatui_core::{
|
||||||
|
/// buffer::Buffer,
|
||||||
|
/// layout::Rect,
|
||||||
|
/// text::Line,
|
||||||
|
/// widgets::{Widget, WidgetRef},
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// struct Greeting;
|
||||||
|
///
|
||||||
|
/// struct Farewell;
|
||||||
|
///
|
||||||
|
/// impl WidgetRef for Greeting {
|
||||||
|
/// fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
/// Line::raw("Hello").render(area, buf);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// /// Only needed for backwards compatibility
|
||||||
|
/// impl Widget for Greeting {
|
||||||
|
/// fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
/// self.render_ref(area, buf);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl WidgetRef for Farewell {
|
||||||
|
/// fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
/// Line::raw("Goodbye").right_aligned().render(area, buf);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// /// Only needed for backwards compatibility
|
||||||
|
/// impl Widget for Farewell {
|
||||||
|
/// fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
/// self.render_ref(area, buf);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn render(area: Rect, buf: &mut Buffer) {
|
||||||
|
/// let greeting = Greeting;
|
||||||
|
/// let farewell = Farewell;
|
||||||
|
///
|
||||||
|
/// // these calls do not consume the widgets, so they can be used again later
|
||||||
|
/// greeting.render_ref(area, buf);
|
||||||
|
/// farewell.render_ref(area, buf);
|
||||||
|
///
|
||||||
|
/// // a collection of widgets with different types
|
||||||
|
/// let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(greeting), Box::new(farewell)];
|
||||||
|
/// for widget in widgets {
|
||||||
|
/// widget.render_ref(area, buf);
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[instability::unstable(feature = "widget-ref")]
|
||||||
|
pub trait WidgetRef {
|
||||||
|
/// Draws the current state of the widget in the given buffer. That is the only method required
|
||||||
|
/// to implement a custom widget.
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This allows you to render a widget by reference.
|
||||||
|
impl<W: WidgetRef> Widget for &W {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
self.render_ref(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A blanket implementation of `WidgetExt` for `Option<W>` where `W` implements `WidgetRef`.
|
||||||
|
///
|
||||||
|
/// This is a convenience implementation that makes it easy to attach child widgets to parent
|
||||||
|
/// widgets. It allows you to render an optional widget by reference.
|
||||||
|
///
|
||||||
|
/// The internal widgets use this pattern to render the optional `Block` widgets that are included
|
||||||
|
/// on most widgets.
|
||||||
|
/// Blanket implementation of `WidgetExt` for `Option<W>` where `W` implements `WidgetRef`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||||
|
/// use ratatui_core::{
|
||||||
|
/// buffer::Buffer,
|
||||||
|
/// layout::Rect,
|
||||||
|
/// text::Line,
|
||||||
|
/// widgets::{Widget, WidgetRef},
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// struct Parent {
|
||||||
|
/// child: Option<Child>,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// struct Child;
|
||||||
|
///
|
||||||
|
/// impl WidgetRef for Child {
|
||||||
|
/// fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
/// Line::raw("Hello from child").render(area, buf);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl WidgetRef for Parent {
|
||||||
|
/// fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
/// self.child.render_ref(area, buf);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
impl<W: WidgetRef> WidgetRef for Option<W> {
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
if let Some(widget) = self {
|
||||||
|
widget.render_ref(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A `StatefulWidgetRef` is a trait that allows rendering a stateful widget by reference.
|
||||||
|
///
|
||||||
|
/// This is the stateful equivalent of `WidgetRef`. It is useful when you want to store a reference
|
||||||
|
/// to a stateful widget and render it later. It also allows you to render boxed stateful widgets.
|
||||||
|
///
|
||||||
|
/// This trait was introduced in Ratatui 0.26.0 and is implemented for all the internal stateful
|
||||||
|
/// widgets. It is currently marked as unstable as we are still evaluating the API and may make
|
||||||
|
/// changes in the future. See <https://github.com/ratatui/ratatui/issues/1287> for more
|
||||||
|
/// information.
|
||||||
|
///
|
||||||
|
/// A blanket implementation of `StatefulWidget` for `&W` where `W` implements `StatefulWidgetRef`
|
||||||
|
/// is provided.
|
||||||
|
///
|
||||||
|
/// See the documentation for [`WidgetRef`] for more information on boxed widgets.
|
||||||
|
/// See the documentation for [`StatefulWidget`] for more information on stateful widgets.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||||
|
/// use ratatui_core::{
|
||||||
|
/// buffer::Buffer,
|
||||||
|
/// layout::Rect,
|
||||||
|
/// style::Stylize,
|
||||||
|
/// text::Line,
|
||||||
|
/// widgets::{StatefulWidget, StatefulWidgetRef, Widget},
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// struct PersonalGreeting;
|
||||||
|
///
|
||||||
|
/// impl StatefulWidgetRef for PersonalGreeting {
|
||||||
|
/// type State = String;
|
||||||
|
/// fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||||
|
/// Line::raw(format!("Hello {}", state)).render(area, buf);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl StatefulWidget for PersonalGreeting {
|
||||||
|
/// type State = String;
|
||||||
|
/// fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||||
|
/// (&self).render_ref(area, buf, state);
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn render(area: Rect, buf: &mut Buffer) {
|
||||||
|
/// let widget = PersonalGreeting;
|
||||||
|
/// let mut state = "world".to_string();
|
||||||
|
/// widget.render(area, buf, &mut state);
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[instability::unstable(feature = "widget-ref")]
|
||||||
|
pub trait StatefulWidgetRef {
|
||||||
|
/// State associated with the stateful widget.
|
||||||
|
///
|
||||||
|
/// If you don't need this then you probably want to implement [`WidgetRef`] instead.
|
||||||
|
type State;
|
||||||
|
/// Draws the current state of the widget in the given buffer. That is the only method required
|
||||||
|
/// to implement a custom stateful widget.
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: while StatefulWidgetRef is marked as unstable, the blanket implementation of StatefulWidget
|
||||||
|
// cannot be implemented as W::State is effectively pub(crate) and not accessible from outside the
|
||||||
|
// crate. Once stabilized, this blanket implementation can be added and the specific implementations
|
||||||
|
// on Table and List can be removed.
|
||||||
|
//
|
||||||
|
// /// Blanket implementation of `StatefulWidget` for `&W` where `W` implements `StatefulWidgetRef`.
|
||||||
|
// ///
|
||||||
|
// /// This allows you to render a stateful widget by reference.
|
||||||
|
// impl<W: StatefulWidgetRef> StatefulWidget for &W {
|
||||||
|
// type State = W::State;
|
||||||
|
// fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||||
|
// StatefulWidgetRef::render_ref(self, area, buf, state);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// Renders a string slice as a widget.
|
||||||
|
///
|
||||||
|
/// This implementation allows a string slice (`&str`) to act as a widget, meaning it can be drawn
|
||||||
|
/// onto a [`Buffer`] in a specified [`Rect`]. The slice represents a static string which can be
|
||||||
|
/// rendered by reference, thereby avoiding the need for string cloning or ownership transfer when
|
||||||
|
/// drawing the text to the screen.
|
||||||
|
impl Widget for &str {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
self.render_ref(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides the ability to render a string slice by reference.
|
||||||
|
///
|
||||||
|
/// This trait implementation ensures that a string slice, which is an immutable view over a
|
||||||
|
/// `String`, can be drawn on demand without requiring ownership of the string itself. It utilizes
|
||||||
|
/// the default text style when rendering onto the provided [`Buffer`] at the position defined by
|
||||||
|
/// [`Rect`].
|
||||||
|
impl WidgetRef for &str {
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renders a `String` object as a widget.
|
||||||
|
///
|
||||||
|
/// This implementation enables an owned `String` to be treated as a widget, which can be rendered
|
||||||
|
/// on a [`Buffer`] within the bounds of a given [`Rect`].
|
||||||
|
impl Widget for String {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
self.render_ref(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides the ability to render a `String` by reference.
|
||||||
|
///
|
||||||
|
/// This trait allows for a `String` to be rendered onto the [`Buffer`], similarly using the default
|
||||||
|
/// style settings. It ensures that an owned `String` can be rendered efficiently by reference,
|
||||||
|
/// without the need to give up ownership of the underlying text.
|
||||||
|
impl WidgetRef for String {
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use rstest::{fixture, rstest};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::text::Line;
|
||||||
|
|
||||||
|
#[fixture]
|
||||||
|
fn buf() -> Buffer {
|
||||||
|
Buffer::empty(Rect::new(0, 0, 20, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
mod widget {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct Greeting;
|
||||||
|
|
||||||
|
impl Widget for Greeting {
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
|
Line::from("Hello").render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render(mut buf: Buffer) {
|
||||||
|
let widget = Greeting;
|
||||||
|
widget.render(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod widget_ref {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct Greeting;
|
||||||
|
struct Farewell;
|
||||||
|
|
||||||
|
impl WidgetRef for Greeting {
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
Line::from("Hello").render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetRef for Farewell {
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
Line::from("Goodbye").right_aligned().render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render_ref(mut buf: Buffer) {
|
||||||
|
let widget = Greeting;
|
||||||
|
widget.render_ref(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensure that the blanket implementation of `Widget` for `&W` where `W` implements
|
||||||
|
/// `WidgetRef` works as expected.
|
||||||
|
#[rstest]
|
||||||
|
fn blanket_render(mut buf: Buffer) {
|
||||||
|
let widget = &Greeting;
|
||||||
|
widget.render(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn box_render_ref(mut buf: Buffer) {
|
||||||
|
let widget: Box<dyn WidgetRef> = Box::new(Greeting);
|
||||||
|
widget.render_ref(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn vec_box_render(mut buf: Buffer) {
|
||||||
|
let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(Greeting), Box::new(Farewell)];
|
||||||
|
for widget in widgets {
|
||||||
|
widget.render_ref(buf.area, &mut buf);
|
||||||
|
}
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["Hello Goodbye"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[fixture]
|
||||||
|
fn state() -> String {
|
||||||
|
"world".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
mod stateful_widget {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct PersonalGreeting;
|
||||||
|
|
||||||
|
impl StatefulWidget for PersonalGreeting {
|
||||||
|
type State = String;
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||||
|
Line::from(format!("Hello {state}")).render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render(mut buf: Buffer, mut state: String) {
|
||||||
|
let widget = PersonalGreeting;
|
||||||
|
widget.render(buf.area, &mut buf, &mut state);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod stateful_widget_ref {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct PersonalGreeting;
|
||||||
|
|
||||||
|
impl StatefulWidgetRef for PersonalGreeting {
|
||||||
|
type State = String;
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||||
|
Line::from(format!("Hello {state}")).render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render_ref(mut buf: Buffer, mut state: String) {
|
||||||
|
let widget = PersonalGreeting;
|
||||||
|
widget.render_ref(buf.area, &mut buf, &mut state);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note this cannot be tested until the blanket implementation of StatefulWidget for &W
|
||||||
|
// where W implements StatefulWidgetRef is added. (see the comment in the blanket
|
||||||
|
// implementation for more).
|
||||||
|
// /// This test is to ensure that the blanket implementation of `StatefulWidget` for `&W`
|
||||||
|
// where /// `W` implements `StatefulWidgetRef` works as expected.
|
||||||
|
// #[rstest]
|
||||||
|
// fn stateful_widget_blanket_render(mut buf: Buffer, mut state: String) {
|
||||||
|
// let widget = &PersonalGreeting;
|
||||||
|
// widget.render(buf.area, &mut buf, &mut state);
|
||||||
|
// assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn box_render_render(mut buf: Buffer, mut state: String) {
|
||||||
|
let widget = Box::new(PersonalGreeting);
|
||||||
|
widget.render_ref(buf.area, &mut buf, &mut state);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod option_widget_ref {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
struct Greeting;
|
||||||
|
|
||||||
|
impl WidgetRef for Greeting {
|
||||||
|
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||||
|
Line::from("Hello").render(area, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render_ref_some(mut buf: Buffer) {
|
||||||
|
let widget = Some(Greeting);
|
||||||
|
widget.render_ref(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render_ref_none(mut buf: Buffer) {
|
||||||
|
let widget: Option<Greeting> = None;
|
||||||
|
widget.render_ref(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines([" "]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod str {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render(mut buf: Buffer) {
|
||||||
|
"hello world".render(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render_area(mut buf: Buffer) {
|
||||||
|
let area = Rect::new(buf.area.x, buf.area.y, 11, buf.area.height);
|
||||||
|
"hello world, just hello".render(area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render_ref(mut buf: Buffer) {
|
||||||
|
"hello world".render_ref(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn option_render(mut buf: Buffer) {
|
||||||
|
Some("hello world").render(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn option_render_ref(mut buf: Buffer) {
|
||||||
|
Some("hello world").render_ref(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod string {
|
||||||
|
use super::*;
|
||||||
|
#[rstest]
|
||||||
|
fn render(mut buf: Buffer) {
|
||||||
|
String::from("hello world").render(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render_area(mut buf: Buffer) {
|
||||||
|
let area = Rect::new(buf.area.x, buf.area.y, 11, buf.area.height);
|
||||||
|
String::from("hello world, just hello").render(area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn render_ref(mut buf: Buffer) {
|
||||||
|
String::from("hello world").render_ref(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn option_render(mut buf: Buffer) {
|
||||||
|
Some(String::from("hello world")).render(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn option_render_ref(mut buf: Buffer) {
|
||||||
|
Some(String::from("hello world")).render_ref(buf.area, &mut buf);
|
||||||
|
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,25 +15,21 @@ edition.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "2.3"
|
bitflags.workspace = true
|
||||||
cassowary = "0.3"
|
|
||||||
compact_str = "0.8.0"
|
|
||||||
crossterm = { version = "0.28.1", optional = true }
|
crossterm = { version = "0.28.1", optional = true }
|
||||||
document-features = { version = "0.2.7", optional = true }
|
document-features = { version = "0.2.7", optional = true }
|
||||||
indoc = "2"
|
indoc = "2"
|
||||||
instability = "0.3.1"
|
instability.workspace = true
|
||||||
itertools = "0.13"
|
itertools.workspace = true
|
||||||
lru = "0.12.0"
|
|
||||||
paste = "1.0.2"
|
|
||||||
palette = { version = "0.7.6", optional = true }
|
palette = { version = "0.7.6", optional = true }
|
||||||
serde = { version = "1", optional = true, features = ["derive"] }
|
ratatui-core = { workspace = true, features = ["unstable-widget-ref"] }
|
||||||
strum = { version = "0.26.3", features = ["derive"] }
|
serde = { workspace = true, optional = true }
|
||||||
|
strum.workspace = true
|
||||||
termwiz = { version = "0.22.0", optional = true }
|
termwiz = { version = "0.22.0", optional = true }
|
||||||
time = { version = "0.3.11", optional = true, features = ["local-offset"] }
|
time = { version = "0.3.11", optional = true, features = ["local-offset"] }
|
||||||
unicode-segmentation = "1.10"
|
unicode-segmentation.workspace = true
|
||||||
unicode-truncate = "1"
|
|
||||||
# See <https://github.com/ratatui/ratatui/issues/1271> for information about why we pin unicode-width
|
# See <https://github.com/ratatui/ratatui/issues/1271> for information about why we pin unicode-width
|
||||||
unicode-width = "=0.2.0"
|
unicode-width.workspace = true
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
# termion is not supported on Windows
|
# termion is not supported on Windows
|
||||||
@ -124,13 +120,13 @@ termwiz = ["dep:termwiz"]
|
|||||||
#! The following optional features are available for all backends:
|
#! The following optional features are available for all backends:
|
||||||
## enables serialization and deserialization of style and color types using the [`serde`] crate.
|
## enables serialization and deserialization of style and color types using the [`serde`] crate.
|
||||||
## This is useful if you want to save themes to a file.
|
## This is useful if you want to save themes to a file.
|
||||||
serde = ["dep:serde", "bitflags/serde", "compact_str/serde"]
|
serde = ["dep:serde", "ratatui-core/serde"]
|
||||||
|
|
||||||
## enables the [`border!`] macro.
|
## enables the [`border!`] macro.
|
||||||
macros = []
|
macros = []
|
||||||
|
|
||||||
## enables conversions from colors in the [`palette`] crate to [`Color`](crate::style::Color).
|
## enables conversions from colors in the [`palette`] crate to [`Color`](crate::style::Color).
|
||||||
palette = ["dep:palette"]
|
palette = ["ratatui-core/palette", "dep:palette"]
|
||||||
|
|
||||||
## Use terminal scrolling regions to make some operations less prone to
|
## Use terminal scrolling regions to make some operations less prone to
|
||||||
## flickering. (i.e. Terminal::insert_before).
|
## flickering. (i.e. Terminal::insert_before).
|
||||||
@ -149,7 +145,7 @@ widget-calendar = ["dep:time"]
|
|||||||
## enables the backend code that sets the underline color.
|
## enables the backend code that sets the underline color.
|
||||||
## Underline color is only supported by the [`CrosstermBackend`](backend::CrosstermBackend) backend,
|
## Underline color is only supported by the [`CrosstermBackend`](backend::CrosstermBackend) backend,
|
||||||
## and is not supported on Windows 7.
|
## and is not supported on Windows 7.
|
||||||
underline-color = ["dep:crossterm"]
|
underline-color = ["dep:crossterm", "ratatui-core/underline-color"]
|
||||||
|
|
||||||
#! The following features are unstable and may change in the future:
|
#! The following features are unstable and may change in the future:
|
||||||
|
|
||||||
@ -166,9 +162,12 @@ unstable = [
|
|||||||
## See [Issue 293](https://github.com/ratatui/ratatui/issues/293) for more details.
|
## See [Issue 293](https://github.com/ratatui/ratatui/issues/293) for more details.
|
||||||
unstable-rendered-line-info = []
|
unstable-rendered-line-info = []
|
||||||
|
|
||||||
## Enables the [`WidgetRef`](widgets::WidgetRef) and [`StatefulWidgetRef`](widgets::StatefulWidgetRef) traits which are experimental and may change in
|
## enables the [`WidgetRef`] and [`StatefulWidgetRef`] traits which are experimental and may change
|
||||||
## the future.
|
## in the future.
|
||||||
unstable-widget-ref = []
|
##
|
||||||
|
## [`WidgetRef`]: widgets::WidgetRef
|
||||||
|
## [`StatefulWidgetRef`]: widgets::StatefulWidgetRef
|
||||||
|
unstable-widget-ref = ["ratatui-core/unstable-widget-ref"]
|
||||||
|
|
||||||
## Enables getting access to backends' writers.
|
## Enables getting access to backends' writers.
|
||||||
unstable-backend-writer = []
|
unstable-backend-writer = []
|
||||||
|
@ -10,13 +10,13 @@ use std::{error::Error, io};
|
|||||||
use crate::{
|
use crate::{
|
||||||
backend::{Backend, WindowSize},
|
backend::{Backend, WindowSize},
|
||||||
buffer::Cell,
|
buffer::Cell,
|
||||||
layout::Size,
|
layout::{Position, Size},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
termwiz::{
|
termwiz::{
|
||||||
caps::Capabilities,
|
caps::Capabilities,
|
||||||
cell::{AttributeChange, Blink, CellAttributes, Intensity, Underline},
|
cell::{AttributeChange, Blink, CellAttributes, Intensity, Underline},
|
||||||
color::{AnsiColor, ColorAttribute, ColorSpec, LinearRgba, RgbColor, SrgbaTuple},
|
color::{AnsiColor, ColorAttribute, ColorSpec, LinearRgba, RgbColor, SrgbaTuple},
|
||||||
surface::{Change, CursorVisibility, Position},
|
surface::{Change, CursorVisibility, Position as TermwizPosition},
|
||||||
terminal::{buffered::BufferedTerminal, ScreenSize, SystemTerminal, Terminal},
|
terminal::{buffered::BufferedTerminal, ScreenSize, SystemTerminal, Terminal},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -117,8 +117,8 @@ impl Backend for TermwizBackend {
|
|||||||
for (x, y, cell) in content {
|
for (x, y, cell) in content {
|
||||||
self.buffered_terminal.add_changes(vec![
|
self.buffered_terminal.add_changes(vec![
|
||||||
Change::CursorPosition {
|
Change::CursorPosition {
|
||||||
x: Position::Absolute(x as usize),
|
x: TermwizPosition::Absolute(x as usize),
|
||||||
y: Position::Absolute(y as usize),
|
y: TermwizPosition::Absolute(y as usize),
|
||||||
},
|
},
|
||||||
Change::Attribute(AttributeChange::Foreground(cell.fg.into_termwiz())),
|
Change::Attribute(AttributeChange::Foreground(cell.fg.into_termwiz())),
|
||||||
Change::Attribute(AttributeChange::Background(cell.bg.into_termwiz())),
|
Change::Attribute(AttributeChange::Background(cell.bg.into_termwiz())),
|
||||||
@ -192,19 +192,16 @@ impl Backend for TermwizBackend {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cursor_position(&mut self) -> io::Result<crate::layout::Position> {
|
fn get_cursor_position(&mut self) -> io::Result<Position> {
|
||||||
let (x, y) = self.buffered_terminal.cursor_position();
|
let (x, y) = self.buffered_terminal.cursor_position();
|
||||||
Ok((x as u16, y as u16).into())
|
Ok(Position::new(x as u16, y as u16))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cursor_position<P: Into<crate::layout::Position>>(
|
fn set_cursor_position<P: Into<Position>>(&mut self, position: P) -> io::Result<()> {
|
||||||
&mut self,
|
let Position { x, y } = position.into();
|
||||||
position: P,
|
|
||||||
) -> io::Result<()> {
|
|
||||||
let crate::layout::Position { x, y } = position.into();
|
|
||||||
self.buffered_terminal.add_change(Change::CursorPosition {
|
self.buffered_terminal.add_change(Change::CursorPosition {
|
||||||
x: Position::Absolute(x as usize),
|
x: TermwizPosition::Absolute(x as usize),
|
||||||
y: Position::Absolute(y as usize),
|
y: TermwizPosition::Absolute(y as usize),
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -144,7 +144,7 @@ impl TestBackend {
|
|||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn assert_buffer(&self, expected: &Buffer) {
|
pub fn assert_buffer(&self, expected: &Buffer) {
|
||||||
// TODO: use assert_eq!()
|
// TODO: use assert_eq!()
|
||||||
crate::assert_buffer_eq!(&self.buffer, expected);
|
ratatui_core::assert_buffer_eq!(&self.buffer, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Asserts that the `TestBackend`'s scrollback buffer is equal to the expected buffer.
|
/// Asserts that the `TestBackend`'s scrollback buffer is equal to the expected buffer.
|
||||||
|
@ -344,11 +344,9 @@ pub use termion;
|
|||||||
pub use termwiz;
|
pub use termwiz;
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod buffer;
|
pub use ratatui_core::{buffer, layout};
|
||||||
pub mod layout;
|
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod style;
|
pub use ratatui_core::{style, symbols};
|
||||||
pub mod symbols;
|
|
||||||
mod terminal;
|
mod terminal;
|
||||||
pub mod text;
|
pub use ratatui_core::text;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
@ -39,6 +39,10 @@ mod sparkline;
|
|||||||
mod table;
|
mod table;
|
||||||
mod tabs;
|
mod tabs;
|
||||||
|
|
||||||
|
pub use ratatui_core::widgets::{StatefulWidget, Widget};
|
||||||
|
#[instability::unstable(feature = "widget-ref")]
|
||||||
|
pub use ratatui_core::widgets::{StatefulWidgetRef, WidgetRef};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
barchart::{Bar, BarChart, BarGroup},
|
barchart::{Bar, BarChart, BarGroup},
|
||||||
block::{Block, BorderType, Padding},
|
block::{Block, BorderType, Padding},
|
||||||
@ -54,693 +58,3 @@ pub use self::{
|
|||||||
table::{Cell, HighlightSpacing, Row, Table, TableState},
|
table::{Cell, HighlightSpacing, Row, Table, TableState},
|
||||||
tabs::Tabs,
|
tabs::Tabs,
|
||||||
};
|
};
|
||||||
use crate::{buffer::Buffer, layout::Rect, style::Style};
|
|
||||||
|
|
||||||
/// A `Widget` is a type that can be drawn on a [`Buffer`] in a given [`Rect`].
|
|
||||||
///
|
|
||||||
/// Prior to Ratatui 0.26.0, widgets generally were created for each frame as they were consumed
|
|
||||||
/// during rendering. This meant that they were not meant to be stored but used as *commands* to
|
|
||||||
/// draw common figures in the UI.
|
|
||||||
///
|
|
||||||
/// Starting with Ratatui 0.26.0, all the internal widgets implement Widget for a reference to
|
|
||||||
/// themselves. This allows you to store a reference to a widget and render it later. Widget crates
|
|
||||||
/// should consider also doing this to allow for more flexibility in how widgets are used.
|
|
||||||
///
|
|
||||||
/// In Ratatui 0.26.0, we also added an unstable [`WidgetRef`] trait and implemented this on all the
|
|
||||||
/// internal widgets. In addition to the above benefit of rendering references to widgets, this also
|
|
||||||
/// allows you to render boxed widgets. This is useful when you want to store a collection of
|
|
||||||
/// widgets with different types. You can then iterate over the collection and render each widget.
|
|
||||||
/// See <https://github.com/ratatui/ratatui/issues/1287> for more information.
|
|
||||||
///
|
|
||||||
/// In general where you expect a widget to immutably work on its data, we recommended to implement
|
|
||||||
/// `Widget` for a reference to the widget (`impl Widget for &MyWidget`). If you need to store state
|
|
||||||
/// between draw calls, implement `StatefulWidget` if you want the Widget to be immutable, or
|
|
||||||
/// implement `Widget` for a mutable reference to the widget (`impl Widget for &mut MyWidget`) if
|
|
||||||
/// you want the widget to be mutable. The mutable widget pattern is used infrequently in apps, but
|
|
||||||
/// can be quite useful.
|
|
||||||
///
|
|
||||||
/// A blanket implementation of `Widget` for `&W` where `W` implements `WidgetRef` is provided.
|
|
||||||
/// Widget is also implemented for `&str` and `String` types.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// use ratatui::{
|
|
||||||
/// backend::TestBackend,
|
|
||||||
/// widgets::{Clear, Widget},
|
|
||||||
/// Terminal,
|
|
||||||
/// };
|
|
||||||
/// # let backend = TestBackend::new(5, 5);
|
|
||||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
|
||||||
///
|
|
||||||
/// terminal.draw(|frame| {
|
|
||||||
/// frame.render_widget(Clear, frame.area());
|
|
||||||
/// });
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// It's common to render widgets inside other widgets:
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// use ratatui::{buffer::Buffer, layout::Rect, text::Line, widgets::Widget};
|
|
||||||
///
|
|
||||||
/// struct MyWidget;
|
|
||||||
///
|
|
||||||
/// impl Widget for MyWidget {
|
|
||||||
/// fn render(self, area: Rect, buf: &mut Buffer) {
|
|
||||||
/// Line::raw("Hello").render(area, buf);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub trait Widget {
|
|
||||||
/// Draws the current state of the widget in the given buffer. That is the only method required
|
|
||||||
/// to implement a custom widget.
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer)
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `StatefulWidget` is a widget that can take advantage of some local state to remember things
|
|
||||||
/// between two draw calls.
|
|
||||||
///
|
|
||||||
/// Most widgets can be drawn directly based on the input parameters. However, some features may
|
|
||||||
/// require some kind of associated state to be implemented.
|
|
||||||
///
|
|
||||||
/// For example, the [`List`] widget can highlight the item currently selected. This can be
|
|
||||||
/// translated in an offset, which is the number of elements to skip in order to have the selected
|
|
||||||
/// item within the viewport currently allocated to this widget. The widget can therefore only
|
|
||||||
/// provide the following behavior: whenever the selected item is out of the viewport scroll to a
|
|
||||||
/// predefined position (making the selected item the last viewable item or the one in the middle
|
|
||||||
/// for example). Nonetheless, if the widget has access to the last computed offset then it can
|
|
||||||
/// implement a natural scrolling experience where the last offset is reused until the selected
|
|
||||||
/// item is out of the viewport.
|
|
||||||
///
|
|
||||||
/// ## Examples
|
|
||||||
///
|
|
||||||
/// ```rust,no_run
|
|
||||||
/// use std::io;
|
|
||||||
///
|
|
||||||
/// use ratatui::{
|
|
||||||
/// backend::TestBackend,
|
|
||||||
/// widgets::{List, ListItem, ListState, StatefulWidget, Widget},
|
|
||||||
/// Terminal,
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// // Let's say we have some events to display.
|
|
||||||
/// struct Events {
|
|
||||||
/// // `items` is the state managed by your application.
|
|
||||||
/// items: Vec<String>,
|
|
||||||
/// // `state` is the state that can be modified by the UI. It stores the index of the selected
|
|
||||||
/// // item as well as the offset computed during the previous draw call (used to implement
|
|
||||||
/// // natural scrolling).
|
|
||||||
/// state: ListState,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl Events {
|
|
||||||
/// fn new(items: Vec<String>) -> Events {
|
|
||||||
/// Events {
|
|
||||||
/// items,
|
|
||||||
/// state: ListState::default(),
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// pub fn set_items(&mut self, items: Vec<String>) {
|
|
||||||
/// self.items = items;
|
|
||||||
/// // We reset the state as the associated items have changed. This effectively reset
|
|
||||||
/// // the selection as well as the stored offset.
|
|
||||||
/// self.state = ListState::default();
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Select the next item. This will not be reflected until the widget is drawn in the
|
|
||||||
/// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
|
|
||||||
/// pub fn next(&mut self) {
|
|
||||||
/// let i = match self.state.selected() {
|
|
||||||
/// Some(i) => {
|
|
||||||
/// if i >= self.items.len() - 1 {
|
|
||||||
/// 0
|
|
||||||
/// } else {
|
|
||||||
/// i + 1
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// None => 0,
|
|
||||||
/// };
|
|
||||||
/// self.state.select(Some(i));
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Select the previous item. This will not be reflected until the widget is drawn in the
|
|
||||||
/// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
|
|
||||||
/// pub fn previous(&mut self) {
|
|
||||||
/// let i = match self.state.selected() {
|
|
||||||
/// Some(i) => {
|
|
||||||
/// if i == 0 {
|
|
||||||
/// self.items.len() - 1
|
|
||||||
/// } else {
|
|
||||||
/// i - 1
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// None => 0,
|
|
||||||
/// };
|
|
||||||
/// self.state.select(Some(i));
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Unselect the currently selected item if any. The implementation of `ListState` makes
|
|
||||||
/// // sure that the stored offset is also reset.
|
|
||||||
/// pub fn unselect(&mut self) {
|
|
||||||
/// self.state.select(None);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # let backend = TestBackend::new(5, 5);
|
|
||||||
/// # let mut terminal = Terminal::new(backend).unwrap();
|
|
||||||
///
|
|
||||||
/// let mut events = Events::new(vec![String::from("Item 1"), String::from("Item 2")]);
|
|
||||||
///
|
|
||||||
/// loop {
|
|
||||||
/// terminal.draw(|f| {
|
|
||||||
/// // The items managed by the application are transformed to something
|
|
||||||
/// // that is understood by ratatui.
|
|
||||||
/// let items: Vec<ListItem> = events
|
|
||||||
/// .items
|
|
||||||
/// .iter()
|
|
||||||
/// .map(|i| ListItem::new(i.as_str()))
|
|
||||||
/// .collect();
|
|
||||||
/// // The `List` widget is then built with those items.
|
|
||||||
/// let list = List::new(items);
|
|
||||||
/// // Finally the widget is rendered using the associated state. `events.state` is
|
|
||||||
/// // effectively the only thing that we will "remember" from this draw call.
|
|
||||||
/// f.render_stateful_widget(list, f.size(), &mut events.state);
|
|
||||||
/// });
|
|
||||||
///
|
|
||||||
/// // In response to some input events or an external http request or whatever:
|
|
||||||
/// events.next();
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub trait StatefulWidget {
|
|
||||||
/// State associated with the stateful widget.
|
|
||||||
///
|
|
||||||
/// If you don't need this then you probably want to implement [`Widget`] instead.
|
|
||||||
type State;
|
|
||||||
/// Draws the current state of the widget in the given buffer. That is the only method required
|
|
||||||
/// to implement a custom stateful widget.
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `WidgetRef` is a trait that allows rendering a widget by reference.
|
|
||||||
///
|
|
||||||
/// This trait is useful when you want to store a reference to a widget and render it later. It also
|
|
||||||
/// allows you to render boxed widgets.
|
|
||||||
///
|
|
||||||
/// Boxed widgets allow you to store widgets with a type that is not known at compile time. This is
|
|
||||||
/// useful when you want to store a collection of widgets with different types. You can then iterate
|
|
||||||
/// over the collection and render each widget.
|
|
||||||
///
|
|
||||||
/// This trait was introduced in Ratatui 0.26.0 and is implemented for all the internal widgets. It
|
|
||||||
/// is currently marked as unstable as we are still evaluating the API and may make changes in the
|
|
||||||
/// future. See <https://github.com/ratatui/ratatui/issues/1287> for more information.
|
|
||||||
///
|
|
||||||
/// A blanket implementation of `Widget` for `&W` where `W` implements `WidgetRef` is provided.
|
|
||||||
///
|
|
||||||
/// A blanket implementation of `WidgetRef` for `Option<W>` where `W` implements `WidgetRef` is
|
|
||||||
/// provided. This is a convenience approach to make it easier to attach child widgets to parent
|
|
||||||
/// widgets. It allows you to render an optional widget by reference.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
|
||||||
/// use ratatui::{
|
|
||||||
/// buffer::Buffer,
|
|
||||||
/// layout::Rect,
|
|
||||||
/// text::Line,
|
|
||||||
/// widgets::{Widget, WidgetRef},
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// struct Greeting;
|
|
||||||
///
|
|
||||||
/// struct Farewell;
|
|
||||||
///
|
|
||||||
/// impl WidgetRef for Greeting {
|
|
||||||
/// fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
/// Line::raw("Hello").render(area, buf);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// /// Only needed for backwards compatibility
|
|
||||||
/// impl Widget for Greeting {
|
|
||||||
/// fn render(self, area: Rect, buf: &mut Buffer) {
|
|
||||||
/// self.render_ref(area, buf);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl WidgetRef for Farewell {
|
|
||||||
/// fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
/// Line::raw("Goodbye").right_aligned().render(area, buf);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// /// Only needed for backwards compatibility
|
|
||||||
/// impl Widget for Farewell {
|
|
||||||
/// fn render(self, area: Rect, buf: &mut Buffer) {
|
|
||||||
/// self.render_ref(area, buf);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// # fn render(area: Rect, buf: &mut Buffer) {
|
|
||||||
/// let greeting = Greeting;
|
|
||||||
/// let farewell = Farewell;
|
|
||||||
///
|
|
||||||
/// // these calls do not consume the widgets, so they can be used again later
|
|
||||||
/// greeting.render_ref(area, buf);
|
|
||||||
/// farewell.render_ref(area, buf);
|
|
||||||
///
|
|
||||||
/// // a collection of widgets with different types
|
|
||||||
/// let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(greeting), Box::new(farewell)];
|
|
||||||
/// for widget in widgets {
|
|
||||||
/// widget.render_ref(area, buf);
|
|
||||||
/// }
|
|
||||||
/// # }
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[instability::unstable(feature = "widget-ref")]
|
|
||||||
pub trait WidgetRef {
|
|
||||||
/// Draws the current state of the widget in the given buffer. That is the only method required
|
|
||||||
/// to implement a custom widget.
|
|
||||||
fn render_ref(&self, area: Rect, buf: &mut Buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This allows you to render a widget by reference.
|
|
||||||
impl<W: WidgetRef> Widget for &W {
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
||||||
self.render_ref(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A blanket implementation of `WidgetExt` for `Option<W>` where `W` implements `WidgetRef`.
|
|
||||||
///
|
|
||||||
/// This is a convenience implementation that makes it easy to attach child widgets to parent
|
|
||||||
/// widgets. It allows you to render an optional widget by reference.
|
|
||||||
///
|
|
||||||
/// The internal widgets use this pattern to render the optional `Block` widgets that are included
|
|
||||||
/// on most widgets.
|
|
||||||
/// Blanket implementation of `WidgetExt` for `Option<W>` where `W` implements `WidgetRef`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
|
||||||
/// use ratatui::{
|
|
||||||
/// buffer::Buffer,
|
|
||||||
/// layout::Rect,
|
|
||||||
/// text::Line,
|
|
||||||
/// widgets::{Widget, WidgetRef},
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// struct Parent {
|
|
||||||
/// child: Option<Child>,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// struct Child;
|
|
||||||
///
|
|
||||||
/// impl WidgetRef for Child {
|
|
||||||
/// fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
/// Line::raw("Hello from child").render(area, buf);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl WidgetRef for Parent {
|
|
||||||
/// fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
/// self.child.render_ref(area, buf);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
impl<W: WidgetRef> WidgetRef for Option<W> {
|
|
||||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
if let Some(widget) = self {
|
|
||||||
widget.render_ref(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A `StatefulWidgetRef` is a trait that allows rendering a stateful widget by reference.
|
|
||||||
///
|
|
||||||
/// This is the stateful equivalent of `WidgetRef`. It is useful when you want to store a reference
|
|
||||||
/// to a stateful widget and render it later. It also allows you to render boxed stateful widgets.
|
|
||||||
///
|
|
||||||
/// This trait was introduced in Ratatui 0.26.0 and is implemented for all the internal stateful
|
|
||||||
/// widgets. It is currently marked as unstable as we are still evaluating the API and may make
|
|
||||||
/// changes in the future. See <https://github.com/ratatui/ratatui/issues/1287> for more
|
|
||||||
/// information.
|
|
||||||
///
|
|
||||||
/// A blanket implementation of `StatefulWidget` for `&W` where `W` implements `StatefulWidgetRef`
|
|
||||||
/// is provided.
|
|
||||||
///
|
|
||||||
/// See the documentation for [`WidgetRef`] for more information on boxed widgets.
|
|
||||||
/// See the documentation for [`StatefulWidget`] for more information on stateful widgets.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```rust
|
|
||||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
|
||||||
/// use ratatui::{
|
|
||||||
/// buffer::Buffer,
|
|
||||||
/// layout::Rect,
|
|
||||||
/// style::Stylize,
|
|
||||||
/// text::Line,
|
|
||||||
/// widgets::{StatefulWidget, StatefulWidgetRef, Widget},
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// struct PersonalGreeting;
|
|
||||||
///
|
|
||||||
/// impl StatefulWidgetRef for PersonalGreeting {
|
|
||||||
/// type State = String;
|
|
||||||
/// fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
|
||||||
/// Line::raw(format!("Hello {}", state)).render(area, buf);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl StatefulWidget for PersonalGreeting {
|
|
||||||
/// type State = String;
|
|
||||||
/// fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
|
||||||
/// (&self).render_ref(area, buf, state);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn render(area: Rect, buf: &mut Buffer) {
|
|
||||||
/// let widget = PersonalGreeting;
|
|
||||||
/// let mut state = "world".to_string();
|
|
||||||
/// widget.render(area, buf, &mut state);
|
|
||||||
/// }
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[instability::unstable(feature = "widget-ref")]
|
|
||||||
pub trait StatefulWidgetRef {
|
|
||||||
/// State associated with the stateful widget.
|
|
||||||
///
|
|
||||||
/// If you don't need this then you probably want to implement [`WidgetRef`] instead.
|
|
||||||
type State;
|
|
||||||
/// Draws the current state of the widget in the given buffer. That is the only method required
|
|
||||||
/// to implement a custom stateful widget.
|
|
||||||
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: while StatefulWidgetRef is marked as unstable, the blanket implementation of StatefulWidget
|
|
||||||
// cannot be implemented as W::State is effectively pub(crate) and not accessible from outside the
|
|
||||||
// crate. Once stabilized, this blanket implementation can be added and the specific implementations
|
|
||||||
// on Table and List can be removed.
|
|
||||||
//
|
|
||||||
// /// Blanket implementation of `StatefulWidget` for `&W` where `W` implements `StatefulWidgetRef`.
|
|
||||||
// ///
|
|
||||||
// /// This allows you to render a stateful widget by reference.
|
|
||||||
// impl<W: StatefulWidgetRef> StatefulWidget for &W {
|
|
||||||
// type State = W::State;
|
|
||||||
// fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
|
||||||
// StatefulWidgetRef::render_ref(self, area, buf, state);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Renders a string slice as a widget.
|
|
||||||
///
|
|
||||||
/// This implementation allows a string slice (`&str`) to act as a widget, meaning it can be drawn
|
|
||||||
/// onto a [`Buffer`] in a specified [`Rect`]. The slice represents a static string which can be
|
|
||||||
/// rendered by reference, thereby avoiding the need for string cloning or ownership transfer when
|
|
||||||
/// drawing the text to the screen.
|
|
||||||
impl Widget for &str {
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
||||||
self.render_ref(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provides the ability to render a string slice by reference.
|
|
||||||
///
|
|
||||||
/// This trait implementation ensures that a string slice, which is an immutable view over a
|
|
||||||
/// `String`, can be drawn on demand without requiring ownership of the string itself. It utilizes
|
|
||||||
/// the default text style when rendering onto the provided [`Buffer`] at the position defined by
|
|
||||||
/// [`Rect`].
|
|
||||||
impl WidgetRef for &str {
|
|
||||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renders a `String` object as a widget.
|
|
||||||
///
|
|
||||||
/// This implementation enables an owned `String` to be treated as a widget, which can be rendered
|
|
||||||
/// on a [`Buffer`] within the bounds of a given [`Rect`].
|
|
||||||
impl Widget for String {
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
||||||
self.render_ref(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provides the ability to render a `String` by reference.
|
|
||||||
///
|
|
||||||
/// This trait allows for a `String` to be rendered onto the [`Buffer`], similarly using the default
|
|
||||||
/// style settings. It ensures that an owned `String` can be rendered efficiently by reference,
|
|
||||||
/// without the need to give up ownership of the underlying text.
|
|
||||||
impl WidgetRef for String {
|
|
||||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use rstest::{fixture, rstest};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::text::Line;
|
|
||||||
|
|
||||||
#[fixture]
|
|
||||||
fn buf() -> Buffer {
|
|
||||||
Buffer::empty(Rect::new(0, 0, 20, 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
mod widget {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
struct Greeting;
|
|
||||||
|
|
||||||
impl Widget for Greeting {
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
|
||||||
Line::from("Hello").render(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render(mut buf: Buffer) {
|
|
||||||
let widget = Greeting;
|
|
||||||
widget.render(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod widget_ref {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
struct Greeting;
|
|
||||||
struct Farewell;
|
|
||||||
|
|
||||||
impl WidgetRef for Greeting {
|
|
||||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
Line::from("Hello").render(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WidgetRef for Farewell {
|
|
||||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
Line::from("Goodbye").right_aligned().render(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render_ref(mut buf: Buffer) {
|
|
||||||
let widget = Greeting;
|
|
||||||
widget.render_ref(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ensure that the blanket implementation of `Widget` for `&W` where `W` implements
|
|
||||||
/// `WidgetRef` works as expected.
|
|
||||||
#[rstest]
|
|
||||||
fn blanket_render(mut buf: Buffer) {
|
|
||||||
let widget = &Greeting;
|
|
||||||
widget.render(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn box_render_ref(mut buf: Buffer) {
|
|
||||||
let widget: Box<dyn WidgetRef> = Box::new(Greeting);
|
|
||||||
widget.render_ref(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn vec_box_render(mut buf: Buffer) {
|
|
||||||
let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(Greeting), Box::new(Farewell)];
|
|
||||||
for widget in widgets {
|
|
||||||
widget.render_ref(buf.area, &mut buf);
|
|
||||||
}
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["Hello Goodbye"]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[fixture]
|
|
||||||
fn state() -> String {
|
|
||||||
"world".to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
mod stateful_widget {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
struct PersonalGreeting;
|
|
||||||
|
|
||||||
impl StatefulWidget for PersonalGreeting {
|
|
||||||
type State = String;
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
|
||||||
Line::from(format!("Hello {state}")).render(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render(mut buf: Buffer, mut state: String) {
|
|
||||||
let widget = PersonalGreeting;
|
|
||||||
widget.render(buf.area, &mut buf, &mut state);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod stateful_widget_ref {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
struct PersonalGreeting;
|
|
||||||
|
|
||||||
impl StatefulWidgetRef for PersonalGreeting {
|
|
||||||
type State = String;
|
|
||||||
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
|
||||||
Line::from(format!("Hello {state}")).render(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render_ref(mut buf: Buffer, mut state: String) {
|
|
||||||
let widget = PersonalGreeting;
|
|
||||||
widget.render_ref(buf.area, &mut buf, &mut state);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note this cannot be tested until the blanket implementation of StatefulWidget for &W
|
|
||||||
// where W implements StatefulWidgetRef is added. (see the comment in the blanket
|
|
||||||
// implementation for more).
|
|
||||||
// /// This test is to ensure that the blanket implementation of `StatefulWidget` for `&W`
|
|
||||||
// where /// `W` implements `StatefulWidgetRef` works as expected.
|
|
||||||
// #[rstest]
|
|
||||||
// fn stateful_widget_blanket_render(mut buf: Buffer, mut state: String) {
|
|
||||||
// let widget = &PersonalGreeting;
|
|
||||||
// widget.render(buf.area, &mut buf, &mut state);
|
|
||||||
// assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
|
||||||
// }
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn box_render_render(mut buf: Buffer, mut state: String) {
|
|
||||||
let widget = Box::new(PersonalGreeting);
|
|
||||||
widget.render_ref(buf.area, &mut buf, &mut state);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod option_widget_ref {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
struct Greeting;
|
|
||||||
|
|
||||||
impl WidgetRef for Greeting {
|
|
||||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
|
||||||
Line::from("Hello").render(area, buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render_ref_some(mut buf: Buffer) {
|
|
||||||
let widget = Some(Greeting);
|
|
||||||
widget.render_ref(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render_ref_none(mut buf: Buffer) {
|
|
||||||
let widget: Option<Greeting> = None;
|
|
||||||
widget.render_ref(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines([" "]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod str {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render(mut buf: Buffer) {
|
|
||||||
"hello world".render(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render_area(mut buf: Buffer) {
|
|
||||||
let area = Rect::new(buf.area.x, buf.area.y, 11, buf.area.height);
|
|
||||||
"hello world, just hello".render(area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render_ref(mut buf: Buffer) {
|
|
||||||
"hello world".render_ref(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn option_render(mut buf: Buffer) {
|
|
||||||
Some("hello world").render(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn option_render_ref(mut buf: Buffer) {
|
|
||||||
Some("hello world").render_ref(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod string {
|
|
||||||
use super::*;
|
|
||||||
#[rstest]
|
|
||||||
fn render(mut buf: Buffer) {
|
|
||||||
String::from("hello world").render(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render_area(mut buf: Buffer) {
|
|
||||||
let area = Rect::new(buf.area.x, buf.area.y, 11, buf.area.height);
|
|
||||||
String::from("hello world, just hello").render(area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn render_ref(mut buf: Buffer) {
|
|
||||||
String::from("hello world").render_ref(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn option_render(mut buf: Buffer) {
|
|
||||||
Some(String::from("hello world")).render(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[rstest]
|
|
||||||
fn option_render_ref(mut buf: Buffer) {
|
|
||||||
Some(String::from("hello world")).render_ref(buf.area, &mut buf);
|
|
||||||
assert_eq!(buf, Buffer::with_lines(["hello world "]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user