mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-21 20:23:11 +00:00
chore: move unstable widget refs to ratatui (#1491)
These are less stable than the non-ref traits as we have not yet committed to the exact API. This change moves them to ratatui from ratatui-core. To facilitate this: - implementations of WidgetRef for all internal widgets are removed and replaced with implementations of Widget for references to those widgets. - Widget is now implemented for Option<W> where W: Widget, allowing for rendering of optional widgets. - The blanket implementation of Widget for WidgetRef is reversed, to be a blanket implementation of WidgetRef for all &W where W: Widget. BREAKING CHANGE: implementations of WidgetRef no longer have a blanket implementation of Widget, so Widgets should generally implement the Widget trait on a reference to the widget rather than implementing WidgetRef directly. This has the advantage of not requiring unstable features to be enabled. Part of: https://github.com/ratatui/ratatui/issues/1388 Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
This commit is contained in:
parent
46902f5587
commit
a41c97b413
29 changed files with 348 additions and 329 deletions
|
@ -76,6 +76,21 @@ This is a quick summary of the sections below:
|
|||
|
||||
## Unreleased (0.30.0)
|
||||
|
||||
### `WidgetRef` no longer has a blanket implementation of Widget
|
||||
|
||||
Previously there was a blanket implementation of Widget for WidgetRef. This has been reversed to
|
||||
instead be a blanket implementation of WidgetRef for all &W where W: Widget. Any widgets that
|
||||
previously implemented WidgetRef directly should now instead implement Widget for a reference to the
|
||||
type.
|
||||
|
||||
```diff
|
||||
-impl WidgetRef for Foo {
|
||||
- fn render_ref(&self, area: Rect, buf: &mut Buffer)
|
||||
+impl Widget for &Foo {
|
||||
+ fn render(self, area: Rect, buf: &mut Buffer)
|
||||
}
|
||||
```
|
||||
|
||||
### The `From` impls for backend types are now replaced with more specific traits [#1464]
|
||||
|
||||
[#1464]: https://github.com/ratatui/ratatui/pull/1464
|
||||
|
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2129,7 +2129,6 @@ dependencies = [
|
|||
"compact_str",
|
||||
"document-features",
|
||||
"indoc",
|
||||
"instability",
|
||||
"itertools 0.13.0",
|
||||
"lru",
|
||||
"palette",
|
||||
|
|
123
bacon.toml
123
bacon.toml
|
@ -12,54 +12,96 @@ command = ["cargo", "check", "--all-features", "--color", "always"]
|
|||
need_stdout = false
|
||||
|
||||
[jobs.check-all]
|
||||
command = ["cargo", "check", "--all-targets", "--all-features", "--color", "always"]
|
||||
command = [
|
||||
"cargo",
|
||||
"check",
|
||||
"--all-targets",
|
||||
"--all-features",
|
||||
"--color",
|
||||
"always",
|
||||
]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-crossterm]
|
||||
command = ["cargo", "check", "--color", "always", "--all-targets", "--no-default-features", "--features", "crossterm"]
|
||||
command = [
|
||||
"cargo",
|
||||
"check",
|
||||
"--color",
|
||||
"always",
|
||||
"--all-targets",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"crossterm",
|
||||
]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-termion]
|
||||
command = ["cargo", "check", "--color", "always", "--all-targets", "--no-default-features", "--features", "termion"]
|
||||
command = [
|
||||
"cargo",
|
||||
"check",
|
||||
"--color",
|
||||
"always",
|
||||
"--all-targets",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"termion",
|
||||
]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-termwiz]
|
||||
command = ["cargo", "check", "--color", "always", "--all-targets", "--no-default-features", "--features", "termwiz"]
|
||||
command = [
|
||||
"cargo",
|
||||
"check",
|
||||
"--color",
|
||||
"always",
|
||||
"--all-targets",
|
||||
"--no-default-features",
|
||||
"--features",
|
||||
"termwiz",
|
||||
]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.clippy]
|
||||
command = [
|
||||
"cargo", "clippy",
|
||||
"--all-targets",
|
||||
"--color", "always",
|
||||
]
|
||||
command = ["cargo", "clippy", "--all-targets", "--color", "always"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.test]
|
||||
command = [
|
||||
"cargo", "test",
|
||||
"cargo",
|
||||
"test",
|
||||
"--all-features",
|
||||
"--color", "always",
|
||||
"--", "--color", "always", # see https://github.com/Canop/bacon/issues/124
|
||||
"--color",
|
||||
"always",
|
||||
"--",
|
||||
"--color",
|
||||
"always", # see https://github.com/Canop/bacon/issues/124
|
||||
]
|
||||
need_stdout = true
|
||||
|
||||
[jobs.test-unit]
|
||||
command = [
|
||||
"cargo", "test",
|
||||
"cargo",
|
||||
"test",
|
||||
"--lib",
|
||||
"--all-features",
|
||||
"--color", "always",
|
||||
"--", "--color", "always", # see https://github.com/Canop/bacon/issues/124
|
||||
"--color",
|
||||
"always",
|
||||
"--",
|
||||
"--color",
|
||||
"always", # see https://github.com/Canop/bacon/issues/124
|
||||
]
|
||||
need_stdout = true
|
||||
|
||||
[jobs.doc]
|
||||
command = [
|
||||
"cargo", "+nightly", "doc",
|
||||
"-Zunstable-options", "-Zrustdoc-scrape-examples",
|
||||
"cargo",
|
||||
"+nightly",
|
||||
"doc",
|
||||
"-Zunstable-options",
|
||||
"-Zrustdoc-scrape-examples",
|
||||
"--all-features",
|
||||
"--color", "always",
|
||||
"--color",
|
||||
"always",
|
||||
"--no-deps",
|
||||
]
|
||||
env.RUSTDOCFLAGS = "--cfg docsrs"
|
||||
|
@ -69,10 +111,14 @@ need_stdout = false
|
|||
# to the previous job
|
||||
[jobs.doc-open]
|
||||
command = [
|
||||
"cargo", "+nightly", "doc",
|
||||
"-Zunstable-options", "-Zrustdoc-scrape-examples",
|
||||
"cargo",
|
||||
"+nightly",
|
||||
"doc",
|
||||
"-Zunstable-options",
|
||||
"-Zrustdoc-scrape-examples",
|
||||
"--all-features",
|
||||
"--color", "always",
|
||||
"--color",
|
||||
"always",
|
||||
"--no-deps",
|
||||
"--open",
|
||||
]
|
||||
|
@ -82,19 +128,40 @@ on_success = "job:doc" # so that we don't open the browser at each change
|
|||
|
||||
[jobs.coverage]
|
||||
command = [
|
||||
"cargo", "llvm-cov",
|
||||
"--lcov", "--output-path", "target/lcov.info",
|
||||
"cargo",
|
||||
"llvm-cov",
|
||||
"--lcov",
|
||||
"--output-path",
|
||||
"target/lcov.info",
|
||||
"--all-features",
|
||||
"--color", "always",
|
||||
"--color",
|
||||
"always",
|
||||
]
|
||||
|
||||
[jobs.coverage-unit-tests-only]
|
||||
command = [
|
||||
"cargo", "llvm-cov",
|
||||
"--lcov", "--output-path", "target/lcov.info",
|
||||
"cargo",
|
||||
"llvm-cov",
|
||||
"--lcov",
|
||||
"--output-path",
|
||||
"target/lcov.info",
|
||||
"--lib",
|
||||
"--all-features",
|
||||
"--color", "always",
|
||||
"--color",
|
||||
"always",
|
||||
]
|
||||
|
||||
[jobs.hack]
|
||||
command = [
|
||||
"cargo",
|
||||
"hack",
|
||||
"test",
|
||||
"--lib",
|
||||
"--each-feature",
|
||||
# "--all-targets",
|
||||
"--workspace",
|
||||
"--color",
|
||||
"always",
|
||||
]
|
||||
|
||||
# You may define here keybindings that would be specific to
|
||||
|
@ -102,7 +169,7 @@ command = [
|
|||
# Shortcuts to internal functions (scrolling, toggling, etc.)
|
||||
# should go in your personal global prefs.toml file instead.
|
||||
[keybindings]
|
||||
# alt-m = "job:my-job"
|
||||
ctrl-h = "job:hack"
|
||||
ctrl-c = "job:check-crossterm"
|
||||
ctrl-t = "job:check-termion"
|
||||
ctrl-w = "job:check-termwiz"
|
||||
|
|
|
@ -29,16 +29,6 @@ underline-color = []
|
|||
## 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"]
|
||||
|
@ -48,7 +38,6 @@ bitflags = "2.3"
|
|||
cassowary = "0.3"
|
||||
compact_str = "0.8.0"
|
||||
document-features = { workspace = true, optional = true }
|
||||
instability.workspace = true
|
||||
indoc.workspace = true
|
||||
itertools.workspace = true
|
||||
lru = "0.12.0"
|
||||
|
|
|
@ -501,80 +501,75 @@ mod tests {
|
|||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(ColorDebugKind::Foreground, Color::Black, ".black()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::Red, ".red()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::Green, ".green()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::Yellow, ".yellow()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::Blue, ".blue()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::Magenta, ".magenta()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::Cyan, ".cyan()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::Gray, ".gray()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::DarkGray, ".dark_gray()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::LightRed, ".light_red()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::LightGreen, ".light_green()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::LightYellow, ".light_yellow()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::LightBlue, ".light_blue()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::LightMagenta, ".light_magenta()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::LightCyan, ".light_cyan()")]
|
||||
#[case(ColorDebugKind::Foreground, Color::White, ".white()")]
|
||||
#[case(
|
||||
ColorDebugKind::Foreground,
|
||||
Color::Indexed(10),
|
||||
".fg(Color::Indexed(10))"
|
||||
)]
|
||||
#[case(
|
||||
ColorDebugKind::Foreground,
|
||||
Color::Rgb(255, 0, 0),
|
||||
".fg(Color::Rgb(255, 0, 0))"
|
||||
)]
|
||||
#[case(ColorDebugKind::Background, Color::Black, ".on_black()")]
|
||||
#[case(ColorDebugKind::Background, Color::Red, ".on_red()")]
|
||||
#[case(ColorDebugKind::Background, Color::Green, ".on_green()")]
|
||||
#[case(ColorDebugKind::Background, Color::Yellow, ".on_yellow()")]
|
||||
#[case(ColorDebugKind::Background, Color::Blue, ".on_blue()")]
|
||||
#[case(ColorDebugKind::Background, Color::Magenta, ".on_magenta()")]
|
||||
#[case(ColorDebugKind::Background, Color::Cyan, ".on_cyan()")]
|
||||
#[case(ColorDebugKind::Background, Color::Gray, ".on_gray()")]
|
||||
#[case(ColorDebugKind::Background, Color::DarkGray, ".on_dark_gray()")]
|
||||
#[case(ColorDebugKind::Background, Color::LightRed, ".on_light_red()")]
|
||||
#[case(ColorDebugKind::Background, Color::LightGreen, ".on_light_green()")]
|
||||
#[case(ColorDebugKind::Background, Color::LightYellow, ".on_light_yellow()")]
|
||||
#[case(ColorDebugKind::Background, Color::LightBlue, ".on_light_blue()")]
|
||||
#[case(ColorDebugKind::Background, Color::LightMagenta, ".on_light_magenta()")]
|
||||
#[case(ColorDebugKind::Background, Color::LightCyan, ".on_light_cyan()")]
|
||||
#[case(ColorDebugKind::Background, Color::White, ".on_white()")]
|
||||
#[case(
|
||||
ColorDebugKind::Background,
|
||||
Color::Indexed(10),
|
||||
".bg(Color::Indexed(10))"
|
||||
)]
|
||||
#[case(
|
||||
ColorDebugKind::Background,
|
||||
Color::Rgb(255, 0, 0),
|
||||
".bg(Color::Rgb(255, 0, 0))"
|
||||
)]
|
||||
#[case(Color::Black, ".black()")]
|
||||
#[case(Color::Red, ".red()")]
|
||||
#[case(Color::Green, ".green()")]
|
||||
#[case(Color::Yellow, ".yellow()")]
|
||||
#[case(Color::Blue, ".blue()")]
|
||||
#[case(Color::Magenta, ".magenta()")]
|
||||
#[case(Color::Cyan, ".cyan()")]
|
||||
#[case(Color::Gray, ".gray()")]
|
||||
#[case(Color::DarkGray, ".dark_gray()")]
|
||||
#[case(Color::LightRed, ".light_red()")]
|
||||
#[case(Color::LightGreen, ".light_green()")]
|
||||
#[case(Color::LightYellow, ".light_yellow()")]
|
||||
#[case(Color::LightBlue, ".light_blue()")]
|
||||
#[case(Color::LightMagenta, ".light_magenta()")]
|
||||
#[case(Color::LightCyan, ".light_cyan()")]
|
||||
#[case(Color::White, ".white()")]
|
||||
#[case(Color::Indexed(10), ".fg(Color::Indexed(10))")]
|
||||
#[case(Color::Rgb(255, 0, 0), ".fg(Color::Rgb(255, 0, 0))")]
|
||||
fn stylize_debug_foreground(#[case] color: Color, #[case] expected: &str) {
|
||||
let debug = color.stylize_debug(ColorDebugKind::Foreground);
|
||||
assert_eq!(format!("{debug:?}"), expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case(Color::Black, ".on_black()")]
|
||||
#[case(Color::Red, ".on_red()")]
|
||||
#[case(Color::Green, ".on_green()")]
|
||||
#[case(Color::Yellow, ".on_yellow()")]
|
||||
#[case(Color::Blue, ".on_blue()")]
|
||||
#[case(Color::Magenta, ".on_magenta()")]
|
||||
#[case(Color::Cyan, ".on_cyan()")]
|
||||
#[case(Color::Gray, ".on_gray()")]
|
||||
#[case(Color::DarkGray, ".on_dark_gray()")]
|
||||
#[case(Color::LightRed, ".on_light_red()")]
|
||||
#[case(Color::LightGreen, ".on_light_green()")]
|
||||
#[case(Color::LightYellow, ".on_light_yellow()")]
|
||||
#[case(Color::LightBlue, ".on_light_blue()")]
|
||||
#[case(Color::LightMagenta, ".on_light_magenta()")]
|
||||
#[case(Color::LightCyan, ".on_light_cyan()")]
|
||||
#[case(Color::White, ".on_white()")]
|
||||
#[case(Color::Indexed(10), ".bg(Color::Indexed(10))")]
|
||||
#[case(Color::Rgb(255, 0, 0), ".bg(Color::Rgb(255, 0, 0))")]
|
||||
fn stylize_debug_background(#[case] color: Color, #[case] expected: &str) {
|
||||
let debug = color.stylize_debug(ColorDebugKind::Background);
|
||||
assert_eq!(format!("{debug:?}"), expected);
|
||||
}
|
||||
|
||||
#[cfg(feature = "underline-color")]
|
||||
#[case(
|
||||
ColorDebugKind::Underline,
|
||||
Color::Black,
|
||||
".underline_color(Color::Black)"
|
||||
)]
|
||||
#[cfg(feature = "underline-color")]
|
||||
#[case(ColorDebugKind::Underline, Color::Red, ".underline_color(Color::Red)")]
|
||||
#[cfg(feature = "underline-color")]
|
||||
#[case(
|
||||
ColorDebugKind::Underline,
|
||||
Color::Green,
|
||||
".underline_color(Color::Green)"
|
||||
)]
|
||||
#[cfg(feature = "underline-color")]
|
||||
#[case(
|
||||
ColorDebugKind::Underline,
|
||||
Color::Yellow,
|
||||
".underline_color(Color::Yellow)"
|
||||
)]
|
||||
fn stylize_debug(#[case] kind: ColorDebugKind, #[case] color: Color, #[case] expected: &str) {
|
||||
let debug = color.stylize_debug(kind);
|
||||
#[rstest]
|
||||
#[case(Color::Black, ".underline_color(Color::Black)")]
|
||||
#[case(Color::Red, ".underline_color(Color::Red)")]
|
||||
#[case(Color::Green, ".underline_color(Color::Green)")]
|
||||
#[case(Color::Yellow, ".underline_color(Color::Yellow)")]
|
||||
#[case(Color::Blue, ".underline_color(Color::Blue)")]
|
||||
#[case(Color::Magenta, ".underline_color(Color::Magenta)")]
|
||||
#[case(Color::Cyan, ".underline_color(Color::Cyan)")]
|
||||
#[case(Color::Gray, ".underline_color(Color::Gray)")]
|
||||
#[case(Color::DarkGray, ".underline_color(Color::DarkGray)")]
|
||||
#[case(Color::LightRed, ".underline_color(Color::LightRed)")]
|
||||
#[case(Color::LightGreen, ".underline_color(Color::LightGreen)")]
|
||||
#[case(Color::LightYellow, ".underline_color(Color::LightYellow)")]
|
||||
#[case(Color::LightBlue, ".underline_color(Color::LightBlue)")]
|
||||
#[case(Color::LightMagenta, ".underline_color(Color::LightMagenta)")]
|
||||
#[case(Color::LightCyan, ".underline_color(Color::LightCyan)")]
|
||||
#[case(Color::White, ".underline_color(Color::White)")]
|
||||
#[case(Color::Indexed(10), ".underline_color(Color::Indexed(10))")]
|
||||
#[case(Color::Rgb(255, 0, 0), ".underline_color(Color::Rgb(255, 0, 0))")]
|
||||
fn stylize_debug_underline(#[case] color: Color, #[case] expected: &str) {
|
||||
let debug = color.stylize_debug(ColorDebugKind::Underline);
|
||||
assert_eq!(format!("{debug:?}"), expected);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
layout::{Alignment, Rect},
|
||||
style::{Style, Styled},
|
||||
text::{Span, StyledGrapheme, Text},
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
/// A line of text, consisting of one or more [`Span`]s.
|
||||
|
@ -683,21 +683,19 @@ impl<'a> Extend<Span<'a>> for Line<'a> {
|
|||
|
||||
impl Widget for Line<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Line<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Line<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_with_alignment(area, buf, None);
|
||||
}
|
||||
}
|
||||
|
||||
impl Line<'_> {
|
||||
/// An internal implementation method for `WidgetRef::render_ref`
|
||||
///
|
||||
/// Allows the parent widget to define a default alignment, to be
|
||||
/// used if `Line::alignment` is `None`.
|
||||
/// An internal implementation method for `Widget::render` that allows the parent widget to
|
||||
/// define a default alignment, to be used if `Line::alignment` is `None`.
|
||||
pub(crate) fn render_with_alignment(
|
||||
&self,
|
||||
area: Rect,
|
||||
|
@ -749,7 +747,7 @@ fn render_spans(spans: &[Span], mut area: Rect, buf: &mut Buffer, span_skip_widt
|
|||
if area.is_empty() {
|
||||
break;
|
||||
}
|
||||
span.render_ref(area, buf);
|
||||
span.render(area, buf);
|
||||
let span_width = u16::try_from(span_width).unwrap_or(u16::MAX);
|
||||
area = area.indent_x(span_width);
|
||||
}
|
||||
|
@ -1321,7 +1319,7 @@ mod tests {
|
|||
"🦀 RFC8628 OAuth 2.0 Device Authorization GrantでCLIからGithubのaccess tokenを取得する"
|
||||
);
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, 83, 1));
|
||||
line.render_ref(buf.area, &mut buf);
|
||||
line.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([
|
||||
"🦀 RFC8628 OAuth 2.0 Device Authorization GrantでCLIからGithubのaccess tokenを取得 "
|
||||
]));
|
||||
|
@ -1358,7 +1356,7 @@ mod tests {
|
|||
) {
|
||||
let line = Line::from("1234🦀7890").alignment(alignment);
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, buf_width, 1));
|
||||
line.render_ref(buf.area, &mut buf);
|
||||
line.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([expected]));
|
||||
}
|
||||
|
||||
|
@ -1411,7 +1409,7 @@ mod tests {
|
|||
};
|
||||
let line = Line::from(value).centered();
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, buf_width, 1));
|
||||
line.render_ref(buf.area, &mut buf);
|
||||
line.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([expected]));
|
||||
}
|
||||
|
||||
|
@ -1429,7 +1427,7 @@ mod tests {
|
|||
// Fill buffer with stuff to ensure the output is indeed padded
|
||||
let mut buf = Buffer::filled(Rect::new(0, 0, 10, 1), Cell::new("X"));
|
||||
let area = Rect::new(2, 0, 6, 1);
|
||||
line.render_ref(area, &mut buf);
|
||||
line.render(area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([expected]));
|
||||
}
|
||||
|
||||
|
@ -1447,7 +1445,7 @@ mod tests {
|
|||
let area = Rect::new(0, 0, buf_width, 1);
|
||||
// Fill buffer with stuff to ensure the output is indeed padded
|
||||
let mut buf = Buffer::filled(area, Cell::new("X"));
|
||||
line.render_ref(buf.area, &mut buf);
|
||||
line.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([expected]));
|
||||
}
|
||||
|
||||
|
@ -1478,7 +1476,7 @@ mod tests {
|
|||
fn render_truncates_flag(#[case] buf_width: u16, #[case] expected: &str) {
|
||||
let line = Line::from("🇺🇸1234");
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, buf_width, 1));
|
||||
line.render_ref(buf.area, &mut buf);
|
||||
line.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([expected]));
|
||||
}
|
||||
|
||||
|
@ -1502,7 +1500,7 @@ mod tests {
|
|||
assert!(line.width() >= min_width);
|
||||
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, 32, 1));
|
||||
line.render_ref(buf.area, &mut buf);
|
||||
line.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([expected]));
|
||||
}
|
||||
|
||||
|
@ -1526,7 +1524,7 @@ mod tests {
|
|||
assert!(line.width() >= min_width);
|
||||
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, 32, 1));
|
||||
line.render_ref(buf.area, &mut buf);
|
||||
line.render(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([expected]));
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
layout::Rect,
|
||||
style::{Style, Styled},
|
||||
text::{Line, StyledGrapheme},
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
/// Represents a part of a line that is contiguous and where all characters share the same style.
|
||||
|
@ -418,12 +418,12 @@ impl<'a> Styled for Span<'a> {
|
|||
|
||||
impl Widget for Span<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Span<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Span<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let area = area.intersection(buf.area);
|
||||
if area.is_empty() {
|
||||
return;
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
layout::{Alignment, Rect},
|
||||
style::{Style, Styled},
|
||||
text::{Line, Span},
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
/// A string split over one or more lines.
|
||||
|
@ -730,12 +730,12 @@ impl fmt::Display for Text<'_> {
|
|||
|
||||
impl Widget for Text<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Text<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Text<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let area = area.intersection(buf.area);
|
||||
buf.set_style(area, self.style);
|
||||
for (line, line_area) in self.iter().zip(area.rows()) {
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
//! render UI elements on the screen.
|
||||
|
||||
pub use self::{stateful_widget::StatefulWidget, widget::Widget};
|
||||
#[instability::unstable(feature = "widget-ref")]
|
||||
pub use self::{stateful_widget_ref::StatefulWidgetRef, widget_ref::WidgetRef};
|
||||
|
||||
mod stateful_widget;
|
||||
mod stateful_widget_ref;
|
||||
mod widget;
|
||||
mod widget_ref;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use super::WidgetRef;
|
||||
use crate::{buffer::Buffer, layout::Rect};
|
||||
use crate::{buffer::Buffer, layout::Rect, style::Style};
|
||||
|
||||
/// A `Widget` is a type that can be drawn on a [`Buffer`] in a given [`Rect`].
|
||||
///
|
||||
|
@ -11,7 +10,7 @@ use crate::{buffer::Buffer, layout::Rect};
|
|||
/// 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
|
||||
/// 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.
|
||||
|
@ -72,7 +71,7 @@ pub trait Widget {
|
|||
/// drawing the text to the screen.
|
||||
impl Widget for &str {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,7 +81,15 @@ impl Widget for &str {
|
|||
/// 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);
|
||||
buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Widget> Widget for Option<W> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
if let Some(widget) = self {
|
||||
widget.render(area, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,9 +15,6 @@ edition.workspace = true
|
|||
rust-version.workspace = true
|
||||
|
||||
[features]
|
||||
## enables `unstable-widget-ref` as default
|
||||
default = ["unstable-widget-ref"]
|
||||
|
||||
## 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", "ratatui-core/serde"]
|
||||
|
@ -32,11 +29,7 @@ all-widgets = ["calendar"]
|
|||
calendar = ["dep:time"]
|
||||
|
||||
## Enable all unstable features.
|
||||
unstable = ["unstable-rendered-line-info", "unstable-widget-ref"]
|
||||
|
||||
## enables the [`WidgetRef`](ratatui_core::widgets::WidgetRef) and
|
||||
## [`StatefulWidgetRef`](ratatui_core::widgets::StatefulWidgetRef) traits which are experimental and may change
|
||||
unstable-widget-ref = ["ratatui-core/unstable-widget-ref"]
|
||||
unstable = ["unstable-rendered-line-info"]
|
||||
|
||||
## Enables the [`Paragraph::line_count`](paragraph::Paragraph::line_count)
|
||||
## [`Paragraph::line_width`](paragraph::Paragraph::line_width) methods
|
||||
|
|
|
@ -6,7 +6,7 @@ use ratatui_core::{
|
|||
style::{Style, Styled},
|
||||
symbols::{self},
|
||||
text::Line,
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
pub use self::{bar::Bar, bar_group::BarGroup};
|
||||
|
@ -600,15 +600,15 @@ impl BarChart<'_> {
|
|||
|
||||
impl Widget for BarChart<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for BarChart<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &BarChart<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
buf.set_style(area, self.style);
|
||||
|
||||
self.block.render_ref(area, buf);
|
||||
self.block.as_ref().render(area, buf);
|
||||
let inner = self.block.inner_if_some(area);
|
||||
|
||||
if inner.is_empty() || self.data.is_empty() || self.bar_width == 0 {
|
||||
|
|
|
@ -12,7 +12,7 @@ use ratatui_core::{
|
|||
style::{Style, Styled},
|
||||
symbols::border,
|
||||
text::Line,
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
pub use self::{
|
||||
|
@ -603,12 +603,12 @@ impl<'a> Block<'a> {
|
|||
|
||||
impl Widget for Block<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Block<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Block<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let area = area.intersection(buf.area);
|
||||
if area.is_empty() {
|
||||
return;
|
||||
|
@ -744,7 +744,7 @@ impl Block<'_> {
|
|||
..titles_area
|
||||
};
|
||||
buf.set_style(title_area, self.titles_style);
|
||||
title.render_ref(title_area, buf);
|
||||
title.render(title_area, buf);
|
||||
|
||||
// bump the width of the titles area to the left
|
||||
titles_area.width = titles_area
|
||||
|
@ -785,7 +785,7 @@ impl Block<'_> {
|
|||
..titles_area
|
||||
};
|
||||
buf.set_style(title_area, self.titles_style);
|
||||
title.render_ref(title_area, buf);
|
||||
title.render(title_area, buf);
|
||||
|
||||
// bump the titles area to the right and reduce its width
|
||||
titles_area.x = titles_area.x.saturating_add(title_width + 1);
|
||||
|
@ -808,7 +808,7 @@ impl Block<'_> {
|
|||
..titles_area
|
||||
};
|
||||
buf.set_style(title_area, self.titles_style);
|
||||
title.render_ref(title_area, buf);
|
||||
title.render(title_area, buf);
|
||||
|
||||
// bump the titles area to the right and reduce its width
|
||||
titles_area.x = titles_area.x.saturating_add(title_width + 1);
|
||||
|
|
|
@ -15,7 +15,7 @@ use ratatui_core::{
|
|||
layout::{Alignment, Constraint, Layout, Rect},
|
||||
style::Style,
|
||||
text::{Line, Span},
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
use time::{Date, Duration, OffsetDateTime};
|
||||
|
||||
|
@ -136,13 +136,13 @@ impl<'a, DS: DateStyler> Monthly<'a, DS> {
|
|||
|
||||
impl<DS: DateStyler> Widget for Monthly<'_, DS> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl<DS: DateStyler> WidgetRef for Monthly<'_, DS> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
self.block.render_ref(area, buf);
|
||||
impl<DS: DateStyler> Widget for &Monthly<'_, DS> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.block.as_ref().render(area, buf);
|
||||
let inner = self.block.inner_if_some(area);
|
||||
self.render_monthly(inner, buf);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ use ratatui_core::{
|
|||
style::{Color, Style},
|
||||
symbols::{self, Marker},
|
||||
text::Line as TextLine,
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
pub use self::{
|
||||
|
@ -747,16 +747,16 @@ where
|
|||
F: Fn(&mut Context),
|
||||
{
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> WidgetRef for Canvas<'_, F>
|
||||
impl<F> Widget for &Canvas<'_, F>
|
||||
where
|
||||
F: Fn(&mut Context),
|
||||
{
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
self.block.render_ref(area, buf);
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.block.as_ref().render(area, buf);
|
||||
let canvas_area = self.block.inner_if_some(area);
|
||||
if canvas_area.is_empty() {
|
||||
return;
|
||||
|
|
|
@ -7,7 +7,7 @@ use ratatui_core::{
|
|||
style::{Color, Style, Styled},
|
||||
symbols::{self},
|
||||
text::Line,
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
use strum::{Display, EnumString};
|
||||
|
||||
|
@ -971,16 +971,16 @@ impl<'a> Chart<'a> {
|
|||
|
||||
impl Widget for Chart<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Chart<'_> {
|
||||
impl Widget for &Chart<'_> {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
buf.set_style(area, self.style);
|
||||
|
||||
self.block.render_ref(area, buf);
|
||||
self.block.as_ref().render(area, buf);
|
||||
let chart_area = self.block.inner_if_some(area);
|
||||
let Some(layout) = self.layout(chart_area) else {
|
||||
return;
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
//! The [`Clear`] widget allows you to clear a certain area to allow overdrawing (e.g. for popups).
|
||||
use ratatui_core::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
widgets::{Widget, WidgetRef},
|
||||
};
|
||||
use ratatui_core::{buffer::Buffer, layout::Rect, widgets::Widget};
|
||||
|
||||
/// A widget to clear/reset a certain area to allow overdrawing (e.g. for popups).
|
||||
///
|
||||
|
@ -35,12 +31,12 @@ pub struct Clear;
|
|||
|
||||
impl Widget for Clear {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Clear {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Clear {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
for x in area.left()..area.right() {
|
||||
for y in area.top()..area.bottom() {
|
||||
buf[(x, y)].reset();
|
||||
|
|
|
@ -5,7 +5,7 @@ use ratatui_core::{
|
|||
style::{Color, Style, Styled},
|
||||
symbols::{self},
|
||||
text::{Line, Span},
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
use crate::block::{Block, BlockExt};
|
||||
|
@ -153,14 +153,14 @@ impl<'a> Gauge<'a> {
|
|||
|
||||
impl Widget for Gauge<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Gauge<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Gauge<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
buf.set_style(area, self.style);
|
||||
self.block.render_ref(area, buf);
|
||||
self.block.as_ref().render(area, buf);
|
||||
let inner = self.block.inner_if_some(area);
|
||||
self.render_gauge(inner, buf);
|
||||
}
|
||||
|
@ -386,14 +386,14 @@ impl<'a> LineGauge<'a> {
|
|||
|
||||
impl Widget for LineGauge<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for LineGauge<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &LineGauge<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
buf.set_style(area, self.style);
|
||||
self.block.render_ref(area, buf);
|
||||
self.block.as_ref().render(area, buf);
|
||||
let gauge_area = self.block.inner_if_some(area);
|
||||
if gauge_area.is_empty() {
|
||||
return;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use ratatui_core::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
widgets::{StatefulWidget, StatefulWidgetRef, Widget, WidgetRef},
|
||||
widgets::{StatefulWidget, Widget},
|
||||
};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
|
@ -12,14 +12,14 @@ use crate::{
|
|||
|
||||
impl Widget for List<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
WidgetRef::render_ref(&self, area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for List<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &List<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let mut state = ListState::default();
|
||||
StatefulWidgetRef::render_ref(self, area, buf, &mut state);
|
||||
StatefulWidget::render(self, area, buf, &mut state);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,24 +27,16 @@ impl StatefulWidget for List<'_> {
|
|||
type State = ListState;
|
||||
|
||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
StatefulWidgetRef::render_ref(&self, area, buf, state);
|
||||
StatefulWidget::render(&self, area, buf, state);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: remove this when StatefulWidgetRef is stabilized and replace with the blanket impl
|
||||
impl StatefulWidget for &List<'_> {
|
||||
type State = ListState;
|
||||
|
||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
StatefulWidgetRef::render_ref(self, area, buf, state);
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulWidgetRef for List<'_> {
|
||||
type State = ListState;
|
||||
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
buf.set_style(area, self.style);
|
||||
self.block.render_ref(area, buf);
|
||||
self.block.as_ref().render(area, buf);
|
||||
let list_area = self.block.inner_if_some(area);
|
||||
|
||||
if list_area.is_empty() {
|
||||
|
@ -113,7 +105,7 @@ impl StatefulWidgetRef for List<'_> {
|
|||
} else {
|
||||
row_area
|
||||
};
|
||||
item.content.render_ref(item_area, buf);
|
||||
Widget::render(&item.content, item_area, buf);
|
||||
|
||||
for j in 0..item.content.height() {
|
||||
// if the item is selected, we need to display the highlight symbol:
|
||||
|
|
|
@ -5,7 +5,7 @@ use ratatui_core::{
|
|||
layout::{Alignment, Position, Rect},
|
||||
style::{Style, Styled},
|
||||
text::{Line, StyledGrapheme, Text},
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
|
@ -419,14 +419,14 @@ impl<'a> Paragraph<'a> {
|
|||
|
||||
impl Widget for Paragraph<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Paragraph<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Paragraph<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
buf.set_style(area, self.style);
|
||||
self.block.render_ref(area, buf);
|
||||
self.block.as_ref().render(area, buf);
|
||||
let inner = self.block.inner_if_some(area);
|
||||
self.render_paragraph(inner, buf);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use ratatui_core::{
|
|||
layout::Rect,
|
||||
style::{Style, Styled},
|
||||
symbols::{self},
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
use strum::{Display, EnumString};
|
||||
|
||||
|
@ -326,13 +326,13 @@ impl<'a> Styled for Sparkline<'a> {
|
|||
|
||||
impl Widget for Sparkline<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Sparkline<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
self.block.render_ref(area, buf);
|
||||
impl Widget for &Sparkline<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.block.as_ref().render(area, buf);
|
||||
let inner = self.block.inner_if_some(area);
|
||||
self.render_sparkline(inner, buf);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use ratatui_core::{
|
|||
layout::{Constraint, Flex, Layout, Rect},
|
||||
style::{Style, Styled},
|
||||
text::Text,
|
||||
widgets::{StatefulWidget, StatefulWidgetRef, Widget, WidgetRef},
|
||||
widgets::{StatefulWidget, Widget},
|
||||
};
|
||||
|
||||
pub use self::{cell::Cell, highlight_spacing::HighlightSpacing, row::Row, state::TableState};
|
||||
|
@ -752,12 +752,12 @@ impl<'a> Table<'a> {
|
|||
|
||||
impl Widget for Table<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
WidgetRef::render_ref(&self, area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Table<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Table<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let mut state = TableState::default();
|
||||
StatefulWidget::render(self, area, buf, &mut state);
|
||||
}
|
||||
|
@ -771,20 +771,12 @@ impl StatefulWidget for Table<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
// Note: remove this when StatefulWidgetRef is stabilized and replace with the blanket impl
|
||||
impl StatefulWidget for &Table<'_> {
|
||||
type State = TableState;
|
||||
|
||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
StatefulWidgetRef::render_ref(self, area, buf, state);
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulWidgetRef for Table<'_> {
|
||||
type State = TableState;
|
||||
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
buf.set_style(area, self.style);
|
||||
self.block.render_ref(area, buf);
|
||||
self.block.as_ref().render(area, buf);
|
||||
let table_area = self.block.inner_if_some(area);
|
||||
if table_area.is_empty() {
|
||||
return;
|
||||
|
@ -910,7 +902,7 @@ impl Table<'_> {
|
|||
..row_area
|
||||
};
|
||||
buf.set_style(selection_area, row.style);
|
||||
highlight_symbol.render_ref(selection_area, buf);
|
||||
highlight_symbol.render(selection_area, buf);
|
||||
};
|
||||
for ((x, width), cell) in columns_widths.iter().zip(row.cells.iter()) {
|
||||
cell.render(
|
||||
|
|
|
@ -3,7 +3,7 @@ use ratatui_core::{
|
|||
layout::Rect,
|
||||
style::{Style, Styled},
|
||||
text::Text,
|
||||
widgets::WidgetRef,
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
/// A [`Cell`] contains the [`Text`] to be displayed in a [`Row`] of a [`Table`].
|
||||
|
@ -163,7 +163,7 @@ impl<'a> Cell<'a> {
|
|||
impl Cell<'_> {
|
||||
pub(crate) fn render(&self, area: Rect, buf: &mut Buffer) {
|
||||
buf.set_style(area, self.style);
|
||||
self.content.render_ref(area, buf);
|
||||
Widget::render(&self.content, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use ratatui_core::{
|
|||
style::{Modifier, Style, Styled},
|
||||
symbols::{self},
|
||||
text::{Line, Span},
|
||||
widgets::{Widget, WidgetRef},
|
||||
widgets::Widget,
|
||||
};
|
||||
|
||||
use crate::block::{Block, BlockExt};
|
||||
|
@ -364,14 +364,14 @@ impl<'a> Styled for Tabs<'a> {
|
|||
|
||||
impl Widget for Tabs<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
self.render_ref(area, buf);
|
||||
Widget::render(&self, area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetRef for Tabs<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Tabs<'_> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
buf.set_style(area, self.style);
|
||||
self.block.render_ref(area, buf);
|
||||
self.block.as_ref().render(area, buf);
|
||||
let inner = self.block.inner_if_some(area);
|
||||
self.render_tabs(inner, buf);
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ indoc = "2"
|
|||
instability.workspace = true
|
||||
itertools.workspace = true
|
||||
palette = { version = "0.7.6", optional = true }
|
||||
ratatui-core = { workspace = true, features = ["unstable-widget-ref"] }
|
||||
ratatui-widgets = { workspace = true, features = ["unstable-widget-ref"] }
|
||||
ratatui-core = { workspace = true }
|
||||
ratatui-widgets = { workspace = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
strum.workspace = true
|
||||
termwiz = { version = "0.22.0", optional = true }
|
||||
|
@ -163,7 +163,7 @@ unstable-rendered-line-info = ["ratatui-widgets/unstable-rendered-line-info"]
|
|||
##
|
||||
## [`WidgetRef`]: widgets::WidgetRef
|
||||
## [`StatefulWidgetRef`]: widgets::StatefulWidgetRef
|
||||
unstable-widget-ref = ["ratatui-core/unstable-widget-ref"]
|
||||
unstable-widget-ref = []
|
||||
|
||||
## Enables getting access to backends' writers.
|
||||
unstable-backend-writer = []
|
||||
|
|
|
@ -113,7 +113,7 @@ impl Frame<'_> {
|
|||
///
|
||||
/// let block = Block::new();
|
||||
/// let area = Rect::new(0, 0, 5, 5);
|
||||
/// frame.render_widget_ref(block, area);
|
||||
/// frame.render_widget_ref(&block, area);
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
|
@ -181,7 +181,7 @@ impl Frame<'_> {
|
|||
/// let mut state = ListState::default().with_selected(Some(1));
|
||||
/// let list = List::new(vec![ListItem::new("Item 1"), ListItem::new("Item 2")]);
|
||||
/// let area = Rect::new(0, 0, 5, 5);
|
||||
/// frame.render_stateful_widget_ref(list, area, &mut state);
|
||||
/// frame.render_stateful_widget_ref(&list, area, &mut state);
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
|
|
|
@ -29,8 +29,6 @@
|
|||
//! [`Canvas`]: crate::widgets::canvas::Canvas
|
||||
|
||||
pub use ratatui_core::widgets::{StatefulWidget, Widget};
|
||||
#[instability::unstable(feature = "widget-ref")]
|
||||
pub use ratatui_core::widgets::{StatefulWidgetRef, WidgetRef};
|
||||
// TODO remove this module once title etc. are gone
|
||||
pub use ratatui_widgets::block;
|
||||
#[cfg(feature = "widget-calendar")]
|
||||
|
@ -51,3 +49,8 @@ pub use ratatui_widgets::{
|
|||
table::{Cell, HighlightSpacing, Row, Table, TableState},
|
||||
tabs::Tabs,
|
||||
};
|
||||
#[instability::unstable(feature = "widget-ref")]
|
||||
pub use {stateful_widget_ref::StatefulWidgetRef, widget_ref::WidgetRef};
|
||||
|
||||
mod stateful_widget_ref;
|
||||
mod widget_ref;
|
||||
|
|
|
@ -1,31 +1,34 @@
|
|||
use ratatui_core::widgets::StatefulWidget;
|
||||
|
||||
use crate::{buffer::Buffer, layout::Rect};
|
||||
|
||||
/// 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
|
||||
/// This is the stateful equivalent of `WidgetRef`. It is useful when you need 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.
|
||||
/// This trait was introduced in Ratatui 0.26.0. 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.
|
||||
/// A blanket implementation of `StatefulWidgetRef` for `&W` where `W` implements `StatefulWidget`
|
||||
/// is provided. Most of the time you will want to implement `StatefulWidget` against a reference to
|
||||
/// the widget instead of implementing `StatefulWidgetRef` directly.
|
||||
///
|
||||
/// See the documentation for [`WidgetRef`] for more information on boxed widgets.
|
||||
/// See the documentation for [`StatefulWidget`] for more information on stateful widgets.
|
||||
/// 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::widgets::StatefulWidgetRef;
|
||||
/// use ratatui_core::{
|
||||
/// buffer::Buffer,
|
||||
/// layout::Rect,
|
||||
/// style::Stylize,
|
||||
/// text::Line,
|
||||
/// widgets::{StatefulWidget, StatefulWidgetRef, Widget},
|
||||
/// widgets::{StatefulWidget, Widget},
|
||||
/// };
|
||||
///
|
||||
/// struct PersonalGreeting;
|
||||
|
@ -64,20 +67,18 @@ pub trait StatefulWidgetRef {
|
|||
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);
|
||||
// }
|
||||
// }
|
||||
/// Blanket implementation of `StatefulWidgetRef` for `&W` where `W` implements `StatefulWidget`.
|
||||
///
|
||||
/// This allows you to render a stateful widget by reference.
|
||||
impl<W, State> StatefulWidgetRef for &W
|
||||
where
|
||||
for<'a> &'a W: StatefulWidget<State = State>,
|
||||
{
|
||||
type State = State;
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
self.render(area, buf, state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
@ -98,35 +99,23 @@ mod tests {
|
|||
|
||||
struct PersonalGreeting;
|
||||
|
||||
impl StatefulWidgetRef for PersonalGreeting {
|
||||
impl StatefulWidget for &PersonalGreeting {
|
||||
type State = String;
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||
fn render(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;
|
||||
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);
|
||||
fn box_render_ref(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 "]));
|
||||
}
|
|
@ -24,12 +24,8 @@ use crate::{buffer::Buffer, layout::Rect, style::Style};
|
|||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||
/// use ratatui_core::{
|
||||
/// buffer::Buffer,
|
||||
/// layout::Rect,
|
||||
/// text::Line,
|
||||
/// widgets::{Widget, WidgetRef},
|
||||
/// };
|
||||
/// use ratatui::widgets::WidgetRef;
|
||||
/// use ratatui_core::{buffer::Buffer, layout::Rect, text::Line, widgets::Widget};
|
||||
///
|
||||
/// struct Greeting;
|
||||
///
|
||||
|
@ -85,9 +81,12 @@ pub trait WidgetRef {
|
|||
}
|
||||
|
||||
/// 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);
|
||||
impl<W> WidgetRef for &W
|
||||
where
|
||||
for<'a> &'a W: Widget,
|
||||
{
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
self.render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,12 +126,8 @@ impl WidgetRef for String {
|
|||
///
|
||||
/// ```rust
|
||||
/// # #[cfg(feature = "unstable-widget-ref")] {
|
||||
/// use ratatui_core::{
|
||||
/// buffer::Buffer,
|
||||
/// layout::Rect,
|
||||
/// text::Line,
|
||||
/// widgets::{Widget, WidgetRef},
|
||||
/// };
|
||||
/// use ratatui::widgets::WidgetRef;
|
||||
/// use ratatui_core::{buffer::Buffer, layout::Rect, text::Line, widgets::Widget};
|
||||
///
|
||||
/// struct Parent {
|
||||
/// child: Option<Child>,
|
||||
|
@ -177,44 +172,37 @@ mod tests {
|
|||
|
||||
struct Farewell;
|
||||
|
||||
impl WidgetRef for Greeting {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
impl Widget for &Greeting {
|
||||
fn render(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) {
|
||||
impl Widget for &Farewell {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
Line::from("Goodbye").right_aligned().render(area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the blanket implementation of `WidgetRef` for `&W` where `W` implements
|
||||
/// `Widget` works as expected.
|
||||
#[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 render_widget(mut buf: Buffer) {
|
||||
let widget = &Greeting;
|
||||
widget.render(buf.area, &mut buf);
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_ref_box(mut buf: Buffer) {
|
||||
let widget: Box<dyn WidgetRef> = Box::new(Greeting);
|
||||
let widget: Box<dyn WidgetRef> = Box::new(&Greeting);
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines(["Hello "]));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_ref_box_vec(mut buf: Buffer) {
|
||||
let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(Greeting), Box::new(Farewell)];
|
||||
let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(&Greeting), Box::new(&Farewell)];
|
||||
for widget in widgets {
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
}
|
||||
|
@ -223,14 +211,14 @@ mod tests {
|
|||
|
||||
#[rstest]
|
||||
fn render_ref_some(mut buf: Buffer) {
|
||||
let widget = Some(Greeting);
|
||||
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;
|
||||
let widget: Option<&Greeting> = None;
|
||||
widget.render_ref(buf.area, &mut buf);
|
||||
assert_eq!(buf, Buffer::with_lines([" "]));
|
||||
}
|
Loading…
Reference in a new issue