taffy/compute/common/
alignment.rs

1//! Generic CSS alignment code that is shared between both the Flexbox and CSS Grid algorithms.
2use crate::style::AlignContent;
3
4/// Implement fallback alignment.
5///
6/// In addition to the spec at https://www.w3.org/TR/css-align-3/ this implementation follows
7/// the resolution of https://github.com/w3c/csswg-drafts/issues/10154
8pub(crate) fn apply_alignment_fallback(
9    free_space: f32,
10    num_items: usize,
11    mut alignment_mode: AlignContent,
12    mut is_safe: bool,
13) -> AlignContent {
14    // Fallback occurs in two cases:
15
16    // 1. If there is only a single item being aligned and alignment is a distributed alignment keyword
17    //    https://www.w3.org/TR/css-align-3/#distribution-values
18    if num_items <= 1 || free_space <= 0.0 {
19        (alignment_mode, is_safe) = match alignment_mode {
20            AlignContent::Stretch => (AlignContent::FlexStart, true),
21            AlignContent::SpaceBetween => (AlignContent::FlexStart, true),
22            AlignContent::SpaceAround => (AlignContent::Center, true),
23            AlignContent::SpaceEvenly => (AlignContent::Center, true),
24            _ => (alignment_mode, is_safe),
25        }
26    };
27
28    // 2. If free space is negative the "safe" alignment variants all fallback to Start alignment
29    if free_space <= 0.0 && is_safe {
30        alignment_mode = AlignContent::Start;
31    }
32
33    alignment_mode
34}
35
36/// Generic alignment function that is used:
37///   - For both align-content and justify-content alignment
38///   - For both the Flexbox and CSS Grid algorithms
39///
40/// CSS Grid does not apply gaps as part of alignment, so the gap parameter should
41/// always be set to zero for CSS Grid.
42pub(crate) fn compute_alignment_offset(
43    free_space: f32,
44    num_items: usize,
45    gap: f32,
46    alignment_mode: AlignContent,
47    layout_is_flex_reversed: bool,
48    is_first: bool,
49) -> f32 {
50    if is_first {
51        match alignment_mode {
52            AlignContent::Start => 0.0,
53            AlignContent::FlexStart => {
54                if layout_is_flex_reversed {
55                    free_space
56                } else {
57                    0.0
58                }
59            }
60            AlignContent::End => free_space,
61            AlignContent::FlexEnd => {
62                if layout_is_flex_reversed {
63                    0.0
64                } else {
65                    free_space
66                }
67            }
68            AlignContent::Center => free_space / 2.0,
69            AlignContent::Stretch => 0.0,
70            AlignContent::SpaceBetween => 0.0,
71            AlignContent::SpaceAround => {
72                if free_space >= 0.0 {
73                    (free_space / num_items as f32) / 2.0
74                } else {
75                    free_space / 2.0
76                }
77            }
78            AlignContent::SpaceEvenly => {
79                if free_space >= 0.0 {
80                    free_space / (num_items + 1) as f32
81                } else {
82                    free_space / 2.0
83                }
84            }
85        }
86    } else {
87        let free_space = free_space.max(0.0);
88        gap + match alignment_mode {
89            AlignContent::Start => 0.0,
90            AlignContent::FlexStart => 0.0,
91            AlignContent::End => 0.0,
92            AlignContent::FlexEnd => 0.0,
93            AlignContent::Center => 0.0,
94            AlignContent::Stretch => 0.0,
95            AlignContent::SpaceBetween => free_space / (num_items - 1) as f32,
96            AlignContent::SpaceAround => free_space / num_items as f32,
97            AlignContent::SpaceEvenly => free_space / (num_items + 1) as f32,
98        }
99    }
100}