widgets.rs
raw
//! The widget demo: a retained widget tree with nested flex layout,
//! wrapping text, and an image, shared by the runnable example and the
//! golden tests.
use guiduck_core::{Container, Image, Text, WidgetId, WidgetTree, taffy};
use guiduck_scene::paint::color::palette::css;
use taffy::prelude::{auto, length, percent};
use crate::{SAMPLE_FONT_FAMILY, checkerboard, sample_text_context};
/// Build the demo tree. Returns the tree and the id of the paragraph widget
/// (used by tests to check wrapping behavior).
pub fn demo_tree() -> (WidgetTree, WidgetId) {
let mut tree = WidgetTree::with_text_context(sample_text_context());
let root = tree.insert(
Container::new().background(css::WHITE_SMOKE),
taffy::Style {
size: taffy::Size {
width: percent(1.0_f32),
height: percent(1.0_f32),
},
flex_direction: taffy::FlexDirection::Column,
padding: taffy::Rect::length(16.0_f32),
gap: taffy::Size {
width: length(12.0_f32),
height: length(12.0_f32),
},
..Default::default()
},
None,
);
tree.insert(
Text::new("guiduck — widgets, flexbox, wrapping text")
.font_size(20.0)
.family(SAMPLE_FONT_FAMILY)
.brush(css::DARK_SLATE_GRAY),
taffy::Style::default(),
Some(root),
);
// A row of three growing cards.
let cards = tree.insert(
Container::new(),
taffy::Style {
flex_direction: taffy::FlexDirection::Row,
gap: taffy::Size {
width: length(12.0_f32),
height: length(12.0_f32),
},
..Default::default()
},
Some(root),
);
for (label, color) in [
("First", css::LIGHT_STEEL_BLUE),
("Second", css::NAVAJO_WHITE),
("Third", css::THISTLE),
] {
let card = tree.insert(
Container::new()
.background(color)
.corner_radius(10.0)
.border(css::SLATE_GRAY, 1.5),
taffy::Style {
flex_grow: 1.0,
flex_basis: length(0.0_f32),
padding: taffy::Rect::length(12.0_f32),
..Default::default()
},
Some(cards),
);
tree.insert(
Text::new(label)
.font_size(15.0)
.family(SAMPLE_FONT_FAMILY)
.brush(css::BLACK),
taffy::Style::default(),
Some(card),
);
}
// A paragraph that wraps beside a fixed-width image.
let body = tree.insert(
Container::new(),
taffy::Style {
flex_direction: taffy::FlexDirection::Row,
gap: taffy::Size {
width: length(12.0_f32),
height: length(12.0_f32),
},
align_items: Some(taffy::AlignItems::FLEX_START),
..Default::default()
},
Some(root),
);
let paragraph = tree.insert(
Text::new(
"This paragraph lives in a flex row next to an image. When the window \
gets narrower the text wraps to more lines and the row grows taller, \
which is exactly the layout behavior taffy owes us. The quick brown \
fox jumps over the lazy dog.",
)
.font_size(14.0)
.family(SAMPLE_FONT_FAMILY)
.brush(css::BLACK),
taffy::Style {
flex_grow: 1.0,
flex_basis: length(0.0_f32),
..Default::default()
},
Some(body),
);
tree.insert(
Image::new(guiduck_core::graphic::Graphic::Image(checkerboard())),
taffy::Style {
size: taffy::Size {
width: length(96.0_f32),
height: auto(),
},
flex_shrink: 0.0,
..Default::default()
},
Some(body),
);
(tree, paragraph)
}
#[cfg(test)]
mod tests {
use guiduck_scene::FragmentStore;
use guiduck_scene::geom::Size;
use super::*;
#[test]
fn demo_lays_out_and_paints() {
let (mut tree, paragraph) = demo_tree();
tree.compute_layout(Size::new(640.0, 420.0));
let wide_height = tree.layout(paragraph).height();
let mut store = FragmentStore::new();
let root = tree.paint_fresh(&mut store).expect("tree has a root");
assert!(store.get(root).unwrap().is_balanced());
let (mut narrow, paragraph2) = demo_tree();
narrow.compute_layout(Size::new(360.0, 420.0));
assert!(
narrow.layout(paragraph2).height() > wide_height,
"narrower viewport wraps the paragraph to more lines"
);
}
}