summaryrefslogtreecommitdiff
path: root/Libraries
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-06-12 13:27:28 +0200
committerAndreas Kling <kling@serenityos.org>2020-06-12 13:43:46 +0200
commit260427f0ad90b91e2865b1c69dd680ad2e262f57 (patch)
tree136e826a6ef1c2f0ee4c8562317faacd58038433 /Libraries
parentff2c949d70033e6bc2deb9cb6c1c8baca4fbbe79 (diff)
downloadserenity-260427f0ad90b91e2865b1c69dd680ad2e262f57.zip
LibWeb: Some improvements to absolute positioning
Absolutely positioned blocks now register themselves with their containing block (and note that the containing block of an absolutely positioned box is the nearest non-statically positioned block ancestor or the ICB as fallback.) Containing blocks then drive the layout of their tracked absolutely positioned descendants as a separate layout pass. This is very far from perfect but the general direction seems good.
Diffstat (limited to 'Libraries')
-rw-r--r--Libraries/LibWeb/Dump.cpp5
-rw-r--r--Libraries/LibWeb/Layout/BoxModelMetrics.cpp20
-rw-r--r--Libraries/LibWeb/Layout/BoxModelMetrics.h2
-rw-r--r--Libraries/LibWeb/Layout/LayoutBlock.cpp103
-rw-r--r--Libraries/LibWeb/Layout/LayoutBlock.h5
-rw-r--r--Libraries/LibWeb/Layout/LayoutDocument.cpp2
6 files changed, 115 insertions, 22 deletions
diff --git a/Libraries/LibWeb/Dump.cpp b/Libraries/LibWeb/Dump.cpp
index eba734220e..46f2c74302 100644
--- a/Libraries/LibWeb/Dump.cpp
+++ b/Libraries/LibWeb/Dump.cpp
@@ -145,6 +145,11 @@ void dump_tree(const LayoutNode& layout_node)
if (layout_node.is_block() && static_cast<const LayoutBlock&>(layout_node).children_are_inline()) {
auto& block = static_cast<const LayoutBlock&>(layout_node);
+ if (block.absolutely_positioned_descendant_count()) {
+ for (size_t i = 0; i < indent; ++i)
+ dbgprintf(" ");
+ dbgprintf(" %zu absolutely positioned descendant(s) tracked here\n", block.absolutely_positioned_descendant_count());
+ }
for (size_t i = 0; i < indent; ++i)
dbgprintf(" ");
dbgprintf(" Line boxes (%d):\n", block.line_boxes().size());
diff --git a/Libraries/LibWeb/Layout/BoxModelMetrics.cpp b/Libraries/LibWeb/Layout/BoxModelMetrics.cpp
index c937918eaf..5843783ae7 100644
--- a/Libraries/LibWeb/Layout/BoxModelMetrics.cpp
+++ b/Libraries/LibWeb/Layout/BoxModelMetrics.cpp
@@ -46,4 +46,24 @@ BoxModelMetrics::PixelBox BoxModelMetrics::full_margin(const LayoutNode& layout_
};
}
+BoxModelMetrics::PixelBox BoxModelMetrics::padding_box(const LayoutNode& layout_node) const
+{
+ return {
+ m_padding.top.to_px(layout_node),
+ m_padding.right.to_px(layout_node),
+ m_padding.bottom.to_px(layout_node),
+ m_padding.left.to_px(layout_node),
+ };
+}
+
+BoxModelMetrics::PixelBox BoxModelMetrics::border_box(const LayoutNode& layout_node) const
+{
+ return {
+ m_border.top.to_px(layout_node) + m_padding.top.to_px(layout_node),
+ m_border.right.to_px(layout_node) + m_padding.right.to_px(layout_node),
+ m_border.bottom.to_px(layout_node) + m_padding.bottom.to_px(layout_node),
+ m_border.left.to_px(layout_node) + m_padding.left.to_px(layout_node),
+ };
+}
+
}
diff --git a/Libraries/LibWeb/Layout/BoxModelMetrics.h b/Libraries/LibWeb/Layout/BoxModelMetrics.h
index 3a699c0f4a..40bc014cf6 100644
--- a/Libraries/LibWeb/Layout/BoxModelMetrics.h
+++ b/Libraries/LibWeb/Layout/BoxModelMetrics.h
@@ -54,6 +54,8 @@ public:
};
PixelBox full_margin(const LayoutNode&) const;
+ PixelBox padding_box(const LayoutNode&) const;
+ PixelBox border_box(const LayoutNode&) const;
private:
LengthBox m_margin;
diff --git a/Libraries/LibWeb/Layout/LayoutBlock.cpp b/Libraries/LibWeb/Layout/LayoutBlock.cpp
index ff43ca236e..bc7d7f3191 100644
--- a/Libraries/LibWeb/Layout/LayoutBlock.cpp
+++ b/Libraries/LibWeb/Layout/LayoutBlock.cpp
@@ -27,6 +27,7 @@
#include <LibGUI/Painter.h>
#include <LibWeb/CSS/StyleResolver.h>
#include <LibWeb/DOM/Element.h>
+#include <LibWeb/Dump.h>
#include <LibWeb/Layout/LayoutBlock.h>
#include <LibWeb/Layout/LayoutInline.h>
#include <LibWeb/Layout/LayoutReplaced.h>
@@ -64,6 +65,69 @@ void LayoutBlock::layout(LayoutMode layout_mode)
layout_children(layout_mode);
compute_height();
+
+ if (layout_mode == LayoutMode::Default)
+ layout_absolute_descendants();
+}
+
+void LayoutBlock::layout_absolute_descendants()
+{
+ for (auto& box : m_absolutely_positioned_descendants) {
+ box->layout(LayoutMode::Default);
+ auto& box_model = box->box_model();
+ auto& style = box->style();
+ auto zero_value = Length(0, Length::Type::Px);
+
+ auto specified_width = style.length_or_fallback(CSS::PropertyID::Width, Length(), width());
+
+ box_model.margin().top = style.length_or_fallback(CSS::PropertyID::MarginTop, {}, height());
+ box_model.margin().right = style.length_or_fallback(CSS::PropertyID::MarginRight, {}, width());
+ box_model.margin().bottom = style.length_or_fallback(CSS::PropertyID::MarginBottom, {}, height());
+ box_model.margin().left = style.length_or_fallback(CSS::PropertyID::MarginLeft, {}, width());
+
+ box_model.offset().top = style.length_or_fallback(CSS::PropertyID::Top, {}, height());
+ box_model.offset().right = style.length_or_fallback(CSS::PropertyID::Right, {}, width());
+ box_model.offset().bottom = style.length_or_fallback(CSS::PropertyID::Bottom, {}, height());
+ box_model.offset().left = style.length_or_fallback(CSS::PropertyID::Left, {}, width());
+
+ if (box_model.offset().left.is_auto() && specified_width.is_auto() && box_model.offset().right.is_auto()) {
+ if (box_model.margin().left.is_auto())
+ box_model.margin().left = zero_value;
+ if (box_model.margin().right.is_auto())
+ box_model.margin().right = zero_value;
+ }
+
+ Gfx::FloatPoint used_offset;
+
+ float x_offset = box_model.offset().left.to_px(*box)
+ + box_model.border_box(*box).left
+ - box_model.offset().right.to_px(*box)
+ - box_model.border_box(*box).right;
+
+ float y_offset = box_model.offset().top.to_px(*box)
+ + box_model.border_box(*box).top
+ - box_model.offset().bottom.to_px(*box)
+ - box_model.border_box(*box).bottom;
+
+ if (!box_model.offset().left.is_auto()) {
+ used_offset.set_x(x_offset + box_model.margin().left.to_px(*box));
+ } else if (!box_model.offset().right.is_auto()) {
+ used_offset.set_x(width() + x_offset - box->width() - box_model.margin().right.to_px(*box));
+ }
+
+ if (!box_model.offset().top.is_auto()) {
+ used_offset.set_y(y_offset + box_model.margin().top.to_px(*box));
+ } else if (!box_model.offset().bottom.is_auto()) {
+ used_offset.set_y(height() + y_offset - box->height() - box_model.margin().bottom.to_px(*box));
+ }
+
+ box->set_offset(used_offset);
+ }
+}
+
+void LayoutBlock::add_absolutely_positioned_descendant(LayoutBox& box)
+{
+ m_absolutely_positioned_descendants.set(box);
}
void LayoutBlock::layout_children(LayoutMode layout_mode)
@@ -377,19 +441,17 @@ void LayoutBlock::compute_width()
void LayoutBlock::compute_position()
{
- auto& style = this->style();
+ // Absolutely positioned blocks are positioned by position_absolute_boxes()
+ if (is_absolutely_positioned()) {
+ dbg() << "Is abspos, adding to containing block " << containing_block()->node()->tag_name();
+ const_cast<LayoutBlock*>(containing_block())->add_absolutely_positioned_descendant(*this);
+ return;
+ }
+ auto& style = this->style();
auto zero_value = Length(0, Length::Type::Px);
-
auto& containing_block = *this->containing_block();
- if (style.position() == CSS::Position::Absolute) {
- box_model().offset().top = style.length_or_fallback(CSS::PropertyID::Top, zero_value, containing_block.height());
- box_model().offset().right = style.length_or_fallback(CSS::PropertyID::Right, zero_value, containing_block.width());
- box_model().offset().bottom = style.length_or_fallback(CSS::PropertyID::Bottom, zero_value, containing_block.height());
- box_model().offset().left = style.length_or_fallback(CSS::PropertyID::Left, zero_value, containing_block.width());
- }
-
box_model().margin().top = style.length_or_fallback(CSS::PropertyID::MarginTop, zero_value, containing_block.width());
box_model().margin().bottom = style.length_or_fallback(CSS::PropertyID::MarginBottom, zero_value, containing_block.width());
box_model().border().top = style.length_or_fallback(CSS::PropertyID::BorderTopWidth, zero_value);
@@ -405,19 +467,17 @@ void LayoutBlock::compute_position()
float position_y = box_model().full_margin(*this).top
+ box_model().offset().top.to_px(*this);
- if (style.position() != CSS::Position::Absolute || containing_block.style().position() == CSS::Position::Absolute) {
- LayoutBlock* relevant_sibling = previous_sibling();
- while (relevant_sibling != nullptr) {
- if (relevant_sibling->style().position() != CSS::Position::Absolute)
- break;
- relevant_sibling = relevant_sibling->previous_sibling();
- }
+ LayoutBlock* relevant_sibling = previous_sibling();
+ while (relevant_sibling != nullptr) {
+ if (relevant_sibling->style().position() != CSS::Position::Absolute)
+ break;
+ relevant_sibling = relevant_sibling->previous_sibling();
+ }
- if (relevant_sibling) {
- auto& previous_sibling_style = relevant_sibling->box_model();
- position_y += relevant_sibling->effective_offset().y() + relevant_sibling->height();
- position_y += previous_sibling_style.full_margin(*this).bottom;
- }
+ if (relevant_sibling) {
+ auto& previous_sibling_style = relevant_sibling->box_model();
+ position_y += relevant_sibling->effective_offset().y() + relevant_sibling->height();
+ position_y += previous_sibling_style.full_margin(*this).bottom;
}
set_offset({ position_x, position_y });
@@ -430,7 +490,6 @@ void LayoutBlock::compute_height()
auto specified_height = style.length_or_fallback(CSS::PropertyID::Height, Length(), containing_block()->height());
auto specified_max_height = style.length_or_fallback(CSS::PropertyID::MaxHeight, Length(), containing_block()->height());
-
if (!specified_height.is_auto()) {
float used_height = specified_height.to_px(*this);
if (!specified_max_height.is_auto())
diff --git a/Libraries/LibWeb/Layout/LayoutBlock.h b/Libraries/LibWeb/Layout/LayoutBlock.h
index af1ab548bf..a55dff6fdb 100644
--- a/Libraries/LibWeb/Layout/LayoutBlock.h
+++ b/Libraries/LibWeb/Layout/LayoutBlock.h
@@ -65,10 +65,14 @@ public:
virtual void split_into_lines(LayoutBlock& container, LayoutMode) override;
+ void add_absolutely_positioned_descendant(LayoutBox&);
+ size_t absolutely_positioned_descendant_count() const { return m_absolutely_positioned_descendants.size(); }
+
protected:
void compute_width();
void compute_position();
void compute_height();
+ void layout_absolute_descendants();
private:
virtual bool is_block() const override { return true; }
@@ -80,6 +84,7 @@ private:
void layout_block_children(LayoutMode);
Vector<LineBox> m_line_boxes;
+ HashTable<RefPtr<LayoutBox>> m_absolutely_positioned_descendants;
};
template<typename Callback>
diff --git a/Libraries/LibWeb/Layout/LayoutDocument.cpp b/Libraries/LibWeb/Layout/LayoutDocument.cpp
index ea81093724..6bc016940f 100644
--- a/Libraries/LibWeb/Layout/LayoutDocument.cpp
+++ b/Libraries/LibWeb/Layout/LayoutDocument.cpp
@@ -58,6 +58,8 @@ void LayoutDocument::layout(LayoutMode layout_mode)
});
set_height(lowest_bottom);
+ layout_absolute_descendants();
+
// FIXME: This is a total hack. Make sure any GUI::Widgets are moved into place after layout.
// We should stop embedding GUI::Widgets entirely, since that won't work out-of-process.
for_each_in_subtree_of_type<LayoutWidget>([&](auto& widget) {