Skip to main content

Cell

Struct Cell 

Source
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

Source

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?
examples/nesting_deep.rs (line 30)
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
Hide additional examples
examples/nesting_fixed_aspect.rs (line 48)
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}
examples/nesting_chrome_coupling.rs (line 40)
39fn plain_plot(id: &str) -> Patch {
40    Patch::new(id).slot(Slot::Panel, Cell::empty())
41}
42
43fn plot_with_axis_top(id: &str, axis_top_text: &str) -> Patch {
44    Patch::new(id)
45        .slot(Slot::AxisTop, text_cell(axis_top_text, 14.0))
46        .slot(Slot::Panel, Cell::empty())
47}
examples/nesting_outer_chrome_propagates.rs (line 46)
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}
examples/nesting_faceted_title.rs (line 48)
44fn facet(id: &str, axis_left: &str, axis_bottom: &str) -> Patch {
45    Patch::new(id)
46        .slot(Slot::AxisLeft, text_cell(axis_left, 11.0))
47        .slot(Slot::AxisBottom, text_cell(axis_bottom, 11.0))
48        .slot(Slot::Panel, Cell::empty())
49}
examples/nesting_asymmetric.rs (line 30)
25fn plot(id: &str, title: &str, axis_left: &str, axis_bottom: &str) -> Patch {
26    Patch::new(id)
27        .slot(Slot::Title, text_cell(title, 16.0))
28        .slot(Slot::AxisLeft, text_cell(axis_left, 11.0))
29        .slot(Slot::AxisBottom, text_cell(axis_bottom, 11.0))
30        .slot(Slot::Panel, Cell::empty())
31}
Source

pub fn measured(m: impl Measure + 'static) -> Self

A leaf whose intrinsic size comes from m.

Examples found in repository?
examples/composition_demo.rs (line 20)
19fn text_cell(text: &str, size: f32) -> Cell {
20    Cell::measured(TextRun::new(text, &TextStyle::new(size), 96.0))
21}
22
23fn weighted_text_cell(text: &str, size: f32, weight: u16) -> Cell {
24    Cell::measured(TextRun::new(
25        text,
26        &TextStyle::new(size).weight(weight),
27        96.0,
28    ))
29}
More examples
Hide additional examples
examples/nesting_asymmetric.rs (line 22)
21fn text_cell(text: &str, size: f32) -> Cell {
22    Cell::measured(TextRun::new(text, &TextStyle::new(size), 96.0))
23}
examples/nesting_chrome_coupling.rs (line 36)
35fn text_cell(text: &str, size: f32) -> Cell {
36    Cell::measured(TextRun::new(text, &TextStyle::new(size), 96.0))
37}
examples/nesting_deep.rs (line 26)
25fn text_cell(text: &str, size: f32) -> Cell {
26    Cell::measured(TextRun::new(text, &TextStyle::new(size), 96.0))
27}
examples/nesting_faceted_title.rs (line 33)
32fn text_cell(text: &str, size: f32) -> Cell {
33    Cell::measured(TextRun::new(text, &TextStyle::new(size), 96.0))
34}
35
36fn weighted_text_cell(text: &str, size: f32, weight: u16) -> Cell {
37    Cell::measured(TextRun::new(
38        text,
39        &TextStyle::new(size).weight(weight),
40        96.0,
41    ))
42}
examples/nesting_fixed_aspect.rs (line 44)
43fn text_cell(text: &str, size: f32) -> Cell {
44    Cell::measured(TextRun::new(text, &TextStyle::new(size), 96.0))
45}
Source

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.

Source

pub fn id(self, id: CellId) -> Self

Tag this cell so its resolved rect is retrievable from Layout::rect.

Examples found in repository?
examples/layout_demo.rs (line 26)
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}
Source

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.

Source

pub fn cell_id(&self) -> Option<CellId>

Borrow this cell’s identifier tag, if any.

Trait Implementations§

Source§

impl From<Cell> for Node

Source§

fn from(c: Cell) -> Self

Converts to this type from the input type.

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> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert 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>

Convert 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)

Convert &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)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, S> SimdFrom<T, S> for T
where S: Simd,

Source§

fn simd_from(_simd: S, value: T) -> T

Source§

impl<F, T, S> SimdInto<T, S> for F
where T: SimdFrom<F, S>, S: Simd,

Source§

fn simd_into(self, simd: S) -> T

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: Sized + ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more