gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 1111HTML 1039Markdown 12TOML 94524 Sutichaykuna

⎇ d4374b93f0ec2c0ace42d3feafec22d3af1940d8 src/util/debug.rs Rust · 192 siq'ikuna · 6.0 KB Ñawpa willay Pi imata llamkarqan ch'uya

Refactor `LayoutTree` + add examples of custom trees (#564) d4374b93f · Nico Burns ·

1#![allow(dead_code)]
2
3use crate::tree::NodeId;
4use crate::{style, LayoutTree};
5
6#[cfg(any(feature = "debug", feature = "profile"))]
7use core::fmt::{Debug, Display, Write};
8#[cfg(any(feature = "debug", feature = "profile"))]
9use std::sync::Mutex;
10
11/// Prints a debug representation of the computed layout for a tree of nodes, starting with the passed root node.
12#[cfg(feature = "std")]
13pub fn print_tree(tree: &impl LayoutTree, root: NodeId) {
14    println!("TREE");
15    print_node(tree, root, false, String::new());
16}
17
18#[cfg(feature = "std")]
19fn print_node(tree: &impl LayoutTree, node: NodeId, has_sibling: bool, lines_string: String) {
20    let layout = &tree.get_final_layout(node);
21    let style = &tree.get_style(node);
22    let num_children = tree.child_count(node);
23
24    let display = match (num_children, style.display) {
25        (_, style::Display::None) => "NONE",
26        (0, _) => "LEAF",
27        #[cfg(feature = "block_layout")]
28        (_, style::Display::Block) => "BLOCK",
29        #[cfg(feature = "flexbox")]
30        (_, style::Display::Flex) => "FLEX",
31        #[cfg(feature = "grid")]
32        (_, style::Display::Grid) => "GRID",
33    };
34
35    let fork_string = if has_sibling { "├── " } else { "└── " };
36    println!(
37        "{lines}{fork} {display} [x: {x:<4} y: {y:<4} width: {width:<4} height: {height:<4}] ({key:?})",
38        lines = lines_string,
39        fork = fork_string,
40        display = display,
41        x = layout.location.x,
42        y = layout.location.y,
43        width = layout.size.width,
44        height = layout.size.height,
45        key = node,
46    );
47    let bar = if has_sibling { "│   " } else { "    " };
48    let new_string = lines_string + bar;
49
50    // Recurse into children
51    for (index, child) in tree.child_ids(node).enumerate() {
52        let has_sibling = index < num_children - 1;
53        print_node(tree, child, has_sibling, new_string.clone());
54    }
55}
56
57#[doc(hidden)]
58#[cfg(any(feature = "debug", feature = "profile"))]
59pub struct DebugLogger {
60    stack: Mutex<Vec<String>>,
61}
62
63#[cfg(any(feature = "debug", feature = "profile"))]
64static EMPTY_STRING: String = String::new();
65#[cfg(any(feature = "debug", feature = "profile"))]
66impl DebugLogger {
67    pub const fn new() -> Self {
68        Self { stack: Mutex::new(Vec::new()) }
69    }
70
71    pub fn push_node(&self, new_key: NodeId) {
72        let mut stack = self.stack.lock().unwrap();
73        let mut key_string = String::new();
74        write!(&mut key_string, "{:?}", new_key).unwrap();
75        stack.push(key_string);
76    }
77
78    pub fn pop_node(&self) {
79        let mut stack = self.stack.lock().unwrap();
80        stack.pop();
81    }
82
83    pub fn log(&self, message: impl Display) {
84        let stack = self.stack.lock().unwrap();
85        let key = stack.last().unwrap_or(&EMPTY_STRING);
86        let level = stack.len() * 4;
87        let space = " ";
88        println!("{space:level$}{key}: {message}");
89    }
90
91    pub fn labelled_log(&self, label: &str, message: impl Display) {
92        let stack = self.stack.lock().unwrap();
93        let key = stack.last().unwrap_or(&EMPTY_STRING);
94        let level = stack.len() * 4;
95        let space = " ";
96        println!("{space:level$}{key}: {label} {message}");
97    }
98
99    pub fn debug_log(&self, message: impl Debug) {
100        let stack = self.stack.lock().unwrap();
101        let key = stack.last().unwrap_or(&EMPTY_STRING);
102        let level = stack.len() * 4;
103        let space = " ";
104        println!("{space:level$}{key}: {message:?}");
105    }
106
107    pub fn labelled_debug_log(&self, label: &str, message: impl Debug) {
108        let stack = self.stack.lock().unwrap();
109        let key = stack.last().unwrap_or(&EMPTY_STRING);
110        let level = stack.len() * 4;
111        let space = " ";
112        println!("{space:level$}{key}: {label} {message:?}");
113    }
114}
115
116#[cfg(any(feature = "debug", feature = "profile"))]
117pub(crate) static NODE_LOGGER: DebugLogger = DebugLogger::new();
118
119macro_rules! debug_log {
120    // String literal label with debug printing
121    ($label:literal, dbg:$item:expr) => {
122        #[cfg(feature = "debug")]
123        $crate::util::debug::NODE_LOGGER.labelled_debug_log($label, $item);
124    };
125    // String literal label with display printing
126    ($label:literal, $item:expr) => {
127        #[cfg(feature = "debug")]
128        $crate::util::debug::NODE_LOGGER.labelled_log($label, $item);
129    };
130    // Debug printing
131    (dbg:$item:expr) => {
132        #[cfg(feature = "debug")]
133        $crate::util::debug::NODE_LOGGER.debug_log($item);
134    };
135    // Display printing
136    ($item:expr) => {
137        #[cfg(feature = "debug")]
138        $crate::util::debug::NODE_LOGGER.log($item);
139    };
140    // Blank newline
141    () => {
142        #[cfg(feature = "debug")]
143        println!();
144    };
145}
146
147macro_rules! debug_log_node {
148    ($known_dimensions: expr, $parent_size: expr, $available_space: expr, $run_mode: expr, $sizing_mode: expr) => {
149        debug_log!(dbg:$run_mode);
150        debug_log!("sizing_mode", dbg:$sizing_mode);
151        debug_log!("known_dimensions", dbg:$known_dimensions);
152        debug_log!("parent_size", dbg:$parent_size);
153        debug_log!("available_space", dbg:$available_space);
154    };
155}
156
157macro_rules! debug_push_node {
158    ($node_id:expr) => {
159        #[cfg(any(feature = "debug", feature = "profile"))]
160        $crate::util::debug::NODE_LOGGER.push_node($node_id);
161        debug_log!("");
162    };
163}
164
165macro_rules! debug_pop_node {
166    () => {
167        #[cfg(any(feature = "debug", feature = "profile"))]
168        $crate::util::debug::NODE_LOGGER.pop_node();
169    };
170}
171
172#[cfg(feature = "profile")]
173#[allow(unused_macros)]
174macro_rules! time {
175    ($label:expr, $($code:tt)*) => {
176        let start = ::std::time::Instant::now();
177        $($code)*
178        let duration = ::std::time::Instant::now().duration_since(start);
179        crate::util::debug::NODE_LOGGER.log(format_args!("Performed {} in {}ms", $label, duration.as_millis()));
180    };
181}
182
183#[cfg(not(feature = "profile"))]
184#[allow(unused_macros)]
185macro_rules! time {
186    ($label:expr, $($code:tt)*) => {
187        $($code)*
188    };
189}
190
191#[allow(unused_imports)]
192pub(crate) use {debug_log, debug_log_node, debug_pop_node, debug_push_node, time};