/src/serenity/Userland/Libraries/LibWeb/Layout/LayoutState.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2022, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/HashMap.h> |
10 | | #include <LibGfx/Point.h> |
11 | | #include <LibWeb/Layout/Box.h> |
12 | | #include <LibWeb/Layout/LineBox.h> |
13 | | #include <LibWeb/Painting/PaintableBox.h> |
14 | | #include <LibWeb/Painting/SVGGraphicsPaintable.h> |
15 | | |
16 | | namespace Web::Layout { |
17 | | |
18 | | enum class SizeConstraint { |
19 | | None, |
20 | | MinContent, |
21 | | MaxContent, |
22 | | }; |
23 | | |
24 | | class AvailableSize; |
25 | | class AvailableSpace; |
26 | | |
27 | | struct LayoutState { |
28 | | LayoutState() |
29 | 0 | : m_root(*this) |
30 | 0 | { |
31 | 0 | } |
32 | | |
33 | | explicit LayoutState(LayoutState const* parent); |
34 | | ~LayoutState(); |
35 | | |
36 | | LayoutState const& find_root() const |
37 | 0 | { |
38 | 0 | LayoutState const* root = this; |
39 | 0 | for (auto* state = m_parent; state; state = state->m_parent) |
40 | 0 | root = state; |
41 | 0 | return *root; |
42 | 0 | } |
43 | | |
44 | | struct UsedValues { |
45 | 0 | NodeWithStyle const& node() const { return *m_node; } |
46 | | void set_node(NodeWithStyle&, UsedValues const* containing_block_used_values); |
47 | | |
48 | 0 | UsedValues const* containing_block_used_values() const { return m_containing_block_used_values; } |
49 | | |
50 | 0 | CSSPixels content_width() const { return m_content_width; } |
51 | 0 | CSSPixels content_height() const { return m_content_height; } |
52 | | void set_content_width(CSSPixels); |
53 | | void set_content_height(CSSPixels); |
54 | | |
55 | | void set_indefinite_content_width(); |
56 | | void set_indefinite_content_height(); |
57 | | |
58 | 0 | void set_has_definite_width(bool has_definite_width) { m_has_definite_width = has_definite_width; } |
59 | 0 | void set_has_definite_height(bool has_definite_height) { m_has_definite_height = has_definite_height; } |
60 | | |
61 | | // NOTE: These are used by FlexFormattingContext to assign a temporary main size to items |
62 | | // early on, so that descendants have something to resolve percentages against. |
63 | | void set_temporary_content_width(CSSPixels); |
64 | | void set_temporary_content_height(CSSPixels); |
65 | | |
66 | 0 | bool has_definite_width() const { return m_has_definite_width && width_constraint == SizeConstraint::None; } |
67 | 0 | bool has_definite_height() const { return m_has_definite_height && height_constraint == SizeConstraint::None; } |
68 | | |
69 | | // Returns the available space for content inside this layout box. |
70 | | // If the space in an axis is indefinite, and the outer space is an intrinsic sizing constraint, |
71 | | // the constraint is used in that axis instead. |
72 | | AvailableSpace available_inner_space_or_constraints_from(AvailableSpace const& outer_space) const; |
73 | | |
74 | | void set_content_offset(CSSPixelPoint); |
75 | | void set_content_x(CSSPixels); |
76 | | void set_content_y(CSSPixels); |
77 | | |
78 | | CSSPixelPoint offset; |
79 | | |
80 | | SizeConstraint width_constraint { SizeConstraint::None }; |
81 | | SizeConstraint height_constraint { SizeConstraint::None }; |
82 | | |
83 | | CSSPixels margin_left { 0 }; |
84 | | CSSPixels margin_right { 0 }; |
85 | | CSSPixels margin_top { 0 }; |
86 | | CSSPixels margin_bottom { 0 }; |
87 | | |
88 | | CSSPixels border_left { 0 }; |
89 | | CSSPixels border_right { 0 }; |
90 | | CSSPixels border_top { 0 }; |
91 | | CSSPixels border_bottom { 0 }; |
92 | | |
93 | | CSSPixels padding_left { 0 }; |
94 | | CSSPixels padding_right { 0 }; |
95 | | CSSPixels padding_top { 0 }; |
96 | | CSSPixels padding_bottom { 0 }; |
97 | | |
98 | | CSSPixels inset_left { 0 }; |
99 | | CSSPixels inset_right { 0 }; |
100 | | CSSPixels inset_top { 0 }; |
101 | | CSSPixels inset_bottom { 0 }; |
102 | | |
103 | | // Used for calculating the static position of an abspos block-level box. |
104 | | CSSPixels vertical_offset_of_parent_block_container { 0 }; |
105 | | |
106 | | Vector<LineBox> line_boxes; |
107 | | |
108 | 0 | CSSPixels margin_box_left() const { return margin_left + border_left_collapsed() + padding_left; } |
109 | 0 | CSSPixels margin_box_right() const { return margin_right + border_right_collapsed() + padding_right; } |
110 | 0 | CSSPixels margin_box_top() const { return margin_top + border_top_collapsed() + padding_top; } |
111 | 0 | CSSPixels margin_box_bottom() const { return margin_bottom + border_bottom_collapsed() + padding_bottom; } |
112 | | |
113 | 0 | CSSPixels margin_box_width() const { return margin_box_left() + content_width() + margin_box_right(); } |
114 | 0 | CSSPixels margin_box_height() const { return margin_box_top() + content_height() + margin_box_bottom(); } |
115 | | |
116 | 0 | CSSPixels border_box_left() const { return border_left_collapsed() + padding_left; } |
117 | 0 | CSSPixels border_box_right() const { return border_right_collapsed() + padding_right; } |
118 | 0 | CSSPixels border_box_top() const { return border_top_collapsed() + padding_top; } |
119 | 0 | CSSPixels border_box_bottom() const { return border_bottom_collapsed() + padding_bottom; } |
120 | | |
121 | 0 | CSSPixels border_box_width() const { return border_box_left() + content_width() + border_box_right(); } |
122 | 0 | CSSPixels border_box_height() const { return border_box_top() + content_height() + border_box_bottom(); } |
123 | | |
124 | | Optional<LineBoxFragmentCoordinate> containing_line_box_fragment; |
125 | | |
126 | 0 | void add_floating_descendant(Box const& box) { m_floating_descendants.set(&box); } |
127 | 0 | auto const& floating_descendants() const { return m_floating_descendants; } |
128 | | |
129 | 0 | void set_override_borders_data(Painting::PaintableBox::BordersDataWithElementKind const& override_borders_data) { m_override_borders_data = override_borders_data; } |
130 | 0 | auto const& override_borders_data() const { return m_override_borders_data; } |
131 | | |
132 | 0 | void set_table_cell_coordinates(Painting::PaintableBox::TableCellCoordinates const& table_cell_coordinates) { m_table_cell_coordinates = table_cell_coordinates; } |
133 | 0 | auto const& table_cell_coordinates() const { return m_table_cell_coordinates; } |
134 | | |
135 | 0 | void set_computed_svg_path(Gfx::Path const& svg_path) { m_computed_svg_path = svg_path; } |
136 | 0 | auto& computed_svg_path() { return m_computed_svg_path; } |
137 | | |
138 | 0 | void set_computed_svg_transforms(Painting::SVGGraphicsPaintable::ComputedTransforms const& computed_transforms) { m_computed_svg_transforms = computed_transforms; } |
139 | 0 | auto const& computed_svg_transforms() const { return m_computed_svg_transforms; } |
140 | | |
141 | | private: |
142 | | AvailableSize available_width_inside() const; |
143 | | AvailableSize available_height_inside() const; |
144 | | |
145 | 0 | bool use_collapsing_borders_model() const { return m_override_borders_data.has_value(); } |
146 | | // Implement the collapsing border model https://www.w3.org/TR/CSS22/tables.html#collapsing-borders. |
147 | 0 | CSSPixels border_left_collapsed() const { return use_collapsing_borders_model() ? round(border_left / 2) : border_left; } |
148 | 0 | CSSPixels border_right_collapsed() const { return use_collapsing_borders_model() ? round(border_right / 2) : border_right; } |
149 | 0 | CSSPixels border_top_collapsed() const { return use_collapsing_borders_model() ? round(border_top / 2) : border_top; } |
150 | 0 | CSSPixels border_bottom_collapsed() const { return use_collapsing_borders_model() ? round(border_bottom / 2) : border_bottom; } |
151 | | |
152 | | JS::GCPtr<Layout::NodeWithStyle> m_node { nullptr }; |
153 | | UsedValues const* m_containing_block_used_values { nullptr }; |
154 | | |
155 | | CSSPixels m_content_width { 0 }; |
156 | | CSSPixels m_content_height { 0 }; |
157 | | |
158 | | bool m_has_definite_width { false }; |
159 | | bool m_has_definite_height { false }; |
160 | | |
161 | | HashTable<JS::GCPtr<Box const>> m_floating_descendants; |
162 | | |
163 | | Optional<Painting::PaintableBox::BordersDataWithElementKind> m_override_borders_data; |
164 | | Optional<Painting::PaintableBox::TableCellCoordinates> m_table_cell_coordinates; |
165 | | |
166 | | Optional<Gfx::Path> m_computed_svg_path; |
167 | | Optional<Painting::SVGGraphicsPaintable::ComputedTransforms> m_computed_svg_transforms; |
168 | | }; |
169 | | |
170 | | // Commits the used values produced by layout and builds a paintable tree. |
171 | | void commit(Box& root); |
172 | | |
173 | | // NOTE: get_mutable() will CoW the UsedValues if it's inherited from an ancestor state; |
174 | | UsedValues& get_mutable(NodeWithStyle const&); |
175 | | |
176 | | // NOTE: get() will not CoW the UsedValues. |
177 | | UsedValues const& get(NodeWithStyle const&) const; |
178 | | |
179 | | HashMap<JS::NonnullGCPtr<Layout::Node const>, NonnullOwnPtr<UsedValues>> used_values_per_layout_node; |
180 | | |
181 | | // We cache intrinsic sizes once determined, as they will not change over the course of a full layout. |
182 | | // This avoids computing them several times while performing flex layout. |
183 | | struct IntrinsicSizes { |
184 | | Optional<CSSPixels> min_content_width; |
185 | | Optional<CSSPixels> max_content_width; |
186 | | |
187 | | HashMap<CSSPixels, Optional<CSSPixels>> min_content_height; |
188 | | HashMap<CSSPixels, Optional<CSSPixels>> max_content_height; |
189 | | }; |
190 | | |
191 | | HashMap<JS::GCPtr<NodeWithStyle const>, NonnullOwnPtr<IntrinsicSizes>> mutable intrinsic_sizes; |
192 | | |
193 | | LayoutState const* m_parent { nullptr }; |
194 | | LayoutState const& m_root; |
195 | | |
196 | | private: |
197 | | void resolve_relative_positions(); |
198 | | }; |
199 | | |
200 | | } |