/src/serenity/Userland/Libraries/LibWeb/Layout/FlexFormattingContext.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2021-2024, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <LibWeb/Layout/Box.h> |
10 | | #include <LibWeb/Layout/FormattingContext.h> |
11 | | |
12 | | namespace Web::Layout { |
13 | | |
14 | | class FlexFormattingContext final : public FormattingContext { |
15 | | public: |
16 | | FlexFormattingContext(LayoutState&, LayoutMode, Box const& flex_container, FormattingContext* parent); |
17 | | ~FlexFormattingContext(); |
18 | | |
19 | 0 | virtual bool inhibits_floating() const override { return true; } |
20 | | |
21 | | virtual void run(AvailableSpace const&) override; |
22 | | virtual CSSPixels automatic_content_width() const override; |
23 | | virtual CSSPixels automatic_content_height() const override; |
24 | | |
25 | 0 | Box const& flex_container() const { return context_box(); } |
26 | | |
27 | | virtual CSSPixelPoint calculate_static_position(Box const&) const override; |
28 | | |
29 | | private: |
30 | | [[nodiscard]] bool should_treat_main_size_as_auto(Box const&) const; |
31 | | [[nodiscard]] bool should_treat_cross_size_as_auto(Box const&) const; |
32 | | |
33 | | [[nodiscard]] bool should_treat_main_max_size_as_none(Box const&) const; |
34 | | [[nodiscard]] bool should_treat_cross_max_size_as_none(Box const&) const; |
35 | | |
36 | | [[nodiscard]] CSSPixels adjust_main_size_through_aspect_ratio_for_cross_size_min_max_constraints(Box const&, CSSPixels main_size, CSS::Size const& min_cross_size, CSS::Size const& max_cross_size) const; |
37 | | [[nodiscard]] CSSPixels adjust_cross_size_through_aspect_ratio_for_main_size_min_max_constraints(Box const&, CSSPixels cross_size, CSS::Size const& min_main_size, CSS::Size const& max_main_size) const; |
38 | | |
39 | | [[nodiscard]] CSSPixels calculate_main_size_from_cross_size_and_aspect_ratio(CSSPixels cross_size, CSSPixelFraction aspect_ratio) const; |
40 | | [[nodiscard]] CSSPixels calculate_cross_size_from_main_size_and_aspect_ratio(CSSPixels main_size, CSSPixelFraction aspect_ratio) const; |
41 | | |
42 | | void dump_items() const; |
43 | | |
44 | | struct DirectionAgnosticMargins { |
45 | | CSSPixels main_before { 0 }; |
46 | | CSSPixels main_after { 0 }; |
47 | | CSSPixels cross_before { 0 }; |
48 | | CSSPixels cross_after { 0 }; |
49 | | |
50 | | bool main_before_is_auto { false }; |
51 | | bool main_after_is_auto { false }; |
52 | | bool cross_before_is_auto { false }; |
53 | | bool cross_after_is_auto { false }; |
54 | | }; |
55 | | |
56 | | struct FlexItem { |
57 | | JS::NonnullGCPtr<Box> box; |
58 | | LayoutState::UsedValues& used_values; |
59 | | Optional<CSS::FlexBasis> used_flex_basis {}; |
60 | | bool used_flex_basis_is_definite { false }; |
61 | | CSSPixels flex_base_size { 0 }; |
62 | | CSSPixels hypothetical_main_size { 0 }; |
63 | | CSSPixels hypothetical_cross_size { 0 }; |
64 | 0 | CSSPixels hypothetical_cross_size_with_margins() { return hypothetical_cross_size + margins.cross_before + margins.cross_after + borders.cross_after + borders.cross_before + padding.cross_after + padding.cross_before; } |
65 | | CSSPixels target_main_size { 0 }; |
66 | | bool frozen { false }; |
67 | | Optional<double> flex_factor {}; |
68 | | double scaled_flex_shrink_factor { 0 }; |
69 | | double desired_flex_fraction { 0 }; |
70 | | |
71 | | CSSPixels outer_hypothetical_main_size() const |
72 | 0 | { |
73 | 0 | return hypothetical_main_size + margins.main_before + margins.main_after + borders.main_before + borders.main_after + padding.main_before + padding.main_after; |
74 | 0 | } |
75 | | |
76 | | CSSPixels outer_target_main_size() const |
77 | 0 | { |
78 | 0 | return target_main_size + margins.main_before + margins.main_after + borders.main_before + borders.main_after + padding.main_before + padding.main_after; |
79 | 0 | } |
80 | | |
81 | | CSSPixels outer_flex_base_size() const |
82 | 0 | { |
83 | 0 | return flex_base_size + margins.main_before + margins.main_after + borders.main_before + borders.main_after + padding.main_before + padding.main_after; |
84 | 0 | } |
85 | | |
86 | | // The used main size of this flex item. Empty until determined. |
87 | | Optional<CSSPixels> main_size {}; |
88 | | |
89 | | // The used cross size of this flex item. Empty until determined. |
90 | | Optional<CSSPixels> cross_size {}; |
91 | | |
92 | | CSSPixels main_offset { 0 }; |
93 | | CSSPixels cross_offset { 0 }; |
94 | | DirectionAgnosticMargins margins {}; |
95 | | DirectionAgnosticMargins borders {}; |
96 | | DirectionAgnosticMargins padding {}; |
97 | | bool is_min_violation { false }; |
98 | | bool is_max_violation { false }; |
99 | | |
100 | | CSSPixels add_main_margin_box_sizes(CSSPixels content_size) const |
101 | 0 | { |
102 | 0 | return content_size + margins.main_before + margins.main_after + borders.main_before + borders.main_after + padding.main_before + padding.main_after; |
103 | 0 | } |
104 | | |
105 | | CSSPixels add_cross_margin_box_sizes(CSSPixels content_size) const |
106 | 0 | { |
107 | 0 | return content_size + margins.cross_before + margins.cross_after + borders.cross_before + borders.cross_after + padding.cross_before + padding.cross_after; |
108 | 0 | } |
109 | | }; |
110 | | |
111 | | struct FlexLine { |
112 | | Vector<FlexItem&> items; |
113 | | CSSPixels cross_size { 0 }; |
114 | | Optional<CSSPixels> remaining_free_space; |
115 | | double chosen_flex_fraction { 0 }; |
116 | | |
117 | | double sum_of_flex_factor_of_unfrozen_items() const; |
118 | | double sum_of_scaled_flex_shrink_factor_of_unfrozen_items() const; |
119 | | }; |
120 | | |
121 | | CSSPixels main_gap() const; |
122 | | CSSPixels cross_gap() const; |
123 | | [[nodiscard]] bool has_definite_main_size(LayoutState::UsedValues const&) const; |
124 | | [[nodiscard]] bool has_definite_cross_size(LayoutState::UsedValues const&) const; |
125 | 0 | [[nodiscard]] bool has_definite_main_size(FlexItem const& item) const { return has_definite_main_size(item.used_values); } |
126 | 0 | [[nodiscard]] bool has_definite_cross_size(FlexItem const& item) const { return has_definite_cross_size(item.used_values); } |
127 | | |
128 | | [[nodiscard]] CSSPixels inner_main_size(LayoutState::UsedValues const&) const; |
129 | | [[nodiscard]] CSSPixels inner_cross_size(LayoutState::UsedValues const&) const; |
130 | 0 | [[nodiscard]] CSSPixels inner_main_size(FlexItem const& item) const { return inner_main_size(item.used_values); } |
131 | 0 | [[nodiscard]] CSSPixels inner_cross_size(FlexItem const& item) const { return inner_cross_size(item.used_values); } |
132 | | bool has_main_min_size(Box const&) const; |
133 | | bool has_cross_min_size(Box const&) const; |
134 | | CSSPixels specified_main_max_size(Box const&) const; |
135 | | CSSPixels specified_cross_max_size(Box const&) const; |
136 | | bool is_cross_auto(Box const&) const; |
137 | | CSSPixels specified_main_min_size(Box const&) const; |
138 | | CSSPixels specified_cross_min_size(Box const&) const; |
139 | | bool has_main_max_size(Box const&) const; |
140 | | bool has_cross_max_size(Box const&) const; |
141 | | CSSPixels automatic_minimum_size(FlexItem const&) const; |
142 | | CSSPixels content_based_minimum_size(FlexItem const&) const; |
143 | | Optional<CSSPixels> specified_size_suggestion(FlexItem const&) const; |
144 | | Optional<CSSPixels> transferred_size_suggestion(FlexItem const&) const; |
145 | | CSSPixels content_size_suggestion(FlexItem const&) const; |
146 | | CSS::Size const& computed_main_size(Box const&) const; |
147 | | CSS::Size const& computed_main_min_size(Box const&) const; |
148 | | CSS::Size const& computed_main_max_size(Box const&) const; |
149 | | CSS::Size const& computed_cross_size(Box const&) const; |
150 | | CSS::Size const& computed_cross_min_size(Box const&) const; |
151 | | CSS::Size const& computed_cross_max_size(Box const&) const; |
152 | | |
153 | | CSSPixels get_pixel_width(Box const&, CSS::Size const&) const; |
154 | | CSSPixels get_pixel_height(Box const&, CSS::Size const&) const; |
155 | | |
156 | | bool flex_item_is_stretched(FlexItem const&) const; |
157 | | |
158 | | void set_main_size(Box const&, CSSPixels size); |
159 | | void set_cross_size(Box const&, CSSPixels size); |
160 | | void set_offset(Box const&, CSSPixels main_offset, CSSPixels cross_offset); |
161 | | void set_main_axis_first_margin(FlexItem&, CSSPixels margin); |
162 | | void set_main_axis_second_margin(FlexItem&, CSSPixels margin); |
163 | | |
164 | | void set_has_definite_main_size(FlexItem&); |
165 | | void set_has_definite_cross_size(FlexItem&); |
166 | | |
167 | | void copy_dimensions_from_flex_items_to_boxes(); |
168 | | |
169 | | void generate_anonymous_flex_items(); |
170 | | |
171 | | void determine_available_space_for_items(AvailableSpace const&); |
172 | | |
173 | | void determine_flex_base_size_and_hypothetical_main_size(FlexItem&); |
174 | | |
175 | | void collect_flex_items_into_flex_lines(); |
176 | | |
177 | | void resolve_flexible_lengths(); |
178 | | void resolve_flexible_lengths_for_line(FlexLine&); |
179 | | |
180 | | void resolve_cross_axis_auto_margins(); |
181 | | |
182 | | void determine_hypothetical_cross_size_of_item(FlexItem&, bool resolve_percentage_min_max_sizes); |
183 | | |
184 | | void calculate_cross_size_of_each_flex_line(); |
185 | | |
186 | | void handle_align_content_stretch(); |
187 | | |
188 | | CSS::AlignItems alignment_for_item(Box const&) const; |
189 | | |
190 | | void determine_used_cross_size_of_each_flex_item(); |
191 | | |
192 | | void distribute_any_remaining_free_space(); |
193 | | |
194 | | void align_all_flex_items_along_the_cross_axis(); |
195 | | |
196 | | void align_all_flex_lines(); |
197 | | |
198 | 0 | bool is_row_layout() const { return m_flex_direction == CSS::FlexDirection::Row || m_flex_direction == CSS::FlexDirection::RowReverse; } |
199 | 0 | bool is_single_line() const { return flex_container().computed_values().flex_wrap() == CSS::FlexWrap::Nowrap; } |
200 | | bool is_direction_reverse() const; |
201 | | void populate_specified_margins(FlexItem&, CSS::FlexDirection) const; |
202 | | |
203 | | void determine_intrinsic_size_of_flex_container(); |
204 | | [[nodiscard]] CSSPixels calculate_intrinsic_main_size_of_flex_container(); |
205 | | [[nodiscard]] CSSPixels calculate_intrinsic_cross_size_of_flex_container(); |
206 | | |
207 | | [[nodiscard]] CSSPixels calculate_cross_min_content_contribution(FlexItem const&, bool resolve_percentage_min_max_sizes) const; |
208 | | [[nodiscard]] CSSPixels calculate_cross_max_content_contribution(FlexItem const&, bool resolve_percentage_min_max_sizes) const; |
209 | | [[nodiscard]] CSSPixels calculate_main_min_content_contribution(FlexItem const&) const; |
210 | | [[nodiscard]] CSSPixels calculate_main_max_content_contribution(FlexItem const&) const; |
211 | | |
212 | | [[nodiscard]] CSSPixels calculate_min_content_main_size(FlexItem const&) const; |
213 | | [[nodiscard]] CSSPixels calculate_max_content_main_size(FlexItem const&) const; |
214 | | [[nodiscard]] CSSPixels calculate_min_content_cross_size(FlexItem const&) const; |
215 | | [[nodiscard]] CSSPixels calculate_max_content_cross_size(FlexItem const&) const; |
216 | | |
217 | | [[nodiscard]] CSSPixels calculate_fit_content_main_size(FlexItem const&) const; |
218 | | [[nodiscard]] CSSPixels calculate_fit_content_cross_size(FlexItem const&) const; |
219 | | |
220 | | [[nodiscard]] CSSPixels calculate_width_to_use_when_determining_intrinsic_height_of_item(FlexItem const&) const; |
221 | | |
222 | | virtual void parent_context_did_dimension_child_root_box() override; |
223 | | |
224 | | CSS::FlexBasis used_flex_basis_for_item(FlexItem const&) const; |
225 | | |
226 | | LayoutState::UsedValues& m_flex_container_state; |
227 | | |
228 | | Vector<FlexLine> m_flex_lines; |
229 | | Vector<FlexItem> m_flex_items; |
230 | | CSS::FlexDirection m_flex_direction {}; |
231 | | |
232 | | struct AxisAgnosticAvailableSpace { |
233 | | AvailableSize main; |
234 | | AvailableSize cross; |
235 | | AvailableSpace space; |
236 | | }; |
237 | | Optional<AxisAgnosticAvailableSpace> m_available_space_for_items; |
238 | | }; |
239 | | |
240 | | } |