Grid/refactor coordinate handling (#301)
1//! Style types for CSS Grid layout
2use super::{AlignContent, LengthPercentage, Style};
3use crate::axis::{AbsoluteAxis, AbstractAxis};
4use crate::compute::grid::{GridCoordinate, GridLine, OriginZeroLine};
5use crate::geometry::{Line, MinMax};
6use crate::style::AvailableSpace;
7use crate::style_helpers::*;
8use crate::sys::GridTrackVec;
9use core::cmp::{max, min};
10
11/// Controls whether grid items are placed row-wise or column-wise. And whether the sparse or dense packing algorithm is used.
12///
13/// The "dense" packing algorithm attempts to fill in holes earlier in the grid, if smaller items come up later. This may cause items to appear out-of-order, when doing so would fill in holes left by larger items.
14///
15/// Defaults to [`GridAutoFlow::Row`]
16///
17/// [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow)
18#[derive(Copy, Clone, PartialEq, Eq, Debug)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20pub enum GridAutoFlow {
21 /// Items are placed by filling each row in turn, adding new rows as necessary
22 Row,
23 /// Items are placed by filling each column in turn, adding new columns as necessary.
24 Column,
25 /// Combines `Row` with the dense packing algorithm.
26 RowDense,
27 /// Combines `Column` with the dense packing algorithm.
28 ColumnDense,
29}
30
31impl Default for GridAutoFlow {
32 fn default() -> Self {
33 Self::Row
34 }
35}
36
37impl GridAutoFlow {
38 /// Whether grid auto placement uses the sparse placement algorithm or the dense placement algorithm
39 /// See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values
40 pub fn is_dense(&self) -> bool {
41 match self {
42 Self::Row | Self::Column => false,
43 Self::RowDense | Self::ColumnDense => true,
44 }
45 }
46
47 /// Whether grid auto placement fills areas row-wise or column-wise
48 /// See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-flow#values
49 pub fn primary_axis(&self) -> AbsoluteAxis {
50 match self {
51 Self::Row | Self::RowDense => AbsoluteAxis::Horizontal,
52 Self::Column | Self::ColumnDense => AbsoluteAxis::Vertical,
53 }
54 }
55}
56
57/// A grid line placement specification which is generic over the coordinate system that it uses to define
58/// grid line positions.
59///
60/// GenericGridPlacement<GridLine> is aliased as GridPlacement and is exposed to users of Taffy to define styles.
61/// GenericGridPlacement<OriginZeroLine> is aliased as OriginZeroGridPlacement and is used internally for placement computations.
62///
63/// See [`crate::compute::grid::type::coordinates`] for documentation on the different coordinate systems.
64#[derive(Copy, Clone, PartialEq, Eq, Debug)]
65#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
66pub enum GenericGridPlacement<LineType: GridCoordinate> {
67 /// Place item according to the auto-placement algorithm, and the parent's grid_auto_flow property
68 Auto,
69 /// Place item at specified line (column or row) index
70 Line(LineType),
71 /// Item should span specified number of tracks (columns or rows)
72 Span(u16),
73}
74
75/// A grid line placement using the normalized OriginZero coordinates to specify line positions.
76pub(crate) type OriginZeroGridPlacement = GenericGridPlacement<OriginZeroLine>;
77
78/// A grid line placement specification. Used for grid-[row/column]-[start/end]. Named tracks are not implemented.
79///
80/// Defaults to [`GridLine::Auto`]
81///
82/// [Specification](https://www.w3.org/TR/css3-grid-layout/#typedef-grid-row-start-grid-line)
83pub type GridPlacement = GenericGridPlacement<GridLine>;
84impl TaffyAuto for GridPlacement {
85 const AUTO: Self = Self::Auto;
86}
87impl TaffyGridLine for GridPlacement {
88 fn from_line_index(index: i16) -> Self {
89 GridPlacement::Line(GridLine::from(index))
90 }
91}
92impl TaffyGridLine for Line<GridPlacement> {
93 fn from_line_index(index: i16) -> Self {
94 Line { start: GridPlacement::from_line_index(index), end: GridPlacement::Auto }
95 }
96}
97impl TaffyGridSpan for GridPlacement {
98 fn from_span(span: u16) -> Self {
99 GridPlacement::Span(span)
100 }
101}
102impl TaffyGridSpan for Line<GridPlacement> {
103 fn from_span(span: u16) -> Self {
104 Line { start: GridPlacement::from_span(span), end: GridPlacement::Auto }
105 }
106}
107
108impl Default for GridPlacement {
109 fn default() -> Self {
110 Self::Auto
111 }
112}
113
114impl GridPlacement {
115 /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
116 pub fn into_origin_zero_placement(self, explicit_track_count: u16) -> OriginZeroGridPlacement {
117 match self {
118 Self::Auto => OriginZeroGridPlacement::Auto,
119 Self::Span(span) => OriginZeroGridPlacement::Span(span),
120 // Grid line zero is an invalid index, so it gets treated as Auto
121 // See: https://developer.mozilla.org/en-US/docs/Web/CSS/grid-row-start#values
122 Self::Line(line) => match line.as_i16() {
123 0 => OriginZeroGridPlacement::Auto,
124 _ => OriginZeroGridPlacement::Line(line.into_origin_zero_line(explicit_track_count)),
125 },
126 }
127 }
128}
129
130impl<T: GridCoordinate> Line<GenericGridPlacement<T>> {
131 #[inline]
132 /// Whether the track position is definite in this axis (or the item will need auto placement)
133 /// The track position is definite if least one of the start and end positions is a track index
134 pub fn is_definite(&self) -> bool {
135 matches!((self.start, self.end), (GenericGridPlacement::Line(_), _) | (_, GenericGridPlacement::Line(_)))
136 }
137
138 /// Resolves the span for an indefinite placement (a placement that does not consist of two `Track`s).
139 /// Panics if called on a definite placement
140 pub fn indefinite_span(&self) -> u16 {
141 use GenericGridPlacement as GP;
142 match (self.start, self.end) {
143 (GP::Line(_), GP::Auto) => 1,
144 (GP::Auto, GP::Line(_)) => 1,
145 (GP::Auto, GP::Auto) => 1,
146 (GP::Line(_), GP::Span(span)) => span,
147 (GP::Span(span), GP::Line(_)) => span,
148 (GP::Span(span), GP::Auto) => span,
149 (GP::Auto, GP::Span(span)) => span,
150 (GP::Span(span), GP::Span(_)) => span,
151 (GP::Line(_), GP::Line(_)) => panic!("indefinite_span should only be called on indefinite grid tracks"),
152 }
153 }
154}
155
156impl Line<GridPlacement> {
157 /// Apply a mapping function if the [`GridPlacement`] is a `Track`. Otherwise return `self` unmodified.
158 pub fn into_origin_zero(&self, explicit_track_count: u16) -> Line<OriginZeroGridPlacement> {
159 Line {
160 start: self.start.into_origin_zero_placement(explicit_track_count),
161 end: self.end.into_origin_zero_placement(explicit_track_count),
162 }
163 }
164}
165
166impl Line<OriginZeroGridPlacement> {
167 /// If at least one of the of the start and end positions is a track index then the other end can be resolved
168 /// into a track index purely based on the information contained with the placement specification
169 pub fn resolve_definite_grid_lines(&self) -> Line<OriginZeroLine> {
170 use OriginZeroGridPlacement as GP;
171 match (self.start, self.end) {
172 (GP::Line(line1), GP::Line(line2)) => {
173 if line1 == line2 {
174 Line { start: line1, end: line1 + 1 }
175 } else {
176 Line { start: min(line1, line2), end: max(line1, line2) }
177 }
178 }
179 (GP::Line(line), GP::Span(span)) => Line { start: line, end: line + span },
180 (GP::Line(line), GP::Auto) => Line { start: line, end: line + 1 },
181 (GP::Span(span), GP::Line(line)) => Line { start: line - span, end: line },
182 (GP::Auto, GP::Line(line)) => Line { start: line - 1, end: line },
183 _ => panic!("resolve_definite_grid_tracks should only be called on definite grid tracks"),
184 }
185 }
186
187 /// For absolutely positioned items:
188 /// - Tracks resolve to definite tracks
189 /// - For Spans:
190 /// - If the other position is a Track, they resolve to a definite track relative to the other track
191 /// - Else resolve to None
192 /// - Auto resolves to None
193 ///
194 /// When finally positioning the item, a value of None means that the item's grid area is bounded by the grid
195 /// container's border box on that side.
196 pub fn resolve_absolutely_positioned_grid_tracks(&self) -> Line<Option<OriginZeroLine>> {
197 use OriginZeroGridPlacement as GP;
198 match (self.start, self.end) {
199 (GP::Line(track1), GP::Line(track2)) => {
200 if track1 == track2 {
201 Line { start: Some(track1), end: Some(track1 + 1) }
202 } else {
203 Line { start: Some(min(track1, track2)), end: Some(max(track1, track2)) }
204 }
205 }
206 (GP::Line(track), GP::Span(span)) => Line { start: Some(track), end: Some(track + span) },
207 (GP::Line(track), GP::Auto) => Line { start: Some(track), end: None },
208 (GP::Span(span), GP::Line(track)) => Line { start: Some(track - span), end: Some(track) },
209 (GP::Auto, GP::Line(track)) => Line { start: None, end: Some(track) },
210 _ => Line { start: None, end: None },
211 }
212 }
213
214 /// If neither of the start and end positions is a track index then the other end can be resolved
215 /// into a track index if a definite start position is supplied externally
216 pub fn resolve_indefinite_grid_tracks(&self, start: OriginZeroLine) -> Line<OriginZeroLine> {
217 use OriginZeroGridPlacement as GP;
218 match (self.start, self.end) {
219 (GP::Auto, GP::Auto) => Line { start, end: start + 1 },
220 (GP::Span(span), GP::Auto) => Line { start, end: start + span },
221 (GP::Auto, GP::Span(span)) => Line { start, end: start + span },
222 (GP::Span(span), GP::Span(_)) => Line { start, end: start + span },
223 _ => panic!("resolve_indefinite_grid_tracks should only be called on indefinite grid tracks"),
224 }
225 }
226}
227
228/// Represents the start and end points of a GridItem within a given axis
229impl Default for Line<GridPlacement> {
230 fn default() -> Self {
231 Line { start: GridPlacement::Auto, end: GridPlacement::Auto }
232 }
233}
234
235/// Maximum track sizing function
236///
237/// Specifies the maximum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
238/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
239/// See https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns
240#[derive(Copy, Clone, PartialEq, Debug)]
241pub enum MaxTrackSizingFunction {
242 /// Track maximum size should be a fixed points or percentage value
243 Fixed(LengthPercentage),
244 /// Track maximum size should be content sized under a min-content constraint
245 MinContent,
246 /// Track maximum size should be content sized under a max-content constraint
247 MaxContent,
248 /// Track maximum size should be sized according to the fit-content formula
249 FitContent(LengthPercentage),
250 /// Track maximum size should be automatically sized
251 Auto,
252 /// The dimension as a fraction of the total available grid space.
253 /// Specified value is the numerator of the fraction. Denominator is the sum of all fraction specified in that grid dimension
254 /// Spec: https://www.w3.org/TR/css3-grid-layout/#fr-unit
255 Flex(f32),
256}
257impl TaffyAuto for MaxTrackSizingFunction {
258 const AUTO: Self = Self::Auto;
259}
260impl TaffyMinContent for MaxTrackSizingFunction {
261 const MIN_CONTENT: Self = Self::MinContent;
262}
263impl TaffyMaxContent for MaxTrackSizingFunction {
264 const MAX_CONTENT: Self = Self::MaxContent;
265}
266impl TaffyFitContent for MaxTrackSizingFunction {
267 fn fit_content(argument: LengthPercentage) -> Self {
268 Self::FitContent(argument)
269 }
270}
271impl TaffyZero for MaxTrackSizingFunction {
272 const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
273}
274impl FromPoints for MaxTrackSizingFunction {
275 fn from_points<Input: Into<f32> + Copy>(points: Input) -> Self {
276 Self::Fixed(LengthPercentage::from_points(points))
277 }
278}
279impl FromPercent for MaxTrackSizingFunction {
280 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
281 Self::Fixed(LengthPercentage::from_percent(percent))
282 }
283}
284impl FromFlex for MaxTrackSizingFunction {
285 fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
286 Self::Flex(flex.into())
287 }
288}
289
290impl MaxTrackSizingFunction {
291 /// Returns true if the max track sizing function is `MinContent`, `MaxContent` or `Auto`, else false.
292 #[inline(always)]
293 pub fn is_intrinsic(&self) -> bool {
294 matches!(self, Self::MinContent | Self::MaxContent | Self::FitContent(_) | Self::Auto)
295 }
296
297 /// Returns true if the max track sizing function is `MaxContent`, `FitContent` or `Auto` else false.
298 /// "In all cases, treat auto and fit-content() as max-content, except where specified otherwise for fit-content()."
299 /// See: https://www.w3.org/TR/css-grid-1/#algo-terms
300 #[inline(always)]
301 pub fn is_max_content_alike(&self) -> bool {
302 matches!(self, Self::MaxContent | Self::FitContent(_) | Self::Auto)
303 }
304
305 /// Returns true if the max track sizing function is `Flex`, else false.
306 #[inline(always)]
307 pub fn is_flexible(&self) -> bool {
308 matches!(self, Self::Flex(_))
309 }
310
311 /// Returns fixed point values directly. Attempts to resolve percentage values against
312 /// the passed available_space and returns if this results in a concrete value (which it
313 /// will if the available_space is `Some`). Otherwise returns None.
314 #[inline(always)]
315 pub fn definite_value(self, available_space: AvailableSpace) -> Option<f32> {
316 use MaxTrackSizingFunction::{Auto, *};
317 match self {
318 Fixed(LengthPercentage::Points(size)) => Some(size),
319 Fixed(LengthPercentage::Percent(fraction)) => match available_space {
320 AvailableSpace::Definite(available_size) => Some(fraction * available_size),
321 _ => None,
322 },
323 MinContent | MaxContent | FitContent(_) | Auto | Flex(_) => None,
324 }
325 }
326}
327
328/// Minimum track sizing function
329///
330/// Specifies the minimum size of a grid track. A grid track will automatically size between it's minimum and maximum size based
331/// on the size of it's contents, the amount of available space, and the sizing constraint the grid is being size under.
332/// See https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns
333#[derive(Copy, Clone, PartialEq, Debug)]
334pub enum MinTrackSizingFunction {
335 /// Track minimum size should be a fixed points or percentage value
336 Fixed(LengthPercentage),
337 /// Track minimum size should be content sized under a min-content constraint
338 MinContent,
339 /// Track minimum size should be content sized under a max-content constraint
340 MaxContent,
341 /// Track minimum size should be automatically sized
342 Auto,
343}
344impl TaffyAuto for MinTrackSizingFunction {
345 const AUTO: Self = Self::Auto;
346}
347impl TaffyMinContent for MinTrackSizingFunction {
348 const MIN_CONTENT: Self = Self::MinContent;
349}
350impl TaffyMaxContent for MinTrackSizingFunction {
351 const MAX_CONTENT: Self = Self::MaxContent;
352}
353impl TaffyZero for MinTrackSizingFunction {
354 const ZERO: Self = Self::Fixed(LengthPercentage::ZERO);
355}
356impl FromPoints for MinTrackSizingFunction {
357 fn from_points<Input: Into<f32> + Copy>(points: Input) -> Self {
358 Self::Fixed(LengthPercentage::from_points(points))
359 }
360}
361impl FromPercent for MinTrackSizingFunction {
362 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
363 Self::Fixed(LengthPercentage::from_percent(percent))
364 }
365}
366
367impl MinTrackSizingFunction {
368 /// Returns fixed point values directly. Attempts to resolve percentage values against
369 /// the passed available_space and returns if this results in a concrete value (which it
370 /// will if the available_space is `Some`). Otherwise returns `None`.
371 #[inline(always)]
372 pub fn definite_value(self, available_space: AvailableSpace) -> Option<f32> {
373 use MinTrackSizingFunction::{Auto, *};
374 match self {
375 Fixed(LengthPercentage::Points(size)) => Some(size),
376 Fixed(LengthPercentage::Percent(fraction)) => match available_space {
377 AvailableSpace::Definite(available_size) => Some(fraction * available_size),
378 _ => None,
379 },
380 MinContent | MaxContent | Auto => None,
381 }
382 }
383}
384
385/// The sizing function for a grid track (row/column) (either auto-track or template track)
386/// May either be a MinMax variant which specifies separate values for the min-/max- track sizing functions
387/// or a scalar value which applies to both track sizing functions.
388pub type NonRepeatedTrackSizingFunction = MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>;
389impl NonRepeatedTrackSizingFunction {
390 /// Extract the min track sizing function
391 pub fn min_sizing_function(&self) -> MinTrackSizingFunction {
392 self.min
393 }
394 /// Extract the max track sizing function
395 pub fn max_sizing_function(&self) -> MaxTrackSizingFunction {
396 self.max
397 }
398 /// Determine whether at least one of the components ("min" and "max") are fixed sizing function
399 pub fn has_fixed_component(&self) -> bool {
400 matches!(self.min, MinTrackSizingFunction::Fixed(_)) || matches!(self.max, MaxTrackSizingFunction::Fixed(_))
401 }
402}
403impl TaffyAuto for NonRepeatedTrackSizingFunction {
404 const AUTO: Self = Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::AUTO };
405}
406impl TaffyMinContent for NonRepeatedTrackSizingFunction {
407 const MIN_CONTENT: Self =
408 Self { min: MinTrackSizingFunction::MIN_CONTENT, max: MaxTrackSizingFunction::MIN_CONTENT };
409}
410impl TaffyMaxContent for NonRepeatedTrackSizingFunction {
411 const MAX_CONTENT: Self =
412 Self { min: MinTrackSizingFunction::MAX_CONTENT, max: MaxTrackSizingFunction::MAX_CONTENT };
413}
414impl TaffyFitContent for NonRepeatedTrackSizingFunction {
415 fn fit_content(argument: LengthPercentage) -> Self {
416 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::FitContent(argument) }
417 }
418}
419impl TaffyZero for NonRepeatedTrackSizingFunction {
420 const ZERO: Self = Self { min: MinTrackSizingFunction::ZERO, max: MaxTrackSizingFunction::ZERO };
421}
422impl FromPoints for NonRepeatedTrackSizingFunction {
423 fn from_points<Input: Into<f32> + Copy>(points: Input) -> Self {
424 Self { min: MinTrackSizingFunction::from_points(points), max: MaxTrackSizingFunction::from_points(points) }
425 }
426}
427impl FromPercent for NonRepeatedTrackSizingFunction {
428 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
429 Self { min: MinTrackSizingFunction::from_percent(percent), max: MaxTrackSizingFunction::from_percent(percent) }
430 }
431}
432impl FromFlex for NonRepeatedTrackSizingFunction {
433 fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
434 Self { min: MinTrackSizingFunction::AUTO, max: MaxTrackSizingFunction::from_flex(flex) }
435 }
436}
437
438/// The first argument to a repeated track definition. This type represents the type of automatic repetition to perform.
439///
440/// See https://www.w3.org/TR/css-grid-1/#auto-repeat for an explanation of how auto-repeated track definitions work
441/// and the difference between AutoFit and AutoFill.
442#[derive(Clone, Copy, Debug, PartialEq, Eq)]
443pub enum GridTrackRepetition {
444 /// Auto-repeating track should be generated to fit the container
445 /// See: https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fill
446 AutoFill,
447 /// Auto-repeating track should be generated to fit the container
448 /// See: https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit
449 AutoFit,
450}
451
452/// The sizing function for a grid track (row/column)
453/// See https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns
454#[derive(Clone, PartialEq, Debug)]
455pub enum TrackSizingFunction {
456 /// A single non-repeated track
457 Single(NonRepeatedTrackSizingFunction),
458 /// Automatically generate grid tracks to fit the available space using the specified definite track lengths
459 /// Only valid if every track in template (not just the repitition) has a fixed size.
460 AutoRepeat(GridTrackRepetition, GridTrackVec<NonRepeatedTrackSizingFunction>),
461}
462impl TrackSizingFunction {
463 /// Whether the track definition is a auto-repeated fragment
464 pub fn is_auto_repetition(&self) -> bool {
465 matches!(self, Self::AutoRepeat(_, _))
466 }
467}
468impl TaffyAuto for TrackSizingFunction {
469 const AUTO: Self = Self::Single(NonRepeatedTrackSizingFunction::AUTO);
470}
471impl TaffyMinContent for TrackSizingFunction {
472 const MIN_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MIN_CONTENT);
473}
474impl TaffyMaxContent for TrackSizingFunction {
475 const MAX_CONTENT: Self = Self::Single(NonRepeatedTrackSizingFunction::MAX_CONTENT);
476}
477impl TaffyFitContent for TrackSizingFunction {
478 fn fit_content(argument: LengthPercentage) -> Self {
479 Self::Single(NonRepeatedTrackSizingFunction::fit_content(argument))
480 }
481}
482impl TaffyZero for TrackSizingFunction {
483 const ZERO: Self = Self::Single(NonRepeatedTrackSizingFunction::ZERO);
484}
485impl FromPoints for TrackSizingFunction {
486 fn from_points<Input: Into<f32> + Copy>(points: Input) -> Self {
487 Self::Single(NonRepeatedTrackSizingFunction::from_points(points))
488 }
489}
490impl FromPercent for TrackSizingFunction {
491 fn from_percent<Input: Into<f32> + Copy>(percent: Input) -> Self {
492 Self::Single(NonRepeatedTrackSizingFunction::from_percent(percent))
493 }
494}
495impl FromFlex for TrackSizingFunction {
496 fn from_flex<Input: Into<f32> + Copy>(flex: Input) -> Self {
497 Self::Single(NonRepeatedTrackSizingFunction::from_flex(flex))
498 }
499}
500impl From<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> for TrackSizingFunction {
501 fn from(input: MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>) -> Self {
502 Self::Single(input)
503 }
504}
505
506// Grid extensions to the Style struct
507impl Style {
508 /// Get a grid item's row or column placement depending on the axis passed
509 pub(crate) fn grid_template_tracks(&self, axis: AbsoluteAxis) -> &GridTrackVec<TrackSizingFunction> {
510 match axis {
511 AbsoluteAxis::Horizontal => &self.grid_template_columns,
512 AbsoluteAxis::Vertical => &self.grid_template_rows,
513 }
514 }
515
516 /// Get a grid item's row or column placement depending on the axis passed
517 pub(crate) fn grid_placement(&self, axis: AbsoluteAxis) -> Line<GridPlacement> {
518 match axis {
519 AbsoluteAxis::Horizontal => self.grid_column,
520 AbsoluteAxis::Vertical => self.grid_row,
521 }
522 }
523
524 /// Get a grid container's align-content or justify-content alignment depending on the axis passed
525 pub(crate) fn grid_align_content(&self, axis: AbstractAxis) -> AlignContent {
526 match axis {
527 AbstractAxis::Inline => self.justify_content.unwrap_or(AlignContent::Stretch),
528 AbstractAxis::Block => self.align_content.unwrap_or(AlignContent::Stretch),
529 }
530 }
531}