layout/taffy/stylo_taffy/
wrapper.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::ops::Deref;
6
7use style::properties::ComputedValues;
8use style::values::CustomIdent;
9use style::values::computed::{GridTemplateAreas, LengthPercentage};
10use style::values::generics::grid::{TrackListValue, TrackRepeat, TrackSize};
11use style::values::specified::position::NamedArea;
12use style::{Atom, OwnedSlice};
13
14use super::{convert, stylo};
15
16/// A wrapper struct for anything that Deref's to a [`ComputedValues`], which
17/// implements Taffy's layout traits and can used with Taffy's layout algorithms.
18pub struct TaffyStyloStyle<T: Deref<Target = ComputedValues>> {
19    pub style: T,
20    pub is_compressible_replaced: bool,
21}
22
23impl<T: Deref<Target = ComputedValues>> TaffyStyloStyle<T> {
24    pub fn new(style: T, is_compressible_replaced: bool) -> Self {
25        Self {
26            style,
27            is_compressible_replaced,
28        }
29    }
30}
31
32impl<T: Deref<Target = ComputedValues>> taffy::CoreStyle for TaffyStyloStyle<T> {
33    type CustomIdent = Atom;
34
35    #[inline]
36    fn box_generation_mode(&self) -> taffy::BoxGenerationMode {
37        convert::box_generation_mode(self.style.get_box().display)
38    }
39
40    #[inline]
41    fn is_block(&self) -> bool {
42        convert::is_block(self.style.get_box().display)
43    }
44
45    #[inline]
46    fn is_compressible_replaced(&self) -> bool {
47        self.is_compressible_replaced
48    }
49
50    #[inline]
51    fn box_sizing(&self) -> taffy::BoxSizing {
52        convert::box_sizing(self.style.get_position().box_sizing)
53    }
54
55    #[inline]
56    fn overflow(&self) -> taffy::Point<taffy::Overflow> {
57        let box_styles = self.style.get_box();
58        taffy::Point {
59            x: convert::overflow(box_styles.overflow_x),
60            y: convert::overflow(box_styles.overflow_y),
61        }
62    }
63
64    #[inline]
65    fn scrollbar_width(&self) -> f32 {
66        0.0
67    }
68
69    #[inline]
70    fn position(&self) -> taffy::Position {
71        convert::position(self.style.get_box().position)
72    }
73
74    #[inline]
75    fn inset(&self) -> taffy::Rect<taffy::LengthPercentageAuto> {
76        let position_styles = self.style.get_position();
77        taffy::Rect {
78            left: convert::inset(&position_styles.left),
79            right: convert::inset(&position_styles.right),
80            top: convert::inset(&position_styles.top),
81            bottom: convert::inset(&position_styles.bottom),
82        }
83    }
84
85    #[inline]
86    fn size(&self) -> taffy::Size<taffy::Dimension> {
87        let position_styles = self.style.get_position();
88        taffy::Size {
89            width: convert::dimension(&position_styles.width),
90            height: convert::dimension(&position_styles.height),
91        }
92    }
93
94    #[inline]
95    fn min_size(&self) -> taffy::Size<taffy::Dimension> {
96        let position_styles = self.style.get_position();
97        taffy::Size {
98            width: convert::dimension(&position_styles.min_width),
99            height: convert::dimension(&position_styles.min_height),
100        }
101    }
102
103    #[inline]
104    fn max_size(&self) -> taffy::Size<taffy::Dimension> {
105        let position_styles = self.style.get_position();
106        taffy::Size {
107            width: convert::max_size_dimension(&position_styles.max_width),
108            height: convert::max_size_dimension(&position_styles.max_height),
109        }
110    }
111
112    #[inline]
113    fn aspect_ratio(&self) -> Option<f32> {
114        convert::aspect_ratio(self.style.get_position().aspect_ratio)
115    }
116
117    #[inline]
118    fn margin(&self) -> taffy::Rect<taffy::LengthPercentageAuto> {
119        let margin_styles = self.style.get_margin();
120        taffy::Rect {
121            left: convert::margin(&margin_styles.margin_left),
122            right: convert::margin(&margin_styles.margin_right),
123            top: convert::margin(&margin_styles.margin_top),
124            bottom: convert::margin(&margin_styles.margin_bottom),
125        }
126    }
127
128    #[inline]
129    fn padding(&self) -> taffy::Rect<taffy::LengthPercentage> {
130        let padding_styles = self.style.get_padding();
131        taffy::Rect {
132            left: convert::length_percentage(&padding_styles.padding_left.0),
133            right: convert::length_percentage(&padding_styles.padding_right.0),
134            top: convert::length_percentage(&padding_styles.padding_top.0),
135            bottom: convert::length_percentage(&padding_styles.padding_bottom.0),
136        }
137    }
138
139    #[inline]
140    fn border(&self) -> taffy::Rect<taffy::LengthPercentage> {
141        let border_styles = self.style.get_border();
142        taffy::Rect {
143            left: taffy::LengthPercentage::length(border_styles.border_left_width.to_f32_px()),
144            right: taffy::LengthPercentage::length(border_styles.border_right_width.to_f32_px()),
145            top: taffy::LengthPercentage::length(border_styles.border_top_width.to_f32_px()),
146            bottom: taffy::LengthPercentage::length(border_styles.border_bottom_width.to_f32_px()),
147        }
148    }
149}
150
151type SliceMapIter<'a, Input, Output> =
152    core::iter::Map<core::slice::Iter<'a, Input>, for<'c> fn(&'c Input) -> Output>;
153type SliceMapRefIter<'a, Input, Output> =
154    core::iter::Map<core::slice::Iter<'a, Input>, for<'c> fn(&'c Input) -> &'c Output>;
155
156// Line name iterator type aliases
157type LineNameSetIter<'a> = SliceMapRefIter<'a, CustomIdent, Atom>;
158type LineNameIter<'a> = core::iter::Map<
159    core::slice::Iter<'a, OwnedSlice<CustomIdent>>,
160    fn(&OwnedSlice<CustomIdent>) -> LineNameSetIter<'_>,
161>;
162
163#[derive(Clone)]
164pub struct StyloLineNameIter<'a>(LineNameIter<'a>);
165impl<'a> StyloLineNameIter<'a> {
166    fn new(names: &'a OwnedSlice<OwnedSlice<CustomIdent>>) -> Self {
167        Self(names.iter().map(|names| names.iter().map(|ident| &ident.0)))
168    }
169}
170impl<'a> Iterator for StyloLineNameIter<'a> {
171    type Item = core::iter::Map<core::slice::Iter<'a, CustomIdent>, fn(&CustomIdent) -> &Atom>;
172    fn next(&mut self) -> Option<Self::Item> {
173        self.0.next()
174    }
175    fn size_hint(&self) -> (usize, Option<usize>) {
176        self.0.size_hint()
177    }
178}
179impl ExactSizeIterator for StyloLineNameIter<'_> {}
180impl<'a> taffy::TemplateLineNames<'a, Atom> for StyloLineNameIter<'a> {
181    type LineNameSet<'b>
182        = SliceMapRefIter<'b, CustomIdent, Atom>
183    where
184        Self: 'b;
185}
186
187pub struct RepetitionWrapper<'a>(&'a TrackRepeat<LengthPercentage, i32>);
188
189impl taffy::GenericRepetition for RepetitionWrapper<'_> {
190    type CustomIdent = Atom;
191
192    type RepetitionTrackList<'a>
193        = SliceMapIter<'a, stylo::TrackSize<LengthPercentage>, taffy::TrackSizingFunction>
194    where
195        Self: 'a;
196
197    type TemplateLineNames<'a>
198        = StyloLineNameIter<'a>
199    where
200        Self: 'a;
201
202    fn count(&self) -> taffy::RepetitionCount {
203        convert::track_repeat(self.0.count)
204    }
205
206    fn tracks(&self) -> Self::RepetitionTrackList<'_> {
207        self.0.track_sizes.iter().map(convert::track_size)
208    }
209
210    fn lines_names(&self) -> Self::TemplateLineNames<'_> {
211        StyloLineNameIter::new(&self.0.line_names)
212    }
213}
214
215impl<T: Deref<Target = ComputedValues>> taffy::GridContainerStyle for TaffyStyloStyle<T> {
216    type Repetition<'a>
217        = RepetitionWrapper<'a>
218    where
219        Self: 'a;
220
221    type TemplateTrackList<'a>
222        = core::iter::Map<
223        core::slice::Iter<'a, TrackListValue<LengthPercentage, i32>>,
224        fn(
225            &'a TrackListValue<LengthPercentage, i32>,
226        ) -> taffy::GenericGridTemplateComponent<Atom, RepetitionWrapper<'a>>,
227    >
228    where
229        Self: 'a;
230
231    type AutoTrackList<'a>
232        = SliceMapIter<'a, TrackSize<LengthPercentage>, taffy::TrackSizingFunction>
233    where
234        Self: 'a;
235
236    type TemplateLineNames<'a>
237        = StyloLineNameIter<'a>
238    where
239        Self: 'a;
240    type GridTemplateAreas<'a>
241        = SliceMapIter<'a, NamedArea, taffy::GridTemplateArea<Atom>>
242    where
243        Self: 'a;
244
245    #[inline]
246    fn grid_template_rows(&self) -> Option<Self::TemplateTrackList<'_>> {
247        match &self.style.get_position().grid_template_rows {
248            stylo::GenericGridTemplateComponent::None => None,
249            stylo::GenericGridTemplateComponent::TrackList(list) => {
250                Some(list.values.iter().map(|track| match track {
251                    stylo::TrackListValue::TrackSize(size) => {
252                        taffy::GenericGridTemplateComponent::Single(convert::track_size(size))
253                    },
254                    stylo::TrackListValue::TrackRepeat(repeat) => {
255                        taffy::GenericGridTemplateComponent::Repeat(RepetitionWrapper(repeat))
256                    },
257                }))
258            },
259
260            // TODO: Implement subgrid and masonry
261            stylo::GenericGridTemplateComponent::Subgrid(_) => None,
262            stylo::GenericGridTemplateComponent::Masonry => None,
263        }
264    }
265
266    #[inline]
267    fn grid_template_columns(&self) -> Option<Self::TemplateTrackList<'_>> {
268        match &self.style.get_position().grid_template_columns {
269            stylo::GenericGridTemplateComponent::None => None,
270            stylo::GenericGridTemplateComponent::TrackList(list) => {
271                Some(list.values.iter().map(|track| match track {
272                    stylo::TrackListValue::TrackSize(size) => {
273                        taffy::GenericGridTemplateComponent::Single(convert::track_size(size))
274                    },
275                    stylo::TrackListValue::TrackRepeat(repeat) => {
276                        taffy::GenericGridTemplateComponent::Repeat(RepetitionWrapper(repeat))
277                    },
278                }))
279            },
280
281            // TODO: Implement subgrid and masonry
282            stylo::GenericGridTemplateComponent::Subgrid(_) => None,
283            stylo::GenericGridTemplateComponent::Masonry => None,
284        }
285    }
286
287    #[inline]
288    fn grid_auto_rows(&self) -> Self::AutoTrackList<'_> {
289        self.style
290            .get_position()
291            .grid_auto_rows
292            .0
293            .iter()
294            .map(convert::track_size)
295    }
296
297    #[inline]
298    fn grid_auto_columns(&self) -> Self::AutoTrackList<'_> {
299        self.style
300            .get_position()
301            .grid_auto_columns
302            .0
303            .iter()
304            .map(convert::track_size)
305    }
306
307    fn grid_template_areas(&self) -> Option<Self::GridTemplateAreas<'_>> {
308        match &self.style.get_position().grid_template_areas {
309            GridTemplateAreas::Areas(areas) => {
310                Some(areas.0.areas.iter().map(|area| taffy::GridTemplateArea {
311                    name: area.name.clone(),
312                    row_start: area.rows.start as u16,
313                    row_end: area.rows.end as u16,
314                    column_start: area.columns.start as u16,
315                    column_end: area.columns.end as u16,
316                }))
317            },
318            GridTemplateAreas::None => None,
319        }
320    }
321
322    fn grid_template_column_names(&self) -> Option<Self::TemplateLineNames<'_>> {
323        match &self.style.get_position().grid_template_columns {
324            stylo::GenericGridTemplateComponent::None => None,
325            stylo::GenericGridTemplateComponent::TrackList(list) => {
326                Some(StyloLineNameIter::new(&list.line_names))
327            },
328            // TODO: Implement subgrid and masonry
329            stylo::GenericGridTemplateComponent::Subgrid(_) => None,
330            stylo::GenericGridTemplateComponent::Masonry => None,
331        }
332    }
333
334    fn grid_template_row_names(&self) -> Option<Self::TemplateLineNames<'_>> {
335        match &self.style.get_position().grid_template_rows {
336            stylo::GenericGridTemplateComponent::None => None,
337            stylo::GenericGridTemplateComponent::TrackList(list) => {
338                Some(StyloLineNameIter::new(&list.line_names))
339            },
340            // TODO: Implement subgrid and masonry
341            stylo::GenericGridTemplateComponent::Subgrid(_) => None,
342            stylo::GenericGridTemplateComponent::Masonry => None,
343        }
344    }
345
346    #[inline]
347    fn grid_auto_flow(&self) -> taffy::GridAutoFlow {
348        convert::grid_auto_flow(self.style.get_position().grid_auto_flow)
349    }
350
351    #[inline]
352    fn gap(&self) -> taffy::Size<taffy::LengthPercentage> {
353        let position_styles = self.style.get_position();
354        taffy::Size {
355            width: convert::gap(&position_styles.column_gap),
356            height: convert::gap(&position_styles.row_gap),
357        }
358    }
359
360    #[inline]
361    fn align_content(&self) -> Option<taffy::AlignContent> {
362        convert::content_alignment(self.style.get_position().align_content.0)
363    }
364
365    #[inline]
366    fn justify_content(&self) -> Option<taffy::JustifyContent> {
367        convert::content_alignment(self.style.get_position().justify_content.0)
368    }
369
370    #[inline]
371    fn align_items(&self) -> Option<taffy::AlignItems> {
372        convert::item_alignment(self.style.get_position().align_items.0)
373    }
374
375    #[inline]
376    fn justify_items(&self) -> Option<taffy::AlignItems> {
377        convert::item_alignment(self.style.get_position().justify_items.computed.0)
378    }
379}
380
381impl<T: Deref<Target = ComputedValues>> taffy::GridItemStyle for TaffyStyloStyle<T> {
382    #[inline]
383    fn grid_row(&self) -> taffy::Line<taffy::GridPlacement<Atom>> {
384        let position_styles = self.style.get_position();
385        taffy::Line {
386            start: convert::grid_line(&position_styles.grid_row_start),
387            end: convert::grid_line(&position_styles.grid_row_end),
388        }
389    }
390
391    #[inline]
392    fn grid_column(&self) -> taffy::Line<taffy::GridPlacement<Atom>> {
393        let position_styles = self.style.get_position();
394        taffy::Line {
395            start: convert::grid_line(&position_styles.grid_column_start),
396            end: convert::grid_line(&position_styles.grid_column_end),
397        }
398    }
399
400    #[inline]
401    fn align_self(&self) -> Option<taffy::AlignSelf> {
402        convert::item_alignment(self.style.get_position().align_self.0.0)
403    }
404
405    #[inline]
406    fn justify_self(&self) -> Option<taffy::AlignSelf> {
407        convert::item_alignment(self.style.get_position().justify_self.0.0)
408    }
409}