1use hephaestus::backend::vello::VelloRenderer;
28use hephaestus::color::{rgb8, Color};
29use hephaestus::composition::{beside, grid, Patch, Slot};
30use hephaestus::layout::Cell;
31use hephaestus::text::{draw_text_in_rect, TextRun, TextStyle};
32use hephaestus::{Affine, Brush, FillRule, Path, PickId, Renderer, SceneBuilder};
33use kurbo::Shape;
34
35fn text_cell(text: &str, size: f32) -> Cell {
36 Cell::measured(TextRun::new(text, &TextStyle::new(size), 96.0))
37}
38
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}
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 let plain = plain_plot("plain");
62 let nested = grid(
63 1,
64 3,
65 vec![
66 plain_plot("c1").into(),
67 plot_with_axis_top("c2", "this is a TALL axis_top label\nspanning multiple lines\nto stress chrome propagation").into(),
68 plain_plot("c3").into(),
69 ],
70 );
71 let composed = beside(plain, nested);
72 let layout = composed.solve(hephaestus::Size::new(w as f64, h as f64), dpi);
73
74 let mut renderer = VelloRenderer::new().expect("vello renderer init");
75 {
76 let scene = renderer.scene();
77 let stroke = hephaestus::stroke::Stroke::new(1.0);
78 let text_brush: Brush = rgb8(20, 20, 30).into();
79
80 for (_id, region, rect) in layout.iter() {
81 if region == "panel" {
82 continue;
83 }
84 let c = color_for_region(region);
85 let tint = Color::new([c.components[0], c.components[1], c.components[2], 0.20]);
86 let path: Path = rect.to_path(0.1);
87 scene.fill(
88 FillRule::NonZero,
89 Affine::IDENTITY,
90 &Brush::Solid(tint),
91 None,
92 &path,
93 PickId::Skip,
94 );
95 scene.stroke(
96 &stroke,
97 Affine::IDENTITY,
98 &Brush::Solid(c),
99 None,
100 &path,
101 PickId::Skip,
102 );
103 }
104 for (_id, region, rect) in layout.iter() {
105 if region != "panel" {
106 continue;
107 }
108 let path: Path = rect.to_path(0.1);
109 scene.fill(
110 FillRule::NonZero,
111 Affine::IDENTITY,
112 &Brush::Solid(color_for_region(region)),
113 None,
114 &path,
115 PickId::Skip,
116 );
117 scene.stroke(
118 &stroke,
119 Affine::IDENTITY,
120 &Brush::Solid(rgb8(255, 255, 255)),
121 None,
122 &path,
123 PickId::Skip,
124 );
125 }
126 if let Some(rect) = layout.get("c2", Slot::AxisTop) {
128 let run = TextRun::new(
129 "this is a TALL axis_top label\nspanning multiple lines\nto stress chrome propagation",
130 &TextStyle::new(14.0),
131 96.0,
132 );
133 draw_text_in_rect(scene, &run, rect, &text_brush, PickId::Skip);
134 }
135 }
136
137 let mut pixels = vec![0u8; (w * h * 4) as usize];
138 let bg: Color = rgb8(248, 248, 252);
139 renderer
140 .render_to_buffer(w, h, bg, &mut pixels)
141 .expect("render");
142
143 let path = std::env::current_dir()
144 .unwrap()
145 .join("examples/nesting_chrome_coupling.png");
146 hephaestus::image::write_png(&path, w, h, &pixels).expect("write png");
147 println!("wrote {}", path.display());
148
149 let plain_y = layout.get("plain", Slot::Panel).unwrap();
153 let c1_y = layout.get("c1", Slot::Panel).unwrap();
154 let c2_y = layout.get("c2", Slot::Panel).unwrap();
155 let c3_y = layout.get("c3", Slot::Panel).unwrap();
156 println!(
157 "panel y0: plain={}, c1={}, c2={}, c3={}",
158 plain_y.y0, c1_y.y0, c2_y.y0, c3_y.y0
159 );
160 println!("(all four should be equal — bidirectional sizer coupling at work)");
161}