1use core::fmt::{Debug, Display, Write};
2use slotmap::Key;
3use std::sync::Mutex;
4
5use crate::node::Node;
6use crate::style;
7use crate::tree::LayoutTree;
8
9/// Prints a debug representation of the computed layout for a tree of nodes, starting with the passed root node.
10pub fn print_tree(tree: &impl LayoutTree, root: Node) {
11 println!("TREE");
12 print_node(tree, root, false, String::new());
13}
14
15fn print_node(tree: &impl LayoutTree, node: Node, has_sibling: bool, lines_string: String) {
16 let layout = tree.layout(node);
17 let style = tree.style(node);
18
19 let num_children = tree.child_count(node);
20
21 let display = match (num_children, style.display) {
22 (_, style::Display::None) => "NONE",
23 (0, _) => "LEAF",
24 (_, style::Display::Flex) => "FLEX",
25 #[cfg(feature = "grid")]
26 (_, style::Display::Grid) => "GRID",
27 };
28
29 let fork_string = if has_sibling { "├── " } else { "└── " };
30 println!(
31 "{lines}{fork} {display} [x: {x:<4} y: {y:<4} width: {width:<4} height: {height:<4}] ({key:?})",
32 lines = lines_string,
33 fork = fork_string,
34 display = display,
35 x = layout.location.x,
36 y = layout.location.y,
37 width = layout.size.width,
38 height = layout.size.height,
39 key = node.data(),
40 );
41 let bar = if has_sibling { "│ " } else { " " };
42 let new_string = lines_string + bar;
43
44 // Recurse into children
45 for (index, child) in tree.children(node).enumerate() {
46 let has_sibling = index < num_children - 1;
47 print_node(tree, *child, has_sibling, new_string.clone());
48 }
49}
50
51#[doc(hidden)]
52pub struct DebugLogger {
53 stack: Mutex<Vec<String>>,
54}
55
56static EMPTY_STRING: String = String::new();
57impl DebugLogger {
58 pub const fn new() -> Self {
59 Self { stack: Mutex::new(Vec::new()) }
60 }
61
62 pub fn push_node(&self, new_key: impl Key) {
63 let mut stack = self.stack.lock().unwrap();
64 let mut key_string = String::new();
65 write!(&mut key_string, "{:?}", new_key.data()).unwrap();
66 stack.push(key_string);
67 }
68
69 pub fn pop_node(&self) {
70 let mut stack = self.stack.lock().unwrap();
71 stack.pop();
72 }
73
74 pub fn log(&self, message: impl Display) {
75 let stack = self.stack.lock().unwrap();
76 let key = stack.last().unwrap_or(&EMPTY_STRING);
77 let level = stack.len() * 4;
78 let space = " ";
79 println!("{space:level$}{key}: {message}");
80 }
81
82 pub fn labelled_log(&self, label: &str, message: impl Display) {
83 let stack = self.stack.lock().unwrap();
84 let key = stack.last().unwrap_or(&EMPTY_STRING);
85 let level = stack.len() * 4;
86 let space = " ";
87 println!("{space:level$}{key}: {label} {message}");
88 }
89
90 pub fn debug_log(&self, message: impl Debug) {
91 let stack = self.stack.lock().unwrap();
92 let key = stack.last().unwrap_or(&EMPTY_STRING);
93 let level = stack.len() * 4;
94 let space = " ";
95 println!("{space:level$}{key}: {message:?}");
96 }
97
98 pub fn labelled_debug_log(&self, label: &str, message: impl Debug) {
99 let stack = self.stack.lock().unwrap();
100 let key = stack.last().unwrap_or(&EMPTY_STRING);
101 let level = stack.len() * 4;
102 let space = " ";
103 println!("{space:level$}{key}: {label} {message:?}");
104 }
105}
106
107#[cfg(any(feature = "debug", feature = "profile"))]
108pub(crate) static NODE_LOGGER: DebugLogger = DebugLogger::new();
109
110#[cfg(feature = "profile")]
111#[allow(unused_macros)]
112macro_rules! time {
113 ($label:expr, $($code:tt)*) => {
114 let start = ::std::time::Instant::now();
115 $($code)*
116 let duration = ::std::time::Instant::now().duration_since(start);
117 crate::debug::NODE_LOGGER.log(format_args!("Performed {} in {}ms", $label, duration.as_millis()));
118 };
119}
120
121#[cfg(not(feature = "profile"))]
122#[allow(unused_macros)]
123macro_rules! time {
124 ($label:expr, $($code:tt)*) => {
125 $($code)*
126 };
127}
128
129#[allow(unused_imports)]
130pub(crate) use time;