1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 06:27:45 +00:00

LibWeb: Split out FFC's "generate anonymous flex items" to a function

Let's begin splitting the FlexFormattingContext layout algorithm into
separate functions to make it more manageable.
This commit is contained in:
Andreas Kling 2021-10-13 19:57:26 +02:00
parent ad50e328e0
commit ca02d112a5
2 changed files with 82 additions and 62 deletions

View file

@ -19,6 +19,7 @@ namespace Web::Layout {
FlexFormattingContext::FlexFormattingContext(Box& context_box, FormattingContext* parent) FlexFormattingContext::FlexFormattingContext(Box& context_box, FormattingContext* parent)
: FormattingContext(context_box, parent) : FormattingContext(context_box, parent)
, m_flex_direction(context_box.computed_values().flex_direction())
{ {
} }
@ -64,9 +65,9 @@ void FlexFormattingContext::run(Box& box, LayoutMode)
// FIXME: Implement reverse and ordering. // FIXME: Implement reverse and ordering.
bool is_row = is_row_layout();
// Determine main/cross direction // Determine main/cross direction
auto flex_direction = box.computed_values().flex_direction();
auto is_row = (flex_direction == CSS::FlexDirection::Row || flex_direction == CSS::FlexDirection::RowReverse);
auto main_size_is_infinite = false; auto main_size_is_infinite = false;
auto get_pixel_size = [](Box& box, const CSS::Length& length) { auto get_pixel_size = [](Box& box, const CSS::Length& length) {
return length.resolved(CSS::Length::make_px(0), box, box.containing_block()->width()).to_px(box); return length.resolved(CSS::Length::make_px(0), box, box.containing_block()->width()).to_px(box);
@ -285,68 +286,11 @@ void FlexFormattingContext::run(Box& box, LayoutMode)
else else
box.box_model().margin.bottom = margin; box.box_model().margin.bottom = margin;
}; };
auto populate_specified_margins = [&is_row](FlexItem& item) {
auto width_of_containing_block = item.box.width_of_logical_containing_block(); Vector<FlexItem> flex_items;
// FIXME: This should also take reverse-ness into account
if (is_row) {
item.margins.main_before = item.box.computed_values().margin().left.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.main_after = item.box.computed_values().margin().right.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.cross_before = item.box.computed_values().margin().top.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.cross_after = item.box.computed_values().margin().bottom.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
} else {
item.margins.main_before = item.box.computed_values().margin().top.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.main_after = item.box.computed_values().margin().bottom.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.cross_before = item.box.computed_values().margin().left.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.cross_after = item.box.computed_values().margin().right.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
}
};
// 1. Generate anonymous flex items // 1. Generate anonymous flex items
// More like, sift through the already generated items. generate_anonymous_flex_items(box, flex_items);
// After this step no items are to be added or removed from flex_items!
// It holds every item we need to consider and there should be nothing in the following
// calculations that could change that.
// This is particularly important since we take references to the items stored in flex_items
// later, whose addresses won't be stable if we added or removed any items.
Vector<FlexItem> flex_items;
if (!box.has_definite_width()) {
box.set_width(box.containing_block()->width());
} else {
box.set_width(box.computed_values().width().resolved_or_zero(box, box.containing_block()->width()).to_px(box));
}
if (!box.has_definite_height()) {
box.set_height(box.containing_block()->height());
} else {
box.set_height(box.computed_values().height().resolved_or_zero(box, box.containing_block()->height()).to_px(box));
}
box.for_each_child_of_type<Box>([&](Box& child_box) {
layout_inside(child_box, LayoutMode::Default);
// Skip anonymous text runs that are only whitespace.
if (child_box.is_anonymous() && !child_box.first_child_of_type<BlockContainer>()) {
bool contains_only_white_space = true;
child_box.for_each_in_inclusive_subtree_of_type<TextNode>([&contains_only_white_space](auto& text_node) {
if (!text_node.text_for_rendering().is_whitespace()) {
contains_only_white_space = false;
return IterationDecision::Break;
}
return IterationDecision::Continue;
});
if (contains_only_white_space)
return IterationDecision::Continue;
}
// Skip any "out-of-flow" children
if (child_box.is_out_of_flow(*this))
return IterationDecision::Continue;
child_box.set_flex_item(true);
FlexItem flex_item = { child_box };
populate_specified_margins(flex_item);
flex_items.append(move(flex_item));
return IterationDecision::Continue;
});
// 2. Determine the available main and cross space for the flex items // 2. Determine the available main and cross space for the flex items
float main_available_size = 0; float main_available_size = 0;
@ -848,4 +792,71 @@ void FlexFormattingContext::run(Box& box, LayoutMode)
} }
} }
} }
static void populate_specified_margins(FlexItem& item, CSS::FlexDirection flex_direction)
{
auto width_of_containing_block = item.box.width_of_logical_containing_block();
// FIXME: This should also take reverse-ness into account
if (flex_direction == CSS::FlexDirection::Row || flex_direction == CSS::FlexDirection::RowReverse) {
item.margins.main_before = item.box.computed_values().margin().left.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.main_after = item.box.computed_values().margin().right.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.cross_before = item.box.computed_values().margin().top.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.cross_after = item.box.computed_values().margin().bottom.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
} else {
item.margins.main_before = item.box.computed_values().margin().top.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.main_after = item.box.computed_values().margin().bottom.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.cross_before = item.box.computed_values().margin().left.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
item.margins.cross_after = item.box.computed_values().margin().right.resolved_or_zero(item.box, width_of_containing_block).to_px(item.box);
}
};
// https://www.w3.org/TR/css-flexbox-1/#flex-items
void FlexFormattingContext::generate_anonymous_flex_items(Box& flex_container, Vector<FlexItem>& flex_items)
{
// More like, sift through the already generated items.
// After this step no items are to be added or removed from flex_items!
// It holds every item we need to consider and there should be nothing in the following
// calculations that could change that.
// This is particularly important since we take references to the items stored in flex_items
// later, whose addresses won't be stable if we added or removed any items.
if (!flex_container.has_definite_width()) {
flex_container.set_width(flex_container.containing_block()->width());
} else {
flex_container.set_width(flex_container.computed_values().width().resolved_or_zero(flex_container, flex_container.containing_block()->width()).to_px(flex_container));
}
if (!flex_container.has_definite_height()) {
flex_container.set_height(flex_container.containing_block()->height());
} else {
flex_container.set_height(flex_container.computed_values().height().resolved_or_zero(flex_container, flex_container.containing_block()->height()).to_px(flex_container));
}
flex_container.for_each_child_of_type<Box>([&](Box& child_box) {
layout_inside(child_box, LayoutMode::Default);
// Skip anonymous text runs that are only whitespace.
if (child_box.is_anonymous() && !child_box.first_child_of_type<BlockContainer>()) {
bool contains_only_white_space = true;
child_box.for_each_in_inclusive_subtree_of_type<TextNode>([&contains_only_white_space](auto& text_node) {
if (!text_node.text_for_rendering().is_whitespace()) {
contains_only_white_space = false;
return IterationDecision::Break;
}
return IterationDecision::Continue;
});
if (contains_only_white_space)
return IterationDecision::Continue;
}
// Skip any "out-of-flow" children
if (child_box.is_out_of_flow(*this))
return IterationDecision::Continue;
child_box.set_flex_item(true);
FlexItem flex_item = { child_box };
populate_specified_margins(flex_item, m_flex_direction);
flex_items.append(move(flex_item));
return IterationDecision::Continue;
});
}
} }

View file

@ -10,6 +10,8 @@
namespace Web::Layout { namespace Web::Layout {
struct FlexItem;
class FlexFormattingContext final : public FormattingContext { class FlexFormattingContext final : public FormattingContext {
public: public:
FlexFormattingContext(Box& containing_block, FormattingContext* parent); FlexFormattingContext(Box& containing_block, FormattingContext* parent);
@ -18,6 +20,13 @@ public:
virtual bool inhibits_floating() const override { return true; } virtual bool inhibits_floating() const override { return true; }
virtual void run(Box&, LayoutMode) override; virtual void run(Box&, LayoutMode) override;
private:
void generate_anonymous_flex_items(Box& flex_container, Vector<FlexItem>&);
bool is_row_layout() const { return m_flex_direction == CSS::FlexDirection::Row || m_flex_direction == CSS::FlexDirection::RowReverse; }
CSS::FlexDirection m_flex_direction {};
}; };
} }