docs: add widget-ref-container example (#1603)

Implements ideas alluded to by
<https://discord.com/channels/1070692720437383208/1072907135664529508/1323061053990637640>
and followup conversations.
This commit is contained in:
Josh McKinney
2025-01-14 14:05:58 -08:00
committed by GitHub
parent 41d883da7a
commit da05957fa0
2 changed files with 97 additions and 0 deletions

View File

@@ -254,6 +254,11 @@ name = "widget_impl"
required-features = ["crossterm", "unstable-widget-ref"]
doc-scrape-examples = true
[[example]]
name = "widget-ref-container"
required-features = ["crossterm", "unstable-widget-ref"]
doc-scrape-examples = true
[[test]]
name = "state_serde"
required-features = ["serde"]

View File

@@ -0,0 +1,92 @@
//! An example of how to use [`WidgetRef`] to store heterogeneous widgets in a container.
//!
//! This example creates a `StackContainer` widget that can hold any number of widgets of different
//! types. It creates two widgets, `Greeting` and `Farewell`, and stores them in a `StackContainer`
//! with a vertical layout. The `StackContainer` widget renders each of its child widgets in the
//! order they were added.
//!
//! `Greetings` and `Farewell` both implement the Widget trait on references to themselves, which
//! triggers the blanket implementation of [`WidgetRef`] for them. This allows them to be stored in
//! the `StackContainer` in a boxed form.
//!
//! This example runs with the Ratatui library code in the branch that you are currently reading.
//! See the [`latest`] branch for the code which works with the most recent Ratatui release.
//!
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
use std::iter::zip;
use color_eyre::Result;
use crossterm::event;
use ratatui::{
buffer::Buffer,
layout::{Constraint, Direction, Layout, Rect},
widgets::{Block, Paragraph, Widget, WidgetRef},
DefaultTerminal, Frame,
};
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let result = run(terminal);
ratatui::restore();
result
}
fn run(mut terminal: DefaultTerminal) -> Result<()> {
loop {
terminal.draw(render)?;
if matches!(event::read()?, event::Event::Key(_)) {
break Ok(());
}
}
}
fn render(frame: &mut Frame) {
let container = StackContainer {
direction: Direction::Vertical,
widgets: vec![
(Box::new(&Greeting), Constraint::Percentage(50)),
(Box::new(&Farewell), Constraint::Percentage(50)),
],
};
frame.render_widget(&container, frame.area());
}
struct Greeting;
impl Widget for &Greeting {
fn render(self, area: Rect, buf: &mut Buffer) {
Paragraph::new("Hello")
.block(Block::bordered())
.render(area, buf);
}
}
struct Farewell;
impl Widget for &Farewell {
fn render(self, area: Rect, buf: &mut Buffer) {
Paragraph::new("Goodbye")
.block(Block::bordered())
.render(area, buf);
}
}
struct StackContainer {
direction: Direction,
widgets: Vec<(Box<dyn WidgetRef>, Constraint)>,
}
impl Widget for &StackContainer {
fn render(self, area: Rect, buf: &mut Buffer) {
let layout = Layout::default()
.direction(self.direction)
.constraints(self.widgets.iter().map(|(_, constraint)| *constraint))
.split(area);
let widgets = self.widgets.iter().map(|(widget, _)| widget);
for (widget, area) in zip(widgets, layout.iter()) {
widget.render_ref(*area, buf);
}
}
}