1mod limits;
3mod node;
4
5pub mod flex;
6
7pub use limits::Limits;
8pub use node::Node;
9
10use crate::{Length, Padding, Point, Rectangle, Size, Vector};
11
12#[derive(Debug, Clone, Copy)]
14pub struct Layout<'a> {
15 position: Point,
16 node: &'a Node,
17}
18
19impl<'a> Layout<'a> {
20 pub fn new(node: &'a Node) -> Self {
22 Self::with_offset(Vector::new(0.0, 0.0), node)
23 }
24
25 pub fn with_offset(offset: Vector, node: &'a Node) -> Self {
28 let bounds = node.bounds();
29
30 Self {
31 position: Point::new(bounds.x, bounds.y) + offset,
32 node,
33 }
34 }
35
36 pub fn position(&self) -> Point {
38 self.position
39 }
40
41 pub fn bounds(&self) -> Rectangle {
46 let bounds = self.node.bounds();
47
48 Rectangle {
49 x: self.position.x,
50 y: self.position.y,
51 width: bounds.width,
52 height: bounds.height,
53 }
54 }
55
56 pub fn children(self) -> impl DoubleEndedIterator<Item = Layout<'a>> + ExactSizeIterator {
58 self.node.children().iter().map(move |node| {
59 Layout::with_offset(Vector::new(self.position.x, self.position.y), node)
60 })
61 }
62
63 pub fn child(self, index: usize) -> Layout<'a> {
71 let node = &self.node.children()[index];
72
73 Layout::with_offset(Vector::new(self.position.x, self.position.y), node)
74 }
75}
76
77pub fn next_to_each_other(
79 limits: &Limits,
80 spacing: f32,
81 left: impl FnOnce(&Limits) -> Node,
82 right: impl FnOnce(&Limits) -> Node,
83) -> Node {
84 let left_node = left(limits);
85 let left_size = left_node.size();
86
87 let right_limits = limits.shrink(Size::new(left_size.width + spacing, 0.0));
88
89 let right_node = right(&right_limits);
90 let right_size = right_node.size();
91
92 let (left_y, right_y) = if left_size.height > right_size.height {
93 (0.0, (left_size.height - right_size.height) / 2.0)
94 } else {
95 ((right_size.height - left_size.height) / 2.0, 0.0)
96 };
97
98 Node::with_children(
99 Size::new(
100 left_size.width + spacing + right_size.width,
101 left_size.height.max(right_size.height),
102 ),
103 vec![
104 left_node.move_to(Point::new(0.0, left_y)),
105 right_node.move_to(Point::new(left_size.width + spacing, right_y)),
106 ],
107 )
108}
109
110pub fn atomic(limits: &Limits, width: impl Into<Length>, height: impl Into<Length>) -> Node {
113 let width = width.into();
114 let height = height.into();
115
116 Node::new(
117 limits
118 .width(width)
119 .height(height)
120 .resolve(width, height, Size::ZERO),
121 )
122}
123
124pub fn sized(
128 limits: &Limits,
129 width: impl Into<Length>,
130 height: impl Into<Length>,
131 f: impl FnOnce(&Limits) -> Size,
132) -> Node {
133 let width = width.into();
134 let height = height.into();
135
136 let limits = limits.width(width).height(height);
137 let intrinsic_size = f(&limits);
138
139 Node::new(limits.resolve(width, height, intrinsic_size))
140}
141
142pub fn contained(
146 limits: &Limits,
147 width: impl Into<Length>,
148 height: impl Into<Length>,
149 f: impl FnOnce(&Limits) -> Node,
150) -> Node {
151 let width = width.into();
152 let height = height.into();
153
154 let limits = limits.width(width).height(height);
155 let content = f(&limits);
156
157 Node::with_children(limits.resolve(width, height, content.size()), vec![content])
158}
159
160pub fn padded(
164 limits: &Limits,
165 width: impl Into<Length>,
166 height: impl Into<Length>,
167 padding: impl Into<Padding>,
168 layout: impl FnOnce(&Limits) -> Node,
169) -> Node {
170 positioned(limits, width, height, padding, layout, |content, _| content)
171}
172
173pub fn positioned(
175 limits: &Limits,
176 width: impl Into<Length>,
177 height: impl Into<Length>,
178 padding: impl Into<Padding>,
179 layout: impl FnOnce(&Limits) -> Node,
180 position: impl FnOnce(Node, Size) -> Node,
181) -> Node {
182 let width = width.into();
183 let height = height.into();
184 let padding = padding.into();
185
186 let limits = limits.width(width).height(height);
187 let content = layout(&limits.shrink(padding));
188 let padding = padding.fit(content.size(), limits.max());
189
190 let size = limits
191 .shrink(padding)
192 .resolve(width, height, content.size());
193
194 Node::with_children(
195 size.expand(padding),
196 vec![position(content.move_to((padding.left, padding.top)), size)],
197 )
198}