pub struct Cell { /* private fields */ }Expand description
A leaf cell in the layout tree. Carries an optional Measure and an
optional CellId. Build with Cell::empty or Cell::measured;
shorthand: Grid::cell returns Cell::empty().
Implementations§
Source§impl Cell
impl Cell
Sourcepub fn empty() -> Self
pub fn empty() -> Self
An empty leaf with zero intrinsic size. Useful as a tagged placeholder inside a parent grid track.
Examples found in repository?
29fn plain(id: &str) -> Patch {
30 Patch::new(id).slot(Slot::Panel, Cell::empty())
31}
32
33fn color_for_region(region: &str) -> Color {
34 match region {
35 "panel" => rgb8(40, 60, 90),
36 "axis_top" => rgb8(200, 100, 130),
37 "axis_bottom" => rgb8(160, 100, 130),
38 _ => rgb8(120, 120, 120),
39 }
40}
41
42fn main() {
43 let (w, h) = (1400u32, 400u32);
44 let dpi = 96.0;
45
46 // Deepest level: two plots, the first carries the chrome that needs
47 // to propagate all the way up to the root composition.
48 let leaf = beside(
49 Patch::new("leaf_l")
50 .slot(Slot::AxisTop, text_cell("axis_top from deepest leaf", 14.0))
51 .slot(Slot::AxisBottom, text_cell("axis_bottom from leaf", 11.0))
52 .slot(Slot::Panel, Cell::empty()),
53 plain("leaf_r"),
54 );
55 // Mid level: a plain plot beside the leaf composition.
56 let mid = beside(plain("mid"), leaf);
57 // Root level: a plain plot beside the mid composition.
58 let composed = beside(plain("root"), mid);
59
60 let layout = composed.solve(hephaestus::Size::new(w as f64, h as f64), dpi);
61
62 let mut renderer = VelloRenderer::new().expect("vello renderer init");
63 {
64 let scene = renderer.scene();
65 let stroke = hephaestus::stroke::Stroke::new(1.0);
66 let text_brush: Brush = rgb8(20, 20, 30).into();
67
68 for (_id, region, rect) in layout.iter() {
69 if region == "panel" {
70 continue;
71 }
72 let c = color_for_region(region);
73 let tint = Color::new([c.components[0], c.components[1], c.components[2], 0.20]);
74 let path: Path = rect.to_path(0.1);
75 scene.fill(
76 FillRule::NonZero,
77 Affine::IDENTITY,
78 &Brush::Solid(tint),
79 None,
80 &path,
81 PickId::Skip,
82 );
83 scene.stroke(
84 &stroke,
85 Affine::IDENTITY,
86 &Brush::Solid(c),
87 None,
88 &path,
89 PickId::Skip,
90 );
91 }
92 for (_id, region, rect) in layout.iter() {
93 if region != "panel" {
94 continue;
95 }
96 let path: Path = rect.to_path(0.1);
97 scene.fill(
98 FillRule::NonZero,
99 Affine::IDENTITY,
100 &Brush::Solid(color_for_region(region)),
101 None,
102 &path,
103 PickId::Skip,
104 );
105 scene.stroke(
106 &stroke,
107 Affine::IDENTITY,
108 &Brush::Solid(rgb8(255, 255, 255)),
109 None,
110 &path,
111 PickId::Skip,
112 );
113 }
114
115 if let Some(rect) = layout.get("leaf_l", Slot::AxisTop) {
116 let run = TextRun::new("axis_top from deepest leaf", &TextStyle::new(14.0), 96.0);
117 draw_text_in_rect(scene, &run, rect, &text_brush, PickId::Skip);
118 }
119 if let Some(rect) = layout.get("leaf_l", Slot::AxisBottom) {
120 let run = TextRun::new("axis_bottom from leaf", &TextStyle::new(11.0), 96.0);
121 draw_text_in_rect(scene, &run, rect, &text_brush, PickId::Skip);
122 }
123 }
124
125 let mut pixels = vec![0u8; (w * h * 4) as usize];
126 let bg: Color = rgb8(248, 248, 252);
127 renderer
128 .render_to_buffer(w, h, bg, &mut pixels)
129 .expect("render");
130
131 let path = std::env::current_dir()
132 .unwrap()
133 .join("examples/nesting_deep.png");
134 hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
135 println!("wrote {}", path.display());
136
137 // Print panel y0s so the user can verify alignment across all 3 nesting levels.
138 let root_panel = layout.get("root", Slot::Panel).unwrap();
139 let mid_panel = layout.get("mid", Slot::Panel).unwrap();
140 let leaf_l_panel = layout.get("leaf_l", Slot::Panel).unwrap();
141 let leaf_r_panel = layout.get("leaf_r", Slot::Panel).unwrap();
142 println!(
143 "panel y0: root={}, mid={}, leaf_l={}, leaf_r={}",
144 root_panel.y0, mid_panel.y0, leaf_l_panel.y0, leaf_r_panel.y0
145 );
146 println!("(all four should be equal — propagation across 3 nesting levels)");
147}More examples
47fn plain(id: &str) -> Patch {
48 Patch::new(id).slot(Slot::Panel, Cell::empty())
49}
50
51fn fixed_square(id: &str, label: &str) -> Patch {
52 // Chrome on a fixed patch is fine — the solver's second iteration
53 // picks up the resolved Auto-row heights from iter 0 and reshapes
54 // the respected fr distribution to honour the lock anyway. The
55 // axis_top here proves it: panels still report ratio = 1.000.
56 Patch::new(id)
57 .aspect(1.0, 1.0)
58 .slot(Slot::AxisTop, text_cell(label, 12.0))
59 .slot(Slot::Panel, Cell::empty())
60}45fn plain_plot(id: &str) -> Patch {
46 Patch::new(id).slot(Slot::Panel, Cell::empty())
47}
48
49fn color_for_region(region: &str) -> Color {
50 match region {
51 "panel" => rgb8(40, 60, 90),
52 "axis_top" => rgb8(200, 100, 130),
53 _ => rgb8(120, 120, 120),
54 }
55}
56
57fn main() {
58 let (w, h) = (1400u32, 400u32);
59 let dpi = 96.0;
60
61 // Top-level chrome lives on the LEFT sibling. The right side is a
62 // nested composition with three plain inner patches.
63 let outer_with_chrome = Patch::new("outer_plain")
64 .slot(
65 Slot::AxisTop,
66 text_cell(
67 "axis_top on the outer SIBLING patch\nspans multiple lines\nto propagate INTO the nested composition",
68 14.0,
69 ),
70 )
71 .slot(Slot::Panel, Cell::empty());
72 let nested = grid(
73 1,
74 3,
75 vec![
76 plain_plot("c1").into(),
77 plain_plot("c2").into(),
78 plain_plot("c3").into(),
79 ],
80 );
81 let composed = beside(outer_with_chrome, nested);
82 let layout = composed.solve(hephaestus::Size::new(w as f64, h as f64), dpi);
83
84 let mut renderer = VelloRenderer::new().expect("vello renderer init");
85 {
86 let scene = renderer.scene();
87 let stroke = hephaestus::stroke::Stroke::new(1.0);
88 let text_brush: Brush = rgb8(20, 20, 30).into();
89
90 for (_id, region, rect) in layout.iter() {
91 if region == "panel" {
92 continue;
93 }
94 let c = color_for_region(region);
95 let tint = Color::new([c.components[0], c.components[1], c.components[2], 0.20]);
96 let path: Path = rect.to_path(0.1);
97 scene.fill(
98 FillRule::NonZero,
99 Affine::IDENTITY,
100 &Brush::Solid(tint),
101 None,
102 &path,
103 PickId::Skip,
104 );
105 scene.stroke(
106 &stroke,
107 Affine::IDENTITY,
108 &Brush::Solid(c),
109 None,
110 &path,
111 PickId::Skip,
112 );
113 }
114 for (_id, region, rect) in layout.iter() {
115 if region != "panel" {
116 continue;
117 }
118 let path: Path = rect.to_path(0.1);
119 scene.fill(
120 FillRule::NonZero,
121 Affine::IDENTITY,
122 &Brush::Solid(color_for_region(region)),
123 None,
124 &path,
125 PickId::Skip,
126 );
127 scene.stroke(
128 &stroke,
129 Affine::IDENTITY,
130 &Brush::Solid(rgb8(255, 255, 255)),
131 None,
132 &path,
133 PickId::Skip,
134 );
135 }
136
137 if let Some(rect) = layout.get("outer_plain", Slot::AxisTop) {
138 let run = TextRun::new(
139 "axis_top on the outer SIBLING patch\nspans multiple lines\nto propagate INTO the nested composition",
140 &TextStyle::new(14.0),
141 96.0,
142 );
143 draw_text_in_rect(scene, &run, rect, &text_brush, PickId::Skip);
144 }
145 }
146
147 let mut pixels = vec![0u8; (w * h * 4) as usize];
148 let bg: Color = rgb8(248, 248, 252);
149 renderer
150 .render_to_buffer(w, h, bg, &mut pixels)
151 .expect("render");
152
153 let path = std::env::current_dir()
154 .unwrap()
155 .join("examples/nesting_outer_chrome_propagates.png");
156 hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
157 println!("wrote {}", path.display());
158
159 // Sanity check: all four panels share y0, even though only the outer
160 // sibling carries the axis_top content. The back sizer drives the
161 // nested inner row 8 from the outer-resolved row 8.
162 let outer_y = layout.get("outer_plain", Slot::Panel).unwrap();
163 let c1_y = layout.get("c1", Slot::Panel).unwrap();
164 let c2_y = layout.get("c2", Slot::Panel).unwrap();
165 let c3_y = layout.get("c3", Slot::Panel).unwrap();
166 println!(
167 "panel y0: outer_plain={}, c1={}, c2={}, c3={}",
168 outer_y.y0, c1_y.y0, c2_y.y0, c3_y.y0
169 );
170 println!("(all four should be equal — outer chrome propagated INTO the nested composition via back sizers)");
171}Sourcepub fn measured(m: impl Measure + 'static) -> Self
pub fn measured(m: impl Measure + 'static) -> Self
A leaf whose intrinsic size comes from m.
Examples found in repository?
More examples
Sourcepub fn measured_boxed(m: Box<dyn Measure>) -> Self
pub fn measured_boxed(m: Box<dyn Measure>) -> Self
Like Self::measured but takes an already-boxed measure.
Used when a caller has extracted a Box<dyn Measure> from
another cell (via Self::into_measure) and wants to
re-wrap it without unboxing.
Sourcepub fn id(self, id: CellId) -> Self
pub fn id(self, id: CellId) -> Self
Tag this cell so its resolved rect is retrievable from
Layout::rect.
Examples found in repository?
15fn main() {
16 let (w, h) = (800u32, 600u32);
17 let dpi = 96.0;
18
19 // Outer: 5 columns × 3 rows of fr(1).
20 let mut root = Grid::new(vec![Track::Fr(1.0); 5], vec![Track::Fr(1.0); 3]);
21
22 // Tag every outer cell so we can outline the underlying grid.
23 let mut outer_id = 1u64;
24 for r in 1..=3u16 {
25 for c in 1..=5u16 {
26 root.place_mut(Placement::at(r, c), Grid::cell().id(CellId(outer_id)));
27 outer_id += 1;
28 }
29 }
30
31 // Inner 2×2 in row 2, cols 3..=5, with 1cm left + 25% right insets.
32 let mut inner = Grid::new(
33 [Track::Fr(1.0), Track::Fr(1.0)],
34 [Track::Fr(1.0), Track::Fr(1.0)],
35 );
36 for r in 1..=2u16 {
37 for c in 1..=2u16 {
38 inner.place_mut(
39 Placement::at(r, c),
40 Grid::cell().id(CellId(100 + (r as u64) * 2 + c as u64)),
41 );
42 }
43 }
44 root.place_mut(
45 Placement::at(2, 3).span(1, 3).inset(
46 Inset::default()
47 .left(Extent::cm(1.0))
48 .right(Extent::percent(0.25)),
49 ),
50 inner,
51 );
52
53 // A 2:1 cell in the top-left, expressed via respect + fr weights.
54 root.place_mut(
55 Placement::at(1, 1),
56 Grid::new([Track::Fr(2.0)], [Track::Fr(1.0)])
57 .respect()
58 .id(CellId(200)),
59 );
60
61 let layout = root.solve(hephaestus::Size::new(w as f64, h as f64), dpi);
62
63 // Render.
64 let mut renderer = VelloRenderer::new().expect("vello renderer init");
65 {
66 let scene = renderer.scene();
67 let outer_brush: Brush = rgb8(80, 90, 110).into();
68 let outer_stroke = Stroke::new(1.0);
69 for id in 1..=15u64 {
70 if let Some(rect) = layout.rect(CellId(id)) {
71 let path: Path = rect.to_path(0.1);
72 scene.stroke(
73 &outer_stroke,
74 Affine::IDENTITY,
75 &outer_brush,
76 None,
77 &path,
78 PickId::Skip,
79 );
80 }
81 }
82
83 let inner_brush: Brush = rgb8(240, 180, 60).into();
84 let inner_stroke = Stroke::new(2.0);
85 for id in [103u64, 104, 105, 106] {
86 if let Some(rect) = layout.rect(CellId(id)) {
87 let path: Path = rect.to_path(0.1);
88 scene.fill(
89 FillRule::NonZero,
90 Affine::IDENTITY,
91 &Brush::Solid(rgb8(245, 220, 160)),
92 None,
93 &path,
94 PickId::Skip,
95 );
96 scene.stroke(
97 &inner_stroke,
98 Affine::IDENTITY,
99 &inner_brush,
100 None,
101 &path,
102 PickId::Skip,
103 );
104 }
105 }
106
107 let aspect_brush: Brush = rgb8(60, 160, 220).into();
108 if let Some(rect) = layout.rect(CellId(200)) {
109 let path: Path = rect.to_path(0.1);
110 scene.fill(
111 FillRule::NonZero,
112 Affine::IDENTITY,
113 &aspect_brush,
114 None,
115 &path,
116 PickId::Skip,
117 );
118 }
119 }
120
121 let mut pixels = vec![0u8; (w * h * 4) as usize];
122 let bg: Color = rgb8(20, 22, 28);
123 renderer
124 .render_to_buffer(w, h, bg, &mut pixels)
125 .expect("render");
126
127 let path = std::env::current_dir()
128 .unwrap()
129 .join("examples/layout_demo.png");
130 hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
131 println!("wrote {}", path.display());
132}Sourcepub fn into_measure(self) -> Box<dyn Measure>
pub fn into_measure(self) -> Box<dyn Measure>
Consume this cell and return its Measure. Callers that
need to merge multiple cells into one (e.g. the orchestrator
when several plots contribute to the same patch slot) extract
the inner measures here and wrap them in
MaxMergeMeasure.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for Cell
impl !Send for Cell
impl !Sync for Cell
impl !UnwindSafe for Cell
impl Freeze for Cell
impl Unpin for Cell
impl UnsafeUnpin for Cell
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.