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}