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