1use blitz_traits::node_id::NodeId;
2use parley::layout::PositionedLayoutItem;
3
4use crate::BaseDocument;
5use crate::layout::TaffyDebugTree;
6
7impl BaseDocument {
8 pub fn print_taffy_tree(&self) {
9 let root_id = crate::taffy_node_id(self.root_element().id);
10 taffy::print_tree(&TaffyDebugTree(self), root_id);
11 for &node_id in &self.sub_document_nodes {
12 if let Some(sub_doc) = self.nodes[node_id].subdoc() {
13 println!("\n=== Subdocument (node {node_id:?}) ===");
14 sub_doc.inner().print_taffy_tree();
15 }
16 }
17 }
18
19 pub fn debug_log_node(&self, node_id: NodeId) {
20 let node = &self.nodes[node_id];
21
22 #[cfg(feature = "tracing")]
23 {
24 tracing::info!("Layout: {:?}", node.final_layout());
25 tracing::info!("Display: {:?}", node.taffy_display());
26 }
27
28 println!("\nNode {} {}", node.id, node.node_debug_str());
29
30 println!("Attrs:");
31
32 for attr in node.attrs().into_iter().flatten() {
33 println!(" {}: {}", attr.name.local, attr.value);
34 }
35
36 if node.flags.is_inline_root() {
37 let inline_layout = &node
38 .data
39 .downcast_element()
40 .unwrap()
41 .inline_layout_data
42 .as_ref()
43 .unwrap();
44
45 println!(
46 "Size: {}x{}",
47 inline_layout.layout.width(),
48 inline_layout.layout.height()
49 );
50 println!("Text content: {:?}", inline_layout.text);
51 println!("Inline Boxes:");
52 for ibox in inline_layout.layout.inline_boxes() {
53 print!("(id: {}) ", ibox.id);
54 }
55 println!();
56 println!("Lines:");
57 for (i, line) in inline_layout.layout.lines().enumerate() {
58 let metrics = line.metrics();
59 let x = metrics.inline_min_coord;
60 let y = metrics.block_min_coord;
61 let w = metrics.inline_max_coord - metrics.inline_min_coord;
62 let h = metrics.block_max_coord - metrics.block_min_coord;
63 println!("Line {i}: x:{x} y:{y} width:{w} height:{h}");
64 for item in line.items() {
65 print!(" ");
66 match item {
67 PositionedLayoutItem::GlyphRun(run) => {
68 print!(
69 "RUN (x: {}, w: {}) ",
70 run.offset().round(),
71 run.run().advance()
72 )
73 }
74 PositionedLayoutItem::InlineBox(ibox) => print!(
75 "BOX {:?} (id: {} x: {} y: {} w: {}, h: {})",
76 ibox.kind,
77 ibox.id,
78 ibox.x.round(),
79 ibox.y.round(),
80 ibox.width.round(),
81 ibox.height.round()
82 ),
83 }
84 println!();
85 }
86 }
87 }
88
89 let layout = node.final_layout();
90 println!("Layout:");
91 println!(
92 " x: {x} y: {y} w: {width} h: {height} overflow: l:{ol} r:{or} t:{ot} b:{ob}",
93 x = layout.location.x,
94 y = layout.location.y,
95 width = layout.size.width,
96 height = layout.size.height,
97 ol = layout.scrollable_overflow_rect.left,
98 or = layout.scrollable_overflow_rect.right,
99 ot = layout.scrollable_overflow_rect.top,
100 ob = layout.scrollable_overflow_rect.bottom,
101 );
102 println!(
103 " border: l:{l} r:{r} t:{t} b:{b}",
104 l = layout.border.left,
105 r = layout.border.right,
106 t = layout.border.top,
107 b = layout.border.bottom,
108 );
109 println!(
110 " padding: l:{l} r:{r} t:{t} b:{b}",
111 l = layout.padding.left,
112 r = layout.padding.right,
113 t = layout.padding.top,
114 b = layout.padding.bottom,
115 );
116 println!(
117 " margin: l:{l} r:{r} t:{t} b:{b}",
118 l = layout.margin.left,
119 r = layout.margin.right,
120 t = layout.margin.top,
121 b = layout.margin.bottom,
122 );
123 println!("Parent: {:?}", node.parent);
124
125 let children: Vec<_> = node
126 .children
127 .iter()
128 .map(|id| &self.nodes[*id])
129 .map(|node| (node.id, node.order(), node.node_debug_str()))
130 .collect();
131 println!("Children: {children:?}");
132
133 println!("Layout Parent: {:?}", node.layout_parent.get());
134
135 let layout_children: Option<Vec<_>> = node.layout_children.borrow().as_ref().map(|lc| {
136 lc.iter()
137 .map(|id| &self.nodes[*id])
138 .map(|node| (node.id, node.order(), node.node_debug_str()))
139 .collect()
140 });
141 if let Some(layout_children) = layout_children {
142 println!("Layout Children: {layout_children:?}");
143 }
144
145 let paint_children: Option<Vec<_>> = node.paint_children.borrow().as_ref().map(|lc| {
146 lc.iter()
147 .map(|id| &self.nodes[*id])
148 .map(|node| (node.id, node.order(), node.node_debug_str()))
149 .collect()
150 });
151 if let Some(paint_children) = paint_children {
152 println!("Paint Children: {paint_children:?}");
153 }
154 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161 use crate::{DocumentConfig, qual_name, taffy_node_id};
162 use blitz_traits::shell::{ColorScheme, Viewport};
163 use taffy::{PrintTree, TraversePartialTree};
164
165 #[test]
166 fn prints_tree_from_shared_document_reference() {
167 let mut doc = BaseDocument::new(DocumentConfig {
168 viewport: Some(Viewport::new(400, 300, 1.0, ColorScheme::Light)),
169 ..Default::default()
170 });
171 let root = doc.root_node().id;
172 let mut mutator = doc.mutate();
173 let html = mutator.create_element(qual_name!("html", html), vec![]);
174 let body = mutator.create_element(qual_name!("body", html), vec![]);
175 let child = mutator.create_element(qual_name!("div", html), vec![]);
176 mutator.set_style_property(child, "height", "20px");
177 mutator.append_children(body, &[child]);
178 mutator.append_children(html, &[body]);
179 mutator.append_children(root, &[html]);
180 drop(mutator);
181 doc.resolve(0.0);
182
183 let doc: &BaseDocument = &doc;
184 doc.print_taffy_tree();
185
186 let tree = TaffyDebugTree(doc);
187 let body_id = taffy_node_id(body);
188 let child_id = taffy_node_id(child);
189 assert_eq!(tree.child_count(body_id), 1);
190 assert_eq!(tree.get_child_id(body_id, 0), child_id);
191 assert_eq!(tree.child_ids(body_id).collect::<Vec<_>>(), [child_id]);
192 assert_eq!(tree.child_count(child_id), 0);
193 assert_eq!(tree.child_ids(child_id).count(), 0);
194 assert_eq!(tree.get_final_layout(child_id).size.height, 20.0);
195 assert_eq!(
196 tree.get_final_layout(child_id),
197 *doc.nodes[child].final_layout()
198 );
199
200 let mut output = Vec::new();
201 taffy::write_tree(&mut output, &tree, taffy_node_id(html)).unwrap();
202 let output = String::from_utf8(output).unwrap();
203 assert_eq!(output.lines().count(), 4);
204 assert!(output.contains("BLOCK"));
205 }
206}