feat: add WidgetRef and StatefulWidgetRef traits (#903)

The Widget trait consumes self, which makes it impossible to use in a
boxed context. Previously we implemented the Widget trait for &T, but
this was not enough to render a boxed widget. We now have a new trait
called `WidgetRef` that allows rendering a widget by reference. This
trait is useful when you want to store a reference to one or more
widgets and render them later. Additionaly this makes it possible to
render boxed widgets where the type is not known at compile time (e.g.
in a composite layout with multiple panes of different types).

This change also adds a new trait called `StatefulWidgetRef` which is
the stateful equivalent of `WidgetRef`.

Both new traits are gated behind the `unstable-widget-ref` feature flag
as we may change the exact name / approach a little on this based on
further discussion.

Blanket implementation of `Widget` for `&W` where `W` implements
`WidgetRef` and `StatefulWidget` for `&W` where `W` implements
`StatefulWidgetRef` is provided. This allows you to render a widget by
reference and a stateful widget by reference.

A blanket implementation of `WidgetRef` for `Option<W>` where `W`
implements `WidgetRef` is provided. This makes it easier to render
child widgets that are optional without the boilerplate of unwrapping
the option. Previously several widgets implemented this manually. This
commits expands the pattern to apply to all widgets.

```rust
struct Parent {
    child: Option<Child>,
}

impl WidgetRef for Parent {
    fn render_ref(&self, area: Rect, buf: &mut Buffer) {
        self.child.render_ref(area, buf);
    }
}
```

```rust
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"]));
```
This commit is contained in:
Josh McKinney 2024-02-02 00:02:16 -08:00 committed by GitHub
parent 652dc469ea
commit c8dd87918d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 483 additions and 132 deletions

View file

@ -100,7 +100,7 @@ underline-color = ["dep:crossterm"]
#! The following features are unstable and may change in the future:
## Enable all unstable features.
unstable = ["unstable-rendered-line-info"]
unstable = ["unstable-rendered-line-info", "unstable-widget-ref"]
## Enables the [`Paragraph::line_count`](crate::widgets::Paragraph::line_count)
## [`Paragraph::line_width`](crate::widgets::Paragraph::line_width) methods
@ -108,6 +108,10 @@ unstable = ["unstable-rendered-line-info"]
## See [Issue 293](https://github.com/ratatui-org/ratatui/issues/293) for more details.
unstable-rendered-line-info = []
## Enables the `WidgetRef` and `StatefulWidgetRef` traits which are experimental and may change in
## the future.
unstable-widget-ref = []
[package.metadata.docs.rs]
all-features = true
# see https://doc.rust-lang.org/nightly/rustdoc/scraped-examples.html

View file

@ -23,6 +23,7 @@ pub use crate::backend::CrosstermBackend;
pub use crate::backend::TermionBackend;
#[cfg(feature = "termwiz")]
pub use crate::backend::TermwizBackend;
pub(crate) use crate::widgets::{StatefulWidgetRef, WidgetRef};
pub use crate::{
backend::{self, Backend},
buffer::{self, Buffer},

View file

@ -1,4 +1,4 @@
use crate::prelude::*;
use crate::{prelude::*, widgets::WidgetRef};
/// A consistent view into the terminal state for rendering a single frame.
///
@ -75,6 +75,27 @@ impl Frame<'_> {
widget.render(area, self.buffer);
}
/// Render a [`WidgetRef`] to the current buffer using [`WidgetRef::render_ref`].
///
/// Usually the area argument is the size of the current frame or a sub-area of the current
/// frame (which can be obtained using [`Layout`] to split the total area).
///
/// # Example
///
/// ```rust
/// # use ratatui::{backend::TestBackend, prelude::*, widgets::Block};
/// # let backend = TestBackend::new(5, 5);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// # let mut frame = terminal.get_frame();
/// let block = Block::default();
/// let area = Rect::new(0, 0, 5, 5);
/// frame.render_widget_ref(block, area);
/// ```
#[stability::unstable(feature = "widget-ref")]
pub fn render_widget_ref<W: WidgetRef>(&mut self, widget: W, area: Rect) {
widget.render_ref(area, self.buffer);
}
/// Render a [`StatefulWidget`] to the current buffer using [`StatefulWidget::render`].
///
/// Usually the area argument is the size of the current frame or a sub-area of the current
@ -83,7 +104,7 @@ impl Frame<'_> {
/// The last argument should be an instance of the [`StatefulWidget::State`] associated to the
/// given [`StatefulWidget`].
///
/// # Examples
/// # Example
///
/// ```rust
/// # use ratatui::{backend::TestBackend, prelude::*, widgets::*};
@ -104,6 +125,35 @@ impl Frame<'_> {
widget.render(area, self.buffer, state);
}
/// Render a [`StatefulWidgetRef`] to the current buffer using
/// [`StatefulWidgetRef::render_ref`].
///
/// Usually the area argument is the size of the current frame or a sub-area of the current
/// frame (which can be obtained using [`Layout`] to split the total area).
///
/// The last argument should be an instance of the [`StatefulWidgetRef::State`] associated to
/// the given [`StatefulWidgetRef`].
///
/// # Example
///
/// ```rust
/// # use ratatui::{backend::TestBackend, prelude::*, widgets::*};
/// # let backend = TestBackend::new(5, 5);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// # let mut frame = terminal.get_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);
/// ```
#[stability::unstable(feature = "widget-ref")]
pub fn render_stateful_widget_ref<W>(&mut self, widget: W, area: Rect, state: &mut W::State)
where
W: StatefulWidgetRef,
{
widget.render_ref(area, self.buffer, state);
}
/// After drawing this frame, make the cursor visible and put it at the specified (x, y)
/// coordinates. If this method is not called, the cursor will be hidden.
///

View file

@ -445,22 +445,12 @@ impl<'a> From<Line<'a>> for String {
impl Widget for Line<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
/// Implement [`Widget`] for [`Option<Line>`] to simplify the common case of having an optional
/// [`Line`] field in a widget.
impl Widget for &Option<Line<'_>> {
fn render(self, area: Rect, buf: &mut Buffer) {
if let Some(line) = self {
line.render(area, buf);
}
}
}
impl Widget for &Line<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Line<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
let area = area.intersection(buf.area);
buf.set_style(area, self.style);
let width = self.width() as u16;

View file

@ -337,22 +337,12 @@ impl<'a> Styled for Span<'a> {
impl Widget for Span<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
/// Implement [`Widget`] for [`Option<Span>`] to simplify the common case of having an optional
/// [`Span`] field in a widget.
impl Widget for &Option<Span<'_>> {
fn render(self, area: Rect, buf: &mut Buffer) {
if let Some(span) = self {
span.render(area, buf);
}
}
}
impl Widget for &Span<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Span<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
let Rect {
x: mut current_x,
y,

View file

@ -444,22 +444,12 @@ impl std::fmt::Display for Text<'_> {
impl Widget for Text<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
/// Implement [`Widget`] for [`Option<Text>`] to simplify the common case of having an optional
/// [`Text`] field in a widget.
impl Widget for &Option<Text<'_>> {
fn render(self, area: Rect, buf: &mut Buffer) {
if let Some(text) = self {
text.render(area, buf);
}
}
}
impl Widget for &Text<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Text<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
for (line, row) in self.iter().zip(area.rows()) {
let line_width = line.width() as u16;

View file

@ -59,9 +59,16 @@ use crate::{buffer::Buffer, layout::Rect};
/// 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, the `Widget` trait was more universally implemented on &T instead
/// of just T. This means that widgets can be stored and reused across frames without having to
/// clone or recreate them.
/// Starting with Ratatui 0.26.0, we added a new [`WidgetRef`] trait and implemented this on all the
/// internal widgets. This allows you to store a reference to a widget and render it later. It 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.
///
/// The `Widget` trait can still be implemented, however, it is recommended to implement `WidgetRef`
/// and add an implementation of `Widget` that calls `WidgetRef::render_ref`. This pattern should be
/// used where backwards compatibility is required (all the internal widgets use this approach).
///
/// A blanket implementation of `Widget` for `&W` where `W` implements `WidgetRef` is provided.
///
/// # Examples
///
@ -75,20 +82,6 @@ use crate::{buffer::Buffer, layout::Rect};
/// });
/// ```
///
/// Rendering a widget by reference:
///
/// ```rust
/// # use ratatui::{backend::TestBackend, prelude::*, widgets::*};
/// # let backend = TestBackend::new(5, 5);
/// # let mut terminal = Terminal::new(backend).unwrap();
/// // this variable could instead be a value stored in a struct and reused across frames
/// let paragraph = Paragraph::new("Hello world!");
///
/// terminal.draw(|frame| {
/// frame.render_widget(&paragraph, frame.size());
/// });
/// ```
///
/// It's common to render widgets inside other widgets:
///
/// ```rust
@ -96,20 +89,18 @@ use crate::{buffer::Buffer, layout::Rect};
///
/// struct MyWidget;
///
/// impl Widget for &MyWidget {
/// impl Widget for MyWidget {
/// fn render(self, area: Rect, buf: &mut Buffer) {
/// Block::default()
/// .title("My Widget")
/// .borders(Borders::ALL)
/// .render(area, buf);
/// // ...
/// 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);
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
@ -227,3 +218,334 @@ pub trait StatefulWidget {
type State;
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.
/// Implementors should prefer to implement this over the `Widget` trait and add an implementation
/// of `Widget` that calls `WidgetRef::render_ref` where backwards compatibility is required.
///
/// 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
/// use ratatui::{prelude::*, widgets::*};
///
/// 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);
/// }
/// # }
/// ```
#[stability::unstable(feature = "widget-ref")]
pub trait WidgetRef {
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
/// use ratatui::{prelude::*, widgets::*};
///
/// 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. Implemetors should prefer to implement this over the `StatefulWidget` trait and add an
/// implementation of `StatefulWidget` that calls `StatefulWidgetRef::render_ref` where backwards
/// compatibility is required.
///
/// 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
/// use ratatui::{prelude::*, widgets::*};
///
/// 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);
/// # }
/// ```
#[stability::unstable(feature = "widget-ref")]
pub trait StatefulWidgetRef {
type State;
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);
// }
// }
#[cfg(test)]
mod tests {
use rstest::{fixture, rstest};
use super::*;
use crate::prelude::*;
struct Greeting;
struct Farewell;
struct PersonalGreeting;
impl Widget for Greeting {
fn render(self, area: Rect, buf: &mut Buffer) {
self.render_ref(area, buf);
}
}
impl WidgetRef for Greeting {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
Line::from("Hello").render(area, buf);
}
}
impl Widget for Farewell {
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::from("Goodbye").right_aligned().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);
}
}
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);
}
}
#[fixture]
fn buf() -> Buffer {
Buffer::empty(Rect::new(0, 0, 20, 1))
}
#[rstest]
fn widget_render(mut buf: Buffer) {
let widget = Greeting;
widget.render(buf.area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["Hello "]));
}
#[rstest]
fn widget_ref_render(mut buf: Buffer) {
let widget = Greeting;
widget.render_ref(buf.area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["Hello "]));
}
/// This test is to ensure that the blanket implementation of `Widget` for `&W` where `W`
/// implements `WidgetRef` works as expected.
#[rstest]
fn widget_blanket_render(mut buf: Buffer) {
let widget = &Greeting;
widget.render(buf.area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["Hello "]));
}
#[rstest]
fn widget_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 widget_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()
}
#[rstest]
fn stateful_widget_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 stateful_widget_ref_render(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 stateful_widget_box_render(mut buf: Buffer, mut state: String) {
let widget = Box::new(PersonalGreeting);
widget.render(buf.area, &mut buf, &mut state);
assert_eq!(buf, Buffer::with_lines(["Hello world "]));
}
#[rstest]
fn widget_option_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 widget_option_render_ref_none(mut buf: Buffer) {
let widget: Option<Greeting> = None;
widget.render_ref(buf.area, &mut buf);
assert_eq!(buf, Buffer::with_lines([" "]));
}
}

View file

@ -576,15 +576,15 @@ impl BarChart<'_> {
impl Widget for BarChart<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
(&self).render(area, buf);
self.render_ref(area, buf);
}
}
impl Widget for &BarChart<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for BarChart<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
self.block.render(area, buf);
self.block.render_ref(area, buf);
let inner = self.block.inner_if_some(area);
if inner.is_empty() || self.data.is_empty() || self.bar_width == 0 {

View file

@ -520,22 +520,12 @@ impl<'a> Block<'a> {
impl Widget for Block<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
/// Implement [`Widget`] for [`Option<Block>`] to simplify the common case of having an optional
/// [`Block`] field in a widget.
impl Widget for &Option<Block<'_>> {
fn render(self, area: Rect, buf: &mut Buffer) {
if let Some(block) = self {
block.render(area, buf);
}
}
}
impl Widget for &Block<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Block<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
if area.is_empty() {
return;
}

View file

@ -116,13 +116,13 @@ impl<'a, DS: DateStyler> Monthly<'a, DS> {
impl<DS: DateStyler> Widget for Monthly<'_, DS> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
impl<DS: DateStyler> Widget for &Monthly<'_, DS> {
fn render(self, area: Rect, buf: &mut Buffer) {
self.block.render(area, buf);
impl<DS: DateStyler> WidgetRef for Monthly<'_, DS> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
self.block.render_ref(area, buf);
let inner = self.block.inner_if_some(area);
self.render_monthly(inner, buf);
}

View file

@ -692,16 +692,16 @@ where
F: Fn(&mut Context),
{
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
impl<F> Widget for &Canvas<'_, F>
impl<F> WidgetRef for Canvas<'_, F>
where
F: Fn(&mut Context),
{
fn render(self, area: Rect, buf: &mut Buffer) {
self.block.render(area, buf);
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
self.block.render_ref(area, buf);
let canvas_area = self.block.inner_if_some(area);
if canvas_area.is_empty() {
return;

View file

@ -919,15 +919,15 @@ impl<'a> Chart<'a> {
impl Widget for Chart<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
impl Widget for &Chart<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Chart<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
self.block.render(area, buf);
self.block.render_ref(area, buf);
let chart_area = self.block.inner_if_some(area);
if chart_area.is_empty() {
return;

View file

@ -26,12 +26,12 @@ pub struct Clear;
impl Widget for Clear {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
impl Widget for &Clear {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Clear {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
for x in area.left()..area.right() {
for y in area.top()..area.bottom() {
buf.get_mut(x, y).reset();

View file

@ -157,14 +157,14 @@ impl<'a> Gauge<'a> {
impl Widget for Gauge<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
impl Widget for &Gauge<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Gauge<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
self.block.render(area, buf);
self.block.render_ref(area, buf);
let inner = self.block.inner_if_some(area);
self.render_gague(inner, buf);
}
@ -352,14 +352,14 @@ impl<'a> LineGauge<'a> {
impl Widget for LineGauge<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
impl Widget for &LineGauge<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for LineGauge<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
self.block.render(area, buf);
self.block.render_ref(area, buf);
let gauge_area = self.block.inner_if_some(area);
if gauge_area.is_empty() {
return;

View file

@ -814,15 +814,14 @@ impl<'a> List<'a> {
impl Widget for List<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let mut state = ListState::default();
StatefulWidget::render(&self, area, buf, &mut state);
WidgetRef::render_ref(&self, area, buf);
}
}
impl Widget for &List<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for List<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
let mut state = ListState::default();
StatefulWidget::render(self, area, buf, &mut state);
StatefulWidgetRef::render_ref(self, area, buf, &mut state);
}
}
@ -830,16 +829,24 @@ impl StatefulWidget for List<'_> {
type State = ListState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
StatefulWidget::render(&self, area, buf, state);
StatefulWidgetRef::render_ref(&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(area, buf);
self.block.render_ref(area, buf);
let list_area = self.block.inner_if_some(area);
if list_area.is_empty() || self.items.is_empty() {

View file

@ -320,14 +320,14 @@ impl<'a> Paragraph<'a> {
impl Widget for Paragraph<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
impl Widget for &Paragraph<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Paragraph<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
self.block.render(area, buf);
self.block.render_ref(area, buf);
let inner = self.block.inner_if_some(area);
self.render_paragraph(inner, buf);
}

View file

@ -155,13 +155,13 @@ impl<'a> Styled for Sparkline<'a> {
impl Widget for Sparkline<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
impl Widget for &Sparkline<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
self.block.render(area, buf);
impl WidgetRef for Sparkline<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
self.block.render_ref(area, buf);
let inner = self.block.inner_if_some(area);
self.render_sparkline(inner, buf);
}

View file

@ -565,13 +565,12 @@ impl<'a> Table<'a> {
impl Widget for Table<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default();
StatefulWidget::render(self, area, buf, &mut state);
WidgetRef::render_ref(&self, area, buf);
}
}
impl Widget for &Table<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Table<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default();
StatefulWidget::render(self, area, buf, &mut state);
}
@ -585,12 +584,20 @@ 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(area, buf);
self.block.render_ref(area, buf);
let table_area = self.block.inner_if_some(area);
if table_area.is_empty() {
return;

View file

@ -248,14 +248,14 @@ impl<'a> Styled for Tabs<'a> {
impl Widget for Tabs<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
Widget::render(&self, area, buf);
self.render_ref(area, buf);
}
}
impl Widget for &Tabs<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
impl WidgetRef for Tabs<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
buf.set_style(area, self.style);
self.block.render(area, buf);
self.block.render_ref(area, buf);
let inner = self.block.inner_if_some(area);
self.render_tabs(inner, buf);
}