gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 492HTML 245JavaScript 7CSS 51137 Sutichaykuna

⎇ 89f50ccb7d37e4d4b78a8250eca82eeaf63ad3de benches/src/yoga_helpers.rs Rust · 263 siq'ikuna · 10.8 KB Ñawpa willay Pi imata llamkarqan ch'uya

Add support for display: flow-root 89f50ccb7 · nico.burns ·

1#![allow(dead_code)]
2
3use rand::distr::uniform::SampleRange;
4use rand::Rng;
5use rand_chacha::ChaCha8Rng;
6use slotmap::{DefaultKey, SlotMap};
7
8use super::{BuildTree, BuildTreeExt, GenStyle};
9
10pub mod yg {
11    pub use ordered_float::OrderedFloat;
12    pub use slotmap::{DefaultKey, SlotMap};
13    pub use yoga::types::*;
14    pub use yoga::Node;
15
16    pub type NodeId = DefaultKey;
17    pub type YogaTree = SlotMap<DefaultKey, Node>;
18}
19mod tf {
20    pub use taffy::prelude::*;
21}
22use tf::Style as TaffyStyle;
23
24pub struct YogaTreeBuilder<R: Rng, G: GenStyle<TaffyStyle>> {
25    rng: R,
26    style_generator: G,
27    tree: yg::YogaTree,
28    root: yg::DefaultKey,
29}
30
31// Implement the BuildTree trait
32impl<R: Rng, G: GenStyle<TaffyStyle>> BuildTree<R, G> for YogaTreeBuilder<R, G> {
33    const NAME: &'static str = "Yoga";
34    type Tree = yg::YogaTree;
35    type Node = DefaultKey;
36
37    fn with_rng(mut rng: R, mut style_generator: G) -> Self {
38        let mut tree = SlotMap::new();
39        let root = create_yg_node(&mut tree, &style_generator.create_root_style(&mut rng), &[]);
40        YogaTreeBuilder { rng, style_generator, tree, root }
41    }
42
43    fn compute_layout_inner(&mut self, available_width: Option<f32>, available_height: Option<f32>) {
44        self.tree[self.root].calculate_layout(
45            available_width.unwrap_or(f32::INFINITY),
46            available_height.unwrap_or(f32::INFINITY),
47            yg::Direction::LTR,
48        )
49    }
50
51    fn random_usize(&mut self, range: impl SampleRange<usize>) -> usize {
52        self.rng.random_range(range)
53    }
54
55    fn create_leaf_node(&mut self) -> Self::Node {
56        let style = self.style_generator.create_leaf_style(&mut self.rng);
57        create_yg_node(&mut self.tree, &style, &[])
58    }
59
60    fn create_container_node(&mut self, children: &[Self::Node]) -> Self::Node {
61        let style = self.style_generator.create_container_style(&mut self.rng);
62        create_yg_node(&mut self.tree, &style, &children)
63    }
64
65    fn set_root_children(&mut self, children: &[Self::Node]) {
66        set_node_children(&mut self.tree, self.root, &children);
67    }
68
69    fn total_node_count(&mut self) -> usize {
70        self.tree.len()
71    }
72
73    fn into_tree_and_root(self) -> (Self::Tree, Self::Node) {
74        (self.tree, self.root)
75    }
76}
77
78impl<G: GenStyle<TaffyStyle>> BuildTreeExt<G> for YogaTreeBuilder<ChaCha8Rng, G> {}
79
80// impl<R: Rng, G: GenStyle<TaffyStyle>> YogaTreeBuilder<R, G> {
81//     /// Create a YogaTreeBuilder with a standard rng from a style generator
82//     fn new<NG: GenStyle<TaffyStyle>>(mut style_generator: NG) -> YogaTreeBuilder<ChaCha8Rng, NG> {
83//         let mut rng = ChaCha8Rng::seed_from_u64(STANDARD_RNG_SEED);
84//         let mut tree = SlotMap::new();
85//         let root = create_yg_node(&mut tree, &style_generator.create_root_style(&mut rng), &[]);
86//         YogaTreeBuilder { rng, style_generator, tree, root }
87//     }
88
89//     /// Create a YogaTreeBuilder with a standard rng from a style generator
90//     fn with_seed<NG: GenStyle<TaffyStyle>>(seed: u64, mut style_generator: NG) -> YogaTreeBuilder<ChaCha8Rng, NG> {
91//         let mut rng = ChaCha8Rng::seed_from_u64(seed);
92//         let mut tree = SlotMap::new();
93//         let root = create_yg_node(&mut tree, &style_generator.create_root_style(&mut rng), &[]);
94//         YogaTreeBuilder { rng, style_generator, tree, root }
95//     }
96
97//     /// Create a YogaTreeBuilder from a random number generator and a style generator
98//     fn with_rng<NR: Rng, NG: GenStyle<TaffyStyle>>(mut rng: NR, mut style_generator: NG) -> YogaTreeBuilder<NR, NG> {
99//         let mut tree = SlotMap::new();
100//         let root = create_yg_node(&mut tree, &style_generator.create_root_style(&mut rng), &[]);
101//         YogaTreeBuilder { rng, style_generator, tree, root }
102//     }
103// }
104
105fn create_yg_node(tree: &mut yg::YogaTree, style: &tf::Style, children: &[yg::DefaultKey]) -> yg::DefaultKey {
106    let mut node = yg::Node::new();
107    apply_taffy_style(&mut node, &style);
108    for (i, child) in children.into_iter().enumerate() {
109        node.insert_child(&mut tree[*child], i);
110    }
111    tree.insert(node)
112}
113
114pub fn new_default_style_with_children(tree: &mut yg::YogaTree, children: &[yg::DefaultKey]) -> yg::DefaultKey {
115    let mut node = yg::Node::new();
116    for (i, child) in children.into_iter().enumerate() {
117        node.insert_child(&mut tree[*child], i);
118    }
119    tree.insert(node)
120}
121
122fn set_node_children(tree: &mut yg::YogaTree, node_id: yg::DefaultKey, children: &[yg::DefaultKey]) {
123    // TODO: clear existing children.
124    for (i, child_id) in children.into_iter().enumerate() {
125        let [node, child] = tree.get_disjoint_mut([node_id, *child_id]).unwrap();
126        node.insert_child(child, i);
127    }
128}
129
130fn into_yg_units(dim: impl Into<tf::Dimension>) -> yg::StyleUnit {
131    let dim: tf::Dimension = dim.into();
132    let val = dim.into_raw().value();
133    match dim.into_raw().tag() {
134        tf::CompactLength::AUTO_TAG => yg::StyleUnit::Auto,
135        tf::CompactLength::LENGTH_TAG => yg::StyleUnit::Point(yg::OrderedFloat(val)),
136        tf::CompactLength::PERCENT_TAG => yg::StyleUnit::Percent(yg::OrderedFloat(val)),
137        _ => unreachable!(),
138    }
139}
140
141fn into_pixels(dim: impl Into<tf::Dimension>) -> f32 {
142    dim.into().into_option().unwrap_or(0.0)
143}
144
145fn items_into_align(align: Option<tf::AlignSelf>) -> yg::Align {
146    // Yoga has no safe/unsafe overflow-position concept — drop the safety field and dispatch
147    // on the bare keyword. Safe and unsafe alike fold to the same yoga keyword.
148    let Some(align) = align else { return yg::Align::Auto };
149    match align.keyword {
150        tf::AlignItemsKeyword::FlexStart => yg::Align::FlexStart,
151        tf::AlignItemsKeyword::FlexEnd => yg::Align::FlexEnd,
152        tf::AlignItemsKeyword::Center => yg::Align::Center,
153        tf::AlignItemsKeyword::Baseline => yg::Align::Baseline,
154        tf::AlignItemsKeyword::Stretch => yg::Align::Stretch,
155        tf::AlignItemsKeyword::Start | tf::AlignItemsKeyword::End => unimplemented!(),
156    }
157}
158
159fn content_into_align(align: Option<tf::AlignContent>) -> yg::Align {
160    let Some(align) = align else { return yg::Align::Auto };
161    match align.keyword {
162        tf::AlignContentKeyword::FlexStart | tf::AlignContentKeyword::Start => yg::Align::FlexStart,
163        tf::AlignContentKeyword::FlexEnd | tf::AlignContentKeyword::End => yg::Align::FlexEnd,
164        tf::AlignContentKeyword::Center => yg::Align::Center,
165        tf::AlignContentKeyword::Stretch => yg::Align::Stretch,
166        tf::AlignContentKeyword::SpaceBetween => yg::Align::SpaceBetween,
167        tf::AlignContentKeyword::SpaceAround => yg::Align::SpaceAround,
168        tf::AlignContentKeyword::SpaceEvenly => unimplemented!(),
169    }
170}
171
172fn content_into_justify(align: Option<tf::JustifyContent>) -> yg::Justify {
173    let Some(align) = align else { return yg::Justify::FlexStart };
174    match align.keyword {
175        tf::AlignContentKeyword::FlexStart | tf::AlignContentKeyword::Start => yg::Justify::FlexStart,
176        tf::AlignContentKeyword::FlexEnd | tf::AlignContentKeyword::End => yg::Justify::FlexEnd,
177        tf::AlignContentKeyword::Center => yg::Justify::Center,
178        tf::AlignContentKeyword::SpaceBetween => yg::Justify::SpaceBetween,
179        tf::AlignContentKeyword::SpaceAround => yg::Justify::SpaceAround,
180        tf::AlignContentKeyword::Stretch | tf::AlignContentKeyword::SpaceEvenly => unimplemented!(),
181    }
182}
183
184fn apply_taffy_style(node: &mut yg::Node, style: &tf::Style) {
185    // display
186    node.set_display(match style.display {
187        tf::Display::None => yg::Display::None,
188        tf::Display::Flex => yg::Display::Flex,
189        tf::Display::Grid => panic!("Yoga does not support CSS Grid layout"),
190        tf::Display::Block => panic!("Yoga does not support CSS Block layout"),
191        tf::Display::FlowRoot => panic!("Yoga does not support CSS Block layout"),
192    });
193
194    // box_sizing
195    node.set_box_sizing(match style.box_sizing {
196        tf::BoxSizing::BorderBox => yg::BoxSizing::BorderBox,
197        tf::BoxSizing::ContentBox => yg::BoxSizing::ContentBox,
198    });
199
200    // position
201    node.set_position_type(match style.position {
202        tf::Position::Relative => yg::PositionType::Relative,
203        tf::Position::Absolute => yg::PositionType::Absolute,
204    });
205    // inset
206    node.set_position(yg::Edge::Left, into_yg_units(style.inset.left));
207    node.set_position(yg::Edge::Right, into_yg_units(style.inset.right));
208    node.set_position(yg::Edge::Top, into_yg_units(style.inset.top));
209    node.set_position(yg::Edge::Bottom, into_yg_units(style.inset.bottom));
210
211    // sizes
212    node.set_width(into_yg_units(style.size.width));
213    node.set_height(into_yg_units(style.size.height));
214    node.set_min_width(into_yg_units(style.min_size.width));
215    node.set_min_height(into_yg_units(style.min_size.height));
216    node.set_max_width(into_yg_units(style.max_size.width));
217    node.set_max_height(into_yg_units(style.max_size.height));
218
219    // aspect_ratio
220    if let Some(aspect_ratio) = style.aspect_ratio {
221        node.set_aspect_ratio(aspect_ratio);
222    }
223
224    // spacing
225    node.set_padding(yg::Edge::Left, into_yg_units(style.padding.left));
226    node.set_padding(yg::Edge::Right, into_yg_units(style.padding.right));
227    node.set_padding(yg::Edge::Top, into_yg_units(style.padding.top));
228    node.set_padding(yg::Edge::Bottom, into_yg_units(style.padding.bottom));
229    node.set_margin(yg::Edge::Left, into_yg_units(style.margin.left));
230    node.set_margin(yg::Edge::Right, into_yg_units(style.margin.right));
231    node.set_margin(yg::Edge::Top, into_yg_units(style.margin.top));
232    node.set_margin(yg::Edge::Bottom, into_yg_units(style.margin.bottom));
233    node.set_border(yg::Edge::Left, into_pixels(style.border.left));
234    node.set_border(yg::Edge::Right, into_pixels(style.border.right));
235    node.set_border(yg::Edge::Top, into_pixels(style.border.top));
236    node.set_border(yg::Edge::Bottom, into_pixels(style.border.bottom));
237
238    // alignment
239    node.set_align_items(items_into_align(style.align_items));
240    node.set_align_self(items_into_align(style.align_self));
241    node.set_align_content(content_into_align(style.align_content));
242    node.set_justify_content(content_into_justify(style.justify_content));
243
244    // gap
245    node.set_column_gap(into_yg_units(style.gap.width));
246    node.set_row_gap(into_yg_units(style.gap.height));
247
248    // flex
249    node.set_flex_direction(match style.flex_direction {
250        tf::FlexDirection::Row => yg::FlexDirection::Row,
251        tf::FlexDirection::Column => yg::FlexDirection::Column,
252        tf::FlexDirection::RowReverse => yg::FlexDirection::RowReverse,
253        tf::FlexDirection::ColumnReverse => yg::FlexDirection::ColumnReverse,
254    });
255    node.set_flex_wrap(match style.flex_wrap {
256        tf::FlexWrap::NoWrap => yg::Wrap::NoWrap,
257        tf::FlexWrap::Wrap => yg::Wrap::Wrap,
258        tf::FlexWrap::WrapReverse => yg::Wrap::WrapReverse,
259    });
260    node.set_flex_basis(into_yg_units(style.flex_basis));
261    node.set_flex_grow(style.flex_grow);
262    node.set_flex_shrink(style.flex_shrink);
263}