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