polyhorn_ui/layout/algorithm/yoga/
mod.rs1use std::cell::RefCell;
2use std::collections::HashMap;
3use std::sync::Mutex;
4
5use super::Algorithm;
6use crate::geometry::{Dimension, Point, Size};
7use crate::layout::{Layout, LayoutAxisX, MeasureFunc};
8use crate::styles::{Position, ViewStyle};
9
10mod convert;
11
12use convert::IntoYoga;
13
14pub struct Flexbox {
16 counter: usize,
17 nodes: Mutex<HashMap<usize, RefCell<yoga::Node>>>,
18}
19
20impl Flexbox {
21 fn next_id(&mut self) -> usize {
22 let id = self.counter;
23 self.counter += 1;
24 id
25 }
26}
27
28#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
30pub struct Node(usize);
31
32impl Algorithm for Flexbox {
33 type Node = Node;
34
35 fn new() -> Self {
36 Flexbox {
37 counter: 0,
38 nodes: Default::default(),
39 }
40 }
41
42 fn new_node(&mut self, style: ViewStyle, children: &[Self::Node]) -> Self::Node {
43 let id = self.next_id();
44
45 let mut nodes = self.nodes.lock().unwrap();
46 let mut node = yoga::Node::new();
47
48 for (i, child) in children.iter().enumerate() {
49 node.insert_child(&mut nodes.get(&child.0).unwrap().borrow_mut(), i as u32);
50 }
51
52 nodes.insert(id, RefCell::new(node));
53
54 std::mem::drop(nodes);
55
56 let node = Node(id);
57
58 self.set_style(node, style);
59
60 node
61 }
62
63 fn new_leaf(&mut self, style: ViewStyle, measure: MeasureFunc) -> Self::Node {
64 let id = self.next_id();
65 let node = yoga::Node::new();
66
67 self.nodes.lock().unwrap().insert(id, RefCell::new(node));
68
69 let node = Node(id);
70
71 self.set_style(node, style);
72 self.set_measure(node, measure);
73
74 node
75 }
76
77 fn add_child(&mut self, parent: Self::Node, child: Self::Node) {
78 let nodes = self.nodes.lock().unwrap();
79 let mut parent = nodes.get(&parent.0).unwrap().borrow_mut();
80 let mut child = nodes.get(&child.0).unwrap().borrow_mut();
81 let child_count = parent.child_count();
82 parent.insert_child(&mut child, child_count);
83 }
84
85 fn remove_child(&mut self, parent: Self::Node, child: Self::Node) {
86 let nodes = self.nodes.lock().unwrap();
87 let mut parent = nodes.get(&parent.0).unwrap().borrow_mut();
88 let mut child = nodes.get(&child.0).unwrap().borrow_mut();
89 parent.remove_child(&mut child);
90 }
91
92 fn child_count(&self, parent: Self::Node) -> usize {
93 self.nodes
94 .lock()
95 .unwrap()
96 .get(&parent.0)
97 .unwrap()
98 .borrow()
99 .child_count() as usize
100 }
101
102 fn remove(&mut self, node: Self::Node) {
103 let _ = self.nodes.lock().unwrap().remove(&node.0);
104 }
105
106 fn set_style(&mut self, node: Self::Node, style: ViewStyle) {
107 let nodes = self.nodes.lock().unwrap();
108 let mut node = nodes.get(&node.0).unwrap().borrow_mut();
109
110 match style.position {
111 Position::Absolute(absolute) => {
112 node.set_position_type(yoga::PositionType::Absolute);
113
114 node.set_position(yoga::Edge::Top, absolute.distances.vertical.top.into_yoga());
115 node.set_position(
116 yoga::Edge::Bottom,
117 absolute.distances.vertical.bottom.into_yoga(),
118 );
119
120 match absolute.distances.horizontal {
121 LayoutAxisX::DirectionDependent { leading, trailing } => {
122 node.set_position(yoga::Edge::Start, leading.into_yoga());
123 node.set_position(yoga::Edge::End, trailing.into_yoga());
124 }
125 LayoutAxisX::DirectionIndependent { left, right } => {
126 node.set_position(yoga::Edge::Left, left.into_yoga());
127 node.set_position(yoga::Edge::Right, right.into_yoga());
128 }
129 }
130 }
131 Position::Relative(relative) => {
132 node.set_position_type(yoga::PositionType::Relative);
133 node.set_flex_basis(relative.flex_basis.into_yoga());
134 node.set_flex_grow(relative.flex_grow);
135 node.set_flex_shrink(relative.flex_shrink);
136 }
137 };
138
139 node.set_flex_direction(style.flex_direction.into_yoga());
140 node.set_align_items(style.align_items.into_yoga());
141 node.set_justify_content(style.justify_content.into_yoga());
142
143 node.set_min_width(style.min_size.width.into_yoga());
144 node.set_width(style.size.width.into_yoga());
145 node.set_max_width(style.max_size.width.into_yoga());
146
147 node.set_min_height(style.min_size.height.into_yoga());
148 node.set_height(style.size.height.into_yoga());
149 node.set_max_height(style.max_size.height.into_yoga());
150
151 node.set_padding(yoga::Edge::Top, style.padding.vertical.top.into_yoga());
152 node.set_padding(
153 yoga::Edge::Bottom,
154 style.padding.vertical.bottom.into_yoga(),
155 );
156
157 match style.padding.horizontal {
158 LayoutAxisX::DirectionDependent { leading, trailing } => {
159 node.set_padding(yoga::Edge::Start, leading.into_yoga());
160 node.set_padding(yoga::Edge::End, trailing.into_yoga());
161 }
162 LayoutAxisX::DirectionIndependent { left, right } => {
163 node.set_padding(yoga::Edge::Left, left.into_yoga());
164 node.set_padding(yoga::Edge::Right, right.into_yoga());
165 }
166 }
167
168 node.set_margin(yoga::Edge::Top, style.margin.vertical.top.into_yoga());
169 node.set_margin(yoga::Edge::Bottom, style.margin.vertical.bottom.into_yoga());
170
171 match style.margin.horizontal {
172 LayoutAxisX::DirectionDependent { leading, trailing } => {
173 node.set_margin(yoga::Edge::Start, leading.into_yoga());
174 node.set_margin(yoga::Edge::End, trailing.into_yoga());
175 }
176 LayoutAxisX::DirectionIndependent { left, right } => {
177 node.set_margin(yoga::Edge::Left, left.into_yoga());
178 node.set_margin(yoga::Edge::Right, right.into_yoga());
179 }
180 }
181
182 node.set_overflow(style.overflow.into_yoga());
183 }
184
185 fn set_measure(&mut self, node: Self::Node, measure: MeasureFunc) {
186 let nodes = self.nodes.lock().unwrap();
187 let mut node = nodes.get(&node.0).unwrap().borrow_mut();
188
189 node.set_context(Some(yoga::Context::new(measure)));
190
191 extern "C" fn measure_fn(
192 node: yoga::NodeRef,
193 width: f32,
194 _width_mode: yoga::MeasureMode,
195 height: f32,
196 _height_mode: yoga::MeasureMode,
197 ) -> yoga::Size {
198 let measure = yoga::Node::get_context(&node)
199 .unwrap()
200 .downcast_ref::<MeasureFunc>()
201 .unwrap();
202
203 match measure {
204 MeasureFunc::Boxed(boxed) => {
205 let result = boxed(Size {
206 width: Dimension::Points(width),
207 height: Dimension::Points(height),
208 });
209
210 yoga::Size {
211 width: result.width,
212 height: result.height,
213 }
214 }
215 }
216 }
217
218 node.set_measure_func(Some(measure_fn))
219 }
220
221 fn compute_layout(&mut self, node: Self::Node, size: Size<Dimension<f32>>) {
222 let nodes = self.nodes.lock().unwrap();
223 let mut node = nodes.get(&node.0).unwrap().borrow_mut();
224
225 node.calculate_layout(
226 match size.width {
227 Dimension::Points(width) => width,
228 _ => 0.0,
229 },
230 match size.height {
231 Dimension::Points(height) => height,
232 _ => 0.0,
233 },
234 yoga::Direction::LTR,
235 );
236 }
237
238 fn layout(&self, node: Self::Node) -> Layout {
239 let nodes = self.nodes.lock().unwrap();
240 let node = nodes.get(&node.0).unwrap().borrow();
241
242 Layout {
243 origin: Point {
244 x: node.get_layout_left(),
245 y: node.get_layout_top(),
246 },
247 size: Size {
248 width: node.get_layout_width(),
249 height: node.get_layout_height(),
250 },
251 }
252 }
253}
254
255unsafe impl Send for Flexbox {}
258unsafe impl Sync for Flexbox {}