feat: impl WidgetRef for Fn(Rect, &mut Buffer)

This allows you to treat any function  that takes a `Rect` and a mutable
reference a `Buffer` as a widget in situations where you don't want to
create a new type for a widget.

Example:

```rust
fn hello(area: Rect, buf: &mut Buffer) {
    Line::raw("Hello").render(area, buf);
}

frame.render_widget(&hello, frame.size());
frame.render_widget_ref(hello, frame.size());
```

Related to: <https://forum.ratatui.rs/t/idea-functionwidget-was-thoughts-on-tui-react/59/2>
This commit is contained in:
Josh McKinney 2024-05-29 16:25:21 -07:00
parent 8061813f32
commit f99c224fc1
No known key found for this signature in database
GPG key ID: 722287396A903BC5

View file

@ -245,6 +245,11 @@ pub trait StatefulWidget {
/// provided. This is a convenience approach to make it easier to attach child widgets to parent /// 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. /// widgets. It allows you to render an optional widget by reference.
/// ///
/// A blanket [implementation of `WidgetRef` for `Fn(Rect, &mut
/// Buffer)`](WidgetRef#impl-WidgetRef-for-F) is provided. This allows you to treat any function or
/// closure that takes a `Rect` and a mutable reference to a `Buffer` as a widget in situations
/// where you don't want to create a new type for a widget.
///
/// # Examples /// # Examples
/// ///
/// ```rust /// ```rust
@ -304,13 +309,40 @@ pub trait WidgetRef {
fn render_ref(&self, area: Rect, buf: &mut Buffer); fn render_ref(&self, area: Rect, buf: &mut Buffer);
} }
/// This allows you to render a widget by reference. // /// This allows you to render a widget by reference.
impl<W: WidgetRef> Widget for &W { impl<W: WidgetRef> Widget for &W {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
self.render_ref(area, buf); self.render_ref(area, buf);
} }
} }
/// A blanket implementation of `WidgetRef` for `Fn(Rect, &mut Buffer)`.
///
/// This allows you to treat any function that takes a `Rect` and a mutable reference to a `Buffer`
/// as a widget in situations where you don't want to create a new type for a widget.
///
/// # Examples
///
/// ```rust
/// use ratatui::{prelude::*, widgets::*};
/// fn hello(area: Rect, buf: &mut Buffer) {
/// Line::raw("Hello").render(area, buf);
/// }
///
/// fn draw(mut frame: Frame) {
/// frame.render_widget(&hello, frame.size());
/// frame.render_widget_ref(hello, frame.size());
/// }
/// ```
impl<F> WidgetRef for F
where
F: Fn(Rect, &mut Buffer),
{
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
self(area, buf);
}
}
/// A blanket implementation of `WidgetExt` for `Option<W>` where `W` implements `WidgetRef`. /// 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 /// This is a convenience implementation that makes it easy to attach child widgets to parent
@ -695,4 +727,42 @@ mod tests {
assert_eq!(buf, Buffer::with_lines(["hello world "])); assert_eq!(buf, Buffer::with_lines(["hello world "]));
} }
} }
mod function_widget {
use super::*;
fn widget_function(area: Rect, buf: &mut Buffer) {
"Hello".render(area, buf);
}
#[rstest]
fn render(mut buf: Buffer) {
widget_function.render(buf.area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["Hello "]));
}
#[rstest]
fn render_ref(mut buf: Buffer) {
widget_function.render_ref(buf.area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["Hello "]));
}
#[rstest]
fn render_closure(mut buf: Buffer) {
let widget = |area: Rect, buf: &mut Buffer| {
"Hello".render(area, buf);
};
widget.render(buf.area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["Hello "]));
}
#[rstest]
fn render_closure_ref(mut buf: Buffer) {
let widget = |area: Rect, buf: &mut Buffer| {
"Hello".render(area, buf);
};
widget.render_ref(buf.area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["Hello "]));
}
}
} }