| //! Generic CSS alignment code that is shared between both the Flexbox and CSS Grid algorithms. |
| use crate::style::AlignContent; |
| |
| /// Implement fallback alignment. |
| /// |
| /// In addition to the spec at https://www.w3.org/TR/css-align-3/ this implementation follows |
| /// the resolution of https://github.com/w3c/csswg-drafts/issues/10154 |
| pub(crate) fn apply_alignment_fallback( |
| free_space: f32, |
| num_items: usize, |
| mut alignment_mode: AlignContent, |
| mut is_safe: bool, |
| ) -> AlignContent { |
| // Fallback occurs in two cases: |
| |
| // 1. If there is only a single item being aligned and alignment is a distributed alignment keyword |
| // https://www.w3.org/TR/css-align-3/#distribution-values |
| if num_items <= 1 || free_space <= 0.0 { |
| (alignment_mode, is_safe) = match alignment_mode { |
| AlignContent::Stretch => (AlignContent::FlexStart, true), |
| AlignContent::SpaceBetween => (AlignContent::FlexStart, true), |
| AlignContent::SpaceAround => (AlignContent::Center, true), |
| AlignContent::SpaceEvenly => (AlignContent::Center, true), |
| _ => (alignment_mode, is_safe), |
| } |
| }; |
| |
| // 2. If free space is negative the "safe" alignment variants all fallback to Start alignment |
| if free_space <= 0.0 && is_safe { |
| alignment_mode = AlignContent::Start; |
| } |
| |
| alignment_mode |
| } |
| |
| /// Generic alignment function that is used: |
| /// - For both align-content and justify-content alignment |
| /// - For both the Flexbox and CSS Grid algorithms |
| /// |
| /// CSS Grid does not apply gaps as part of alignment, so the gap parameter should |
| /// always be set to zero for CSS Grid. |
| pub(crate) fn compute_alignment_offset( |
| free_space: f32, |
| num_items: usize, |
| gap: f32, |
| alignment_mode: AlignContent, |
| layout_is_flex_reversed: bool, |
| is_first: bool, |
| ) -> f32 { |
| if is_first { |
| match alignment_mode { |
| AlignContent::Start => 0.0, |
| AlignContent::FlexStart => { |
| if layout_is_flex_reversed { |
| free_space |
| } else { |
| 0.0 |
| } |
| } |
| AlignContent::End => free_space, |
| AlignContent::FlexEnd => { |
| if layout_is_flex_reversed { |
| 0.0 |
| } else { |
| free_space |
| } |
| } |
| AlignContent::Center => free_space / 2.0, |
| AlignContent::Stretch => 0.0, |
| AlignContent::SpaceBetween => 0.0, |
| AlignContent::SpaceAround => { |
| if free_space >= 0.0 { |
| (free_space / num_items as f32) / 2.0 |
| } else { |
| free_space / 2.0 |
| } |
| } |
| AlignContent::SpaceEvenly => { |
| if free_space >= 0.0 { |
| free_space / (num_items + 1) as f32 |
| } else { |
| free_space / 2.0 |
| } |
| } |
| } |
| } else { |
| let free_space = free_space.max(0.0); |
| gap + match alignment_mode { |
| AlignContent::Start => 0.0, |
| AlignContent::FlexStart => 0.0, |
| AlignContent::End => 0.0, |
| AlignContent::FlexEnd => 0.0, |
| AlignContent::Center => 0.0, |
| AlignContent::Stretch => 0.0, |
| AlignContent::SpaceBetween => free_space / (num_items - 1) as f32, |
| AlignContent::SpaceAround => free_space / num_items as f32, |
| AlignContent::SpaceEvenly => free_space / (num_items + 1) as f32, |
| } |
| } |
| } |