1use crate::geometry::{Edges, Point, Rect, Size};
7use crate::tree::NodeId;
8
9#[derive(Debug, Clone)]
11pub struct Fragment {
12 pub node: NodeId,
14 pub position: Point,
16 pub size: Size,
18 pub padding: Edges,
20 pub border: Edges,
22 pub margin: Edges,
24 pub children: Vec<Fragment>,
26 pub kind: FragmentKind,
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum FragmentKind {
33 Box,
35 AnonymousBox,
37 LineBox,
39 TextRun,
41}
42
43impl Fragment {
44 pub fn new(node: NodeId, kind: FragmentKind) -> Self {
45 Self {
46 node,
47 position: Point::ZERO,
48 size: Size::ZERO,
49 padding: Edges::ZERO,
50 border: Edges::ZERO,
51 margin: Edges::ZERO,
52 children: Vec::new(),
53 kind,
54 }
55 }
56
57 pub fn border_box(&self) -> Rect {
59 Rect::new(
60 self.position.x,
61 self.position.y,
62 self.size.width + self.padding.horizontal() + self.border.horizontal(),
63 self.size.height + self.padding.vertical() + self.border.vertical(),
64 )
65 }
66
67 pub fn margin_box(&self) -> Rect {
69 Rect::new(
70 self.position.x - self.margin.left,
71 self.position.y - self.margin.top,
72 self.size.width
73 + self.padding.horizontal()
74 + self.border.horizontal()
75 + self.margin.horizontal(),
76 self.size.height
77 + self.padding.vertical()
78 + self.border.vertical()
79 + self.margin.vertical(),
80 )
81 }
82
83 pub fn content_box(&self) -> Rect {
85 Rect::new(
86 self.position.x + self.border.left + self.padding.left,
87 self.position.y + self.border.top + self.padding.top,
88 self.size.width,
89 self.size.height,
90 )
91 }
92
93 pub fn absolute_position(&self, ancestors: &[&Fragment]) -> Point {
96 let mut x = self.position.x;
97 let mut y = self.position.y;
98 for ancestor in ancestors.iter().rev() {
99 x += ancestor.position.x + ancestor.border.left + ancestor.padding.left;
100 y += ancestor.position.y + ancestor.border.top + ancestor.padding.top;
101 }
102 Point::new(x, y)
103 }
104}
105
106#[derive(Debug, Clone)]
108pub struct LayoutResult {
109 pub root: Fragment,
111}
112
113impl LayoutResult {
114 pub fn find_fragment(&self, node: NodeId) -> Option<&Fragment> {
116 find_in_fragment(&self.root, node)
117 }
118
119 pub fn get_layout(&self, node: NodeId) -> Option<LayoutOutput> {
121 let mut path = Vec::new();
123 if find_path_to_node(&self.root, node, &mut path) {
124 let fragment = path.last().unwrap();
125 let ancestors: Vec<&Fragment> = path[..path.len() - 1].to_vec();
126 let abs_pos = fragment.absolute_position(&ancestors);
127 Some(LayoutOutput {
128 position: abs_pos,
129 size: fragment.size,
130 padding: fragment.padding,
131 border: fragment.border,
132 margin: fragment.margin,
133 })
134 } else {
135 None
136 }
137 }
138
139 pub fn bounding_rect(&self, node: NodeId) -> Option<Rect> {
141 self.get_layout(node).map(|l| {
142 Rect::new(
143 l.position.x,
144 l.position.y,
145 l.size.width + l.padding.horizontal() + l.border.horizontal(),
146 l.size.height + l.padding.vertical() + l.border.vertical(),
147 )
148 })
149 }
150}
151
152#[derive(Debug, Clone, Copy, PartialEq)]
154pub struct LayoutOutput {
155 pub position: Point,
157 pub size: Size,
159 pub padding: Edges,
161 pub border: Edges,
163 pub margin: Edges,
165}
166
167fn find_in_fragment(fragment: &Fragment, node: NodeId) -> Option<&Fragment> {
168 if fragment.node == node {
169 return Some(fragment);
170 }
171 for child in &fragment.children {
172 if let Some(found) = find_in_fragment(child, node) {
173 return Some(found);
174 }
175 }
176 None
177}
178
179fn find_path_to_node<'a>(
180 fragment: &'a Fragment,
181 node: NodeId,
182 path: &mut Vec<&'a Fragment>,
183) -> bool {
184 path.push(fragment);
185 if fragment.node == node {
186 return true;
187 }
188 for child in &fragment.children {
189 if find_path_to_node(child, node, path) {
190 return true;
191 }
192 }
193 path.pop();
194 false
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn test_border_box() {
203 let mut f = Fragment::new(NodeId(0), FragmentKind::Box);
204 f.position = Point::new(10.0, 20.0);
205 f.size = Size::new(100.0, 50.0);
206 f.padding = Edges::all(5.0);
207 f.border = Edges::all(1.0);
208
209 let bb = f.border_box();
210 assert_eq!(bb.width, 112.0); assert_eq!(bb.height, 62.0); }
213}