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