ratatui/widgets/
widget_ref.rs

1use alloc::string::String;
2
3use super::Widget;
4use crate::buffer::Buffer;
5use crate::layout::Rect;
6use crate::style::Style;
7
8/// A `WidgetRef` is a trait that allows rendering a widget by reference.
9///
10/// This trait is useful when you want to store a reference to a widget and render it later. It also
11/// allows you to render boxed widgets.
12///
13/// Boxed widgets allow you to store widgets with a type that is not known at compile time. This is
14/// useful when you want to store a collection of widgets with different types. You can then iterate
15/// over the collection and render each widget.
16///
17/// This trait was introduced in Ratatui 0.26.0 and is implemented for all the internal widgets. It
18/// is currently marked as unstable as we are still evaluating the API and may make changes in the
19/// future. See <https://github.com/ratatui/ratatui/issues/1287> for more information.
20///
21/// A blanket implementation of `Widget` for `&W` where `W` implements `WidgetRef` is provided.
22///
23/// A blanket implementation of `WidgetRef` for `Option<W>` where `W` implements `WidgetRef` is
24/// provided. This is a convenience approach to make it easier to attach child widgets to parent
25/// widgets. It allows you to render an optional widget by reference.
26///
27/// For comprehensive information about widget implementation patterns, rendering, and usage,
28/// see the [`widgets`] module documentation.
29///
30/// [`widgets`]: crate::widgets
31///
32/// # Examples
33///
34/// ```rust
35/// # #[cfg(feature = "unstable-widget-ref")] {
36/// use ratatui::widgets::WidgetRef;
37/// use ratatui_core::buffer::Buffer;
38/// use ratatui_core::layout::Rect;
39/// use ratatui_core::text::Line;
40/// use ratatui_core::widgets::Widget;
41///
42/// struct Greeting;
43///
44/// struct Farewell;
45///
46/// impl WidgetRef for Greeting {
47///     fn render_ref(&self, area: Rect, buf: &mut Buffer) {
48///         Line::raw("Hello").render(area, buf);
49///     }
50/// }
51///
52/// /// Only needed for backwards compatibility
53/// impl Widget for Greeting {
54///     fn render(self, area: Rect, buf: &mut Buffer) {
55///         self.render_ref(area, buf);
56///     }
57/// }
58///
59/// impl WidgetRef for Farewell {
60///     fn render_ref(&self, area: Rect, buf: &mut Buffer) {
61///         Line::raw("Goodbye").right_aligned().render(area, buf);
62///     }
63/// }
64///
65/// /// Only needed for backwards compatibility
66/// impl Widget for Farewell {
67///     fn render(self, area: Rect, buf: &mut Buffer) {
68///         self.render_ref(area, buf);
69///     }
70/// }
71///
72/// # fn render(area: Rect, buf: &mut Buffer) {
73/// let greeting = Greeting;
74/// let farewell = Farewell;
75///
76/// // these calls do not consume the widgets, so they can be used again later
77/// greeting.render_ref(area, buf);
78/// farewell.render_ref(area, buf);
79///
80/// // a collection of widgets with different types
81/// let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(greeting), Box::new(farewell)];
82/// for widget in widgets {
83///     widget.render_ref(area, buf);
84/// }
85/// # }
86/// # }
87/// ```
88#[instability::unstable(feature = "widget-ref")]
89pub trait WidgetRef {
90    /// Draws the current state of the widget in the given buffer. That is the only method required
91    /// to implement a custom widget.
92    fn render_ref(&self, area: Rect, buf: &mut Buffer);
93}
94
95/// This allows you to render a widget by reference.
96impl<W> WidgetRef for &W
97where
98    for<'a> &'a W: Widget,
99{
100    fn render_ref(&self, area: Rect, buf: &mut Buffer) {
101        self.render(area, buf);
102    }
103}
104
105/// Provides the ability to render a string slice by reference.
106///
107/// This trait implementation ensures that a string slice, which is an immutable view over a
108/// `String`, can be drawn on demand without requiring ownership of the string itself. It utilizes
109/// the default text style when rendering onto the provided [`Buffer`] at the position defined by
110/// [`Rect`].
111impl WidgetRef for &str {
112    fn render_ref(&self, area: Rect, buf: &mut Buffer) {
113        buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
114    }
115}
116
117/// Provides the ability to render a `String` by reference.
118///
119/// This trait allows for a `String` to be rendered onto the [`Buffer`], similarly using the default
120/// style settings. It ensures that an owned `String` can be rendered efficiently by reference,
121/// without the need to give up ownership of the underlying text.
122impl WidgetRef for String {
123    fn render_ref(&self, area: Rect, buf: &mut Buffer) {
124        buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
125    }
126}
127
128/// A blanket implementation of `WidgetExt` for `Option<W>` where `W` implements `WidgetRef`.
129///
130/// This is a convenience implementation that makes it easy to attach child widgets to parent
131/// widgets. It allows you to render an optional widget by reference.
132///
133/// The internal widgets use this pattern to render the optional `Block` widgets that are included
134/// on most widgets.
135/// Blanket implementation of `WidgetExt` for `Option<W>` where `W` implements `WidgetRef`.
136///
137/// # Examples
138///
139/// ```rust
140/// # #[cfg(feature = "unstable-widget-ref")] {
141/// use ratatui::widgets::WidgetRef;
142/// use ratatui_core::buffer::Buffer;
143/// use ratatui_core::layout::Rect;
144/// use ratatui_core::text::Line;
145/// use ratatui_core::widgets::Widget;
146///
147/// struct Parent {
148///     child: Option<Child>,
149/// }
150///
151/// struct Child;
152///
153/// impl WidgetRef for Child {
154///     fn render_ref(&self, area: Rect, buf: &mut Buffer) {
155///         Line::raw("Hello from child").render(area, buf);
156///     }
157/// }
158///
159/// impl WidgetRef for Parent {
160///     fn render_ref(&self, area: Rect, buf: &mut Buffer) {
161///         self.child.render_ref(area, buf);
162///     }
163/// }
164/// # }
165/// ```
166impl<W: WidgetRef> WidgetRef for Option<W> {
167    fn render_ref(&self, area: Rect, buf: &mut Buffer) {
168        if let Some(widget) = self {
169            widget.render_ref(area, buf);
170        }
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use alloc::boxed::Box;
177    use alloc::vec;
178    use alloc::vec::Vec;
179
180    use rstest::{fixture, rstest};
181
182    use super::*;
183    use crate::buffer::Buffer;
184    use crate::layout::Rect;
185    use crate::text::Line;
186
187    #[fixture]
188    fn buf() -> Buffer {
189        Buffer::empty(Rect::new(0, 0, 20, 1))
190    }
191
192    struct Greeting;
193
194    struct Farewell;
195
196    impl Widget for &Greeting {
197        fn render(self, area: Rect, buf: &mut Buffer) {
198            Line::from("Hello").render(area, buf);
199        }
200    }
201
202    impl Widget for &Farewell {
203        fn render(self, area: Rect, buf: &mut Buffer) {
204            Line::from("Goodbye").right_aligned().render(area, buf);
205        }
206    }
207
208    /// Ensure that the blanket implementation of `WidgetRef` for `&W` where `W` implements
209    /// `Widget` works as expected.
210    #[rstest]
211    fn render_ref(mut buf: Buffer) {
212        let widget = &Greeting;
213        widget.render_ref(buf.area, &mut buf);
214        assert_eq!(buf, Buffer::with_lines(["Hello               "]));
215    }
216
217    #[rstest]
218    fn render_ref_box(mut buf: Buffer) {
219        let widget: Box<dyn WidgetRef> = Box::new(&Greeting);
220        widget.render_ref(buf.area, &mut buf);
221        assert_eq!(buf, Buffer::with_lines(["Hello               "]));
222    }
223
224    #[rstest]
225    fn render_ref_box_vec(mut buf: Buffer) {
226        let widgets: Vec<Box<dyn WidgetRef>> = vec![Box::new(&Greeting), Box::new(&Farewell)];
227        for widget in widgets {
228            widget.render_ref(buf.area, &mut buf);
229        }
230        assert_eq!(buf, Buffer::with_lines(["Hello        Goodbye"]));
231    }
232
233    #[rstest]
234    fn render_ref_some(mut buf: Buffer) {
235        let widget = Some(&Greeting);
236        widget.render_ref(buf.area, &mut buf);
237        assert_eq!(buf, Buffer::with_lines(["Hello               "]));
238    }
239
240    #[rstest]
241    fn render_ref_none(mut buf: Buffer) {
242        let widget: Option<&Greeting> = None;
243        widget.render_ref(buf.area, &mut buf);
244        assert_eq!(buf, Buffer::with_lines(["                    "]));
245    }
246
247    #[rstest]
248    fn render_ref_str(mut buf: Buffer) {
249        "hello world".render_ref(buf.area, &mut buf);
250        assert_eq!(buf, Buffer::with_lines(["hello world         "]));
251    }
252
253    #[rstest]
254    fn render_ref_option_str(mut buf: Buffer) {
255        Some("hello world").render_ref(buf.area, &mut buf);
256        assert_eq!(buf, Buffer::with_lines(["hello world         "]));
257    }
258
259    #[rstest]
260    fn render_ref_string(mut buf: Buffer) {
261        String::from("hello world").render_ref(buf.area, &mut buf);
262        assert_eq!(buf, Buffer::with_lines(["hello world         "]));
263    }
264
265    #[rstest]
266    fn render_ref_option_string(mut buf: Buffer) {
267        Some(String::from("hello world")).render_ref(buf.area, &mut buf);
268        assert_eq!(buf, Buffer::with_lines(["hello world         "]));
269    }
270}