layout 2020: Implement justify-content in flexbox

Align the items along the main-axis per justify-content.
This commit is contained in:
Pu Xingyu 2023-05-05 23:14:38 +08:00
parent f29834608a
commit debcd30b21
2 changed files with 61 additions and 19 deletions

View file

@ -22,9 +22,11 @@ use style::properties::longhands::align_self::computed_value::T as AlignSelf;
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing; use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
use style::properties::longhands::flex_direction::computed_value::T as FlexDirection; use style::properties::longhands::flex_direction::computed_value::T as FlexDirection;
use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap; use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap;
use style::properties::longhands::justify_content::computed_value::T as JustifyContent;
use style::values::computed::length::Size; use style::values::computed::length::Size;
use style::values::computed::Length; use style::values::computed::Length;
use style::values::generics::flex::GenericFlexBasis as FlexBasis; use style::values::generics::flex::GenericFlexBasis as FlexBasis;
use style::values::CSSFloat;
use style::Zero; use style::Zero;
// FIMXE: “Flex items […] `z-index` values other than `auto` create a stacking context // FIMXE: “Flex items […] `z-index` values other than `auto` create a stacking context
@ -45,6 +47,7 @@ struct FlexContext<'a> {
main_start_cross_start_sides_are: MainStartCrossStart, main_start_cross_start_sides_are: MainStartCrossStart,
container_definite_inner_size: FlexRelativeVec2<Option<Length>>, container_definite_inner_size: FlexRelativeVec2<Option<Length>>,
align_items: AlignItems, align_items: AlignItems,
justify_content: JustifyContent,
} }
/// A flex item with some intermediate results /// A flex item with some intermediate results
@ -259,6 +262,7 @@ fn layout<'context, 'boxes>(
FlexWrap::WrapReverse => true, FlexWrap::WrapReverse => true,
}; };
let align_items = containing_block.style.clone_align_items(); let align_items = containing_block.style.clone_align_items();
let justify_content = containing_block.style.clone_justify_content();
let mut flex_context = FlexContext { let mut flex_context = FlexContext {
layout_context, layout_context,
@ -269,6 +273,7 @@ fn layout<'context, 'boxes>(
container_is_single_line, container_is_single_line,
flex_axis, flex_axis,
align_items, align_items,
justify_content,
main_start_cross_start_sides_are: MainStartCrossStart::from( main_start_cross_start_sides_are: MainStartCrossStart::from(
flex_direction, flex_direction,
flex_wrap_reverse, flex_wrap_reverse,
@ -669,10 +674,37 @@ impl FlexLine<'_> {
// Distribute any remaining free space // Distribute any remaining free space
// https://drafts.csswg.org/css-flexbox/#algo-main-align // https://drafts.csswg.org/css-flexbox/#algo-main-align
let item_main_margins = self.resolve_auto_main_margins(remaining_free_space); let (item_main_margins, free_space_distributed) =
self.resolve_auto_main_margins(remaining_free_space);
// FIXME: “Align the items along the main-axis per justify-content.” // Align the items along the main-axis per justify-content.
// For now we hard-code `justify-content` to `flex-start`. let item_count = self.items.len();
let main_start_position = if free_space_distributed {
Length::zero()
} else {
match flex_context.justify_content {
JustifyContent::FlexEnd => remaining_free_space,
JustifyContent::Center => remaining_free_space / 2.0,
JustifyContent::SpaceAround => remaining_free_space / (item_count * 2) as CSSFloat,
_ => Length::zero(),
}
};
let item_main_interval = if free_space_distributed {
Length::zero()
} else {
match flex_context.justify_content {
JustifyContent::SpaceBetween => {
if item_count > 1 {
remaining_free_space / (item_count - 1) as CSSFloat
} else {
Length::zero()
}
},
JustifyContent::SpaceAround => remaining_free_space / item_count as CSSFloat,
_ => Length::zero(),
}
};
// https://drafts.csswg.org/css-flexbox/#algo-cross-margins // https://drafts.csswg.org/css-flexbox/#algo-cross-margins
let item_cross_margins = self.items.iter().zip(&item_used_cross_sizes).map( let item_cross_margins = self.items.iter().zip(&item_used_cross_sizes).map(
@ -697,8 +729,12 @@ impl FlexLine<'_> {
) )
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// https://drafts.csswg.org/css-flexbox/#algo-main-align // https://drafts.csswg.org/css-flexbox/#algo-main-align
let items_content_main_start_positions = let items_content_main_start_positions = self.align_along_main_axis(
self.align_along_main_axis(&item_used_main_sizes, &item_margins); &item_used_main_sizes,
&item_margins,
main_start_position,
item_main_interval,
);
// https://drafts.csswg.org/css-flexbox/#algo-cross-align // https://drafts.csswg.org/css-flexbox/#algo-cross-align
let item_content_cross_start_posititons = self let item_content_cross_start_posititons = self
@ -1029,11 +1065,12 @@ impl<'items> FlexLine<'items> {
} }
// Return the main-start and main-end margin of each item in the line, // Return the main-start and main-end margin of each item in the line,
// with `auto` values resolved. // with `auto` values resolved,
// and return whether free space has been distributed.
fn resolve_auto_main_margins( fn resolve_auto_main_margins(
&self, &self,
remaining_free_space: Length, remaining_free_space: Length,
) -> impl Iterator<Item = (Length, Length)> + '_ { ) -> (impl Iterator<Item = (Length, Length)> + '_, bool) {
let each_auto_margin = if remaining_free_space > Length::zero() { let each_auto_margin = if remaining_free_space > Length::zero() {
let auto_margins_count = self let auto_margins_count = self
.items .items
@ -1050,12 +1087,15 @@ impl<'items> FlexLine<'items> {
} else { } else {
Length::zero() Length::zero()
}; };
self.items.iter().map(move |item| { (
( self.items.iter().map(move |item| {
item.margin.main_start.auto_is(|| each_auto_margin), (
item.margin.main_end.auto_is(|| each_auto_margin), item.margin.main_start.auto_is(|| each_auto_margin),
) item.margin.main_end.auto_is(|| each_auto_margin),
}) )
}),
each_auto_margin > Length::zero(),
)
} }
/// Return the coordinate of the main-start side of the content area of each item /// Return the coordinate of the main-start side of the content area of each item
@ -1063,11 +1103,11 @@ impl<'items> FlexLine<'items> {
&'a self, &'a self,
item_used_main_sizes: &'a [Length], item_used_main_sizes: &'a [Length],
item_margins: &'a [FlexRelativeSides<Length>], item_margins: &'a [FlexRelativeSides<Length>],
main_start_position: Length,
item_main_interval: Length,
) -> impl Iterator<Item = Length> + 'a { ) -> impl Iterator<Item = Length> + 'a {
// “Align the items along the main-axis” // “Align the items along the main-axis”
// FIXME: “per justify-content.” let mut main_position_cursor = main_start_position;
// For now we hard-code the behavior for `justify-content: flex-start`.
let mut main_position_cursor = Length::zero();
self.items self.items
.iter() .iter()
.zip(item_used_main_sizes) .zip(item_used_main_sizes)
@ -1079,7 +1119,8 @@ impl<'items> FlexLine<'items> {
main_position_cursor += main_content_size + main_position_cursor += main_content_size +
item.padding.main_end + item.padding.main_end +
item.border.main_end + item.border.main_end +
margin.main_end; margin.main_end +
item_main_interval;
content_main_start_position content_main_start_position
}) })
} }

View file

@ -92,12 +92,13 @@ ${helpers.single_keyword(
gecko_enum_prefix = "StyleFlexWrap", gecko_enum_prefix = "StyleFlexWrap",
)} )}
% if engine == "servo-2013": % if engine in ["servo-2013", "servo-2020"]:
// FIXME: Update Servo to support the same Syntax as Gecko. // FIXME: Update Servo to support the same Syntax as Gecko.
${helpers.single_keyword( ${helpers.single_keyword(
"justify-content", "justify-content",
"flex-start stretch flex-end center space-between space-around", "flex-start stretch flex-end center space-between space-around",
engines="servo-2013", engines="servo-2013 servo-2020",
servo_2020_pref="layout.flexbox.enabled",
extra_prefixes="webkit", extra_prefixes="webkit",
spec="https://drafts.csswg.org/css-align/#propdef-justify-content", spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
animation_value_type="discrete", animation_value_type="discrete",