1use crate::style::{ComputedStyle, DisplayInner};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct NodeId(pub usize);
11
12impl NodeId {
13 pub const ROOT: Self = NodeId(0);
14}
15
16#[derive(Debug, Clone)]
18pub struct TextContent {
19 pub text: String,
20}
21
22#[derive(Debug, Clone)]
24pub enum NodeKind {
25 Element,
27 Text(TextContent),
29 AnonymousBlock,
31 AnonymousInline,
33}
34
35#[derive(Debug, Clone)]
37pub struct BoxTreeNode {
38 pub id: NodeId,
39 pub kind: NodeKind,
40 pub style: ComputedStyle,
41 pub children: Vec<NodeId>,
42 pub parent: Option<NodeId>,
43}
44
45impl BoxTreeNode {
46 pub fn new(id: NodeId, kind: NodeKind, style: ComputedStyle) -> Self {
47 Self {
48 id,
49 kind,
50 style,
51 children: Vec::new(),
52 parent: None,
53 }
54 }
55
56 pub fn is_text(&self) -> bool {
57 matches!(self.kind, NodeKind::Text(_))
58 }
59
60 pub fn text_content(&self) -> Option<&str> {
61 match &self.kind {
62 NodeKind::Text(tc) => Some(&tc.text),
63 _ => None,
64 }
65 }
66}
67
68#[derive(Debug, Clone)]
70pub struct BoxTree {
71 nodes: Vec<BoxTreeNode>,
72 root: NodeId,
73}
74
75impl BoxTree {
76 pub fn new() -> Self {
77 Self {
78 nodes: Vec::new(),
79 root: NodeId(0),
80 }
81 }
82
83 pub fn add_node(&mut self, kind: NodeKind, style: ComputedStyle) -> NodeId {
85 let id = NodeId(self.nodes.len());
86 self.nodes.push(BoxTreeNode::new(id, kind, style));
87 id
88 }
89
90 pub fn set_root(&mut self, id: NodeId) {
92 self.root = id;
93 }
94
95 pub fn append_child(&mut self, parent: NodeId, child: NodeId) {
97 self.nodes[child.0].parent = Some(parent);
98 self.nodes[parent.0].children.push(child);
99 }
100
101 pub fn node(&self, id: NodeId) -> &BoxTreeNode {
103 &self.nodes[id.0]
104 }
105
106 pub fn node_mut(&mut self, id: NodeId) -> &mut BoxTreeNode {
108 &mut self.nodes[id.0]
109 }
110
111 pub fn root(&self) -> NodeId {
113 self.root
114 }
115
116 pub fn children(&self, id: NodeId) -> &[NodeId] {
118 &self.nodes[id.0].children
119 }
120
121 pub fn style(&self, id: NodeId) -> &ComputedStyle {
123 &self.nodes[id.0].style
124 }
125
126 pub fn parent(&self, id: NodeId) -> Option<NodeId> {
128 self.nodes[id.0].parent
129 }
130
131 pub fn len(&self) -> usize {
133 self.nodes.len()
134 }
135
136 pub fn is_empty(&self) -> bool {
137 self.nodes.is_empty()
138 }
139
140 pub fn formatting_context(&self, id: NodeId) -> FormattingContextType {
142 let style = self.style(id);
143 match style.display.inner {
144 DisplayInner::Flex => FormattingContextType::Flex,
145 DisplayInner::Grid => FormattingContextType::Grid,
146 DisplayInner::Table => FormattingContextType::Table,
147 DisplayInner::Flow | DisplayInner::FlowRoot => {
148 let children = self.children(id);
150 if children.is_empty() {
151 return FormattingContextType::Block;
152 }
153
154 let has_block = children.iter().any(|&c| {
155 let cs = self.style(c);
156 cs.display.is_block_level() && !cs.is_out_of_flow()
157 });
158
159 if has_block {
160 FormattingContextType::Block
161 } else {
162 FormattingContextType::Inline
163 }
164 }
165 _ => FormattingContextType::Block,
166 }
167 }
168}
169
170impl Default for BoxTree {
171 fn default() -> Self {
172 Self::new()
173 }
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178pub enum FormattingContextType {
179 Block,
180 Inline,
181 Flex,
182 Grid,
183 Table,
184}
185
186pub struct BoxTreeBuilder {
188 pub tree: BoxTree,
189}
190
191impl BoxTreeBuilder {
192 pub fn new() -> Self {
193 Self {
194 tree: BoxTree::new(),
195 }
196 }
197
198 pub fn root(&mut self, style: ComputedStyle) -> NodeId {
200 let id = self.tree.add_node(NodeKind::Element, style);
201 self.tree.set_root(id);
202 id
203 }
204
205 pub fn element(&mut self, parent: NodeId, style: ComputedStyle) -> NodeId {
207 let id = self.tree.add_node(NodeKind::Element, style);
208 self.tree.append_child(parent, id);
209 id
210 }
211
212 pub fn text(&mut self, parent: NodeId, text: &str) -> NodeId {
214 let id = self.tree.add_node(
215 NodeKind::Text(TextContent {
216 text: text.to_string(),
217 }),
218 ComputedStyle::inline(),
219 );
220 self.tree.append_child(parent, id);
221 id
222 }
223
224 pub fn build(self) -> BoxTree {
226 self.tree
227 }
228}
229
230impl Default for BoxTreeBuilder {
231 fn default() -> Self {
232 Self::new()
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use crate::style::ComputedStyle;
240
241 #[test]
242 fn test_build_simple_tree() {
243 let mut builder = BoxTreeBuilder::new();
244 let root = builder.root(ComputedStyle::block());
245 let child1 = builder.element(root, ComputedStyle::block());
246 let child2 = builder.element(root, ComputedStyle::block());
247 builder.text(child1, "Hello");
248 let tree = builder.build();
249
250 assert_eq!(tree.len(), 4);
251 assert_eq!(tree.children(root).len(), 2);
252 assert_eq!(tree.children(child1).len(), 1);
253 assert_eq!(tree.children(child2).len(), 0);
254 assert_eq!(tree.parent(child1), Some(root));
255 }
256
257 #[test]
258 fn test_formatting_context_detection() {
259 let mut builder = BoxTreeBuilder::new();
260 let root = builder.root(ComputedStyle::block());
261 builder.element(root, ComputedStyle::block());
262 builder.element(root, ComputedStyle::block());
263 let tree = builder.build();
264
265 assert_eq!(tree.formatting_context(root), FormattingContextType::Block);
266 }
267}