Skip to main content

polyhorn_layout/algorithm/yoga/
mod.rs

1use 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}