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