1use hephaestus::backend::vello::VelloRenderer;
18use hephaestus::color::{rgb8, Color};
19use hephaestus::composition::{beside, Patch, Slot};
20use hephaestus::layout::Cell;
21use hephaestus::text::{draw_text_in_rect, TextRun, TextStyle};
22use hephaestus::{Affine, Brush, FillRule, Path, PickId, Renderer, SceneBuilder};
23use kurbo::Shape;
24
25fn text_cell(text: &str, size: f32) -> Cell {
26 Cell::measured(TextRun::new(text, &TextStyle::new(size), 96.0))
27}
28
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 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 let mid = beside(plain("mid"), leaf);
57 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 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}