Skip to main content

blitz_dom/
debug.rs

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        // taffy::print_tree(&self.dom, node_id.into());
155    }
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}