Jelajahi Sumber

LibWeb: CSS: Add "position: absolute" with top and left

This momentarily handles the CSS property "position: absolute;" in
combination with the properties "top" and "left", so that elements can
be placed anywhere on the page independently from their parents.

Statically positioned elements ignore absolute positioned elements when
calculating their position as they don't take up space.
myphs 5 tahun lalu
induk
melakukan
f42f300ba3

+ 67 - 0
Base/home/anon/www/position-absolute-top-left.html

@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>absolute</title>
+    <style>
+        div {
+            width: 100px;
+            height: 100px;
+        }
+        .absolute {
+            position: absolute;
+        }
+        .blue {
+            width: 200px;
+            height: 200px;
+            background-color: blue;
+            top: 208px;
+            left: 208px;
+        }
+        .yellow {
+            background-color: yellow;
+            top: 50px;
+            left: 50px;
+        }
+        .red {
+            background-color: red;
+            top: 100px;
+            left: 100px;
+        }
+        .green {
+            background-color: green;
+            top: 300px;
+            left: 300px;
+        }
+        .black {
+            background-color: black;
+            width: 50px;
+            height: 50px;
+            top: 50px;
+            left: 50px;   
+        }
+        .blue_margin {
+            width: 200px;
+            height: 200px;
+            background-color: blue;
+            margin-top: 200px;
+            margin-left: 400px;
+        }
+    </style>
+</head>
+<body>
+    <div class="blue absolute">
+        <div class="red absolute"></div>
+        <div class="yellow absolute">
+            <div class="black absolute"></div>
+        </div>
+        <div class="green absolute"></div>
+    </div>
+    <div class="blue">
+        <div class="red"></div>
+        <div class="yellow"></div>
+        <div class="green"></div>
+    </div>
+    <div class="blue_margin"></div>
+</body>
+</html>

+ 1 - 0
Base/home/anon/www/welcome.html

@@ -23,6 +23,7 @@ h1 {
     <p>This is a very simple browser built on the LibWeb engine.</p>
     <p>This is a very simple browser built on the LibWeb engine.</p>
     <p>Some small test pages:</p>
     <p>Some small test pages:</p>
     <ul>
     <ul>
+        <li><a href="position-absolute-top-left.html">position: absolute; for top and left</a></li>
         <li><a href="demo.html">fun demo</a></li>
         <li><a href="demo.html">fun demo</a></li>
         <li><a href="raf.html">requestAnimationFrame test</a></li>
         <li><a href="raf.html">requestAnimationFrame test</a></li>
         <li><a href="canvas.html">canvas 2D test</a></li>
         <li><a href="canvas.html">canvas 2D test</a></li>

+ 16 - 0
Libraries/LibWeb/CSS/StyleProperties.cpp

@@ -162,6 +162,22 @@ float StyleProperties::line_height() const
     return (float)font().glyph_height() * 1.4f;
     return (float)font().glyph_height() * 1.4f;
 }
 }
 
 
+CSS::Position StyleProperties::position() const
+{
+    if (property(CSS::PropertyID::Position).has_value()) {
+        String position_string = string_or_fallback(CSS::PropertyID::Position, "static");
+        if (position_string == "relative")
+            return CSS::Position::Relative;
+        if (position_string == "absolute")
+            return CSS::Position::Absolute;
+        if (position_string == "sticky")
+            return CSS::Position::Sticky;
+        if (position_string == "fixed")
+            return CSS::Position::Fixed;
+    }
+    return CSS::Position::Static;
+}
+
 bool StyleProperties::operator==(const StyleProperties& other) const
 bool StyleProperties::operator==(const StyleProperties& other) const
 {
 {
     if (m_property_values.size() != other.m_property_values.size())
     if (m_property_values.size() != other.m_property_values.size())

+ 2 - 0
Libraries/LibWeb/CSS/StyleProperties.h

@@ -70,6 +70,8 @@ public:
     bool operator==(const StyleProperties&) const;
     bool operator==(const StyleProperties&) const;
     bool operator!=(const StyleProperties& other) const { return !(*this == other); }
     bool operator!=(const StyleProperties& other) const { return !(*this == other); }
 
 
+    CSS::Position position() const;
+
 private:
 private:
     HashMap<unsigned, NonnullRefPtr<StyleValue>> m_property_values;
     HashMap<unsigned, NonnullRefPtr<StyleValue>> m_property_values;
 
 

+ 10 - 0
Libraries/LibWeb/CSS/StyleValue.h

@@ -50,6 +50,14 @@ enum class ValueID {
     Right,
     Right,
     Justify,
     Justify,
 };
 };
+
+enum class Position {
+    Static,
+    Relative,
+    Absolute,
+    Fixed,
+    Sticky,
+};
 }
 }
 
 
 class StyleValue : public RefCounted<StyleValue> {
 class StyleValue : public RefCounted<StyleValue> {
@@ -65,6 +73,7 @@ public:
         Color,
         Color,
         Identifier,
         Identifier,
         Image,
         Image,
+        Position,
     };
     };
 
 
     Type type() const { return m_type; }
     Type type() const { return m_type; }
@@ -76,6 +85,7 @@ public:
     bool is_image() const { return type() == Type::Image; }
     bool is_image() const { return type() == Type::Image; }
     bool is_string() const { return type() == Type::String; }
     bool is_string() const { return type() == Type::String; }
     bool is_length() const { return type() == Type::Length; }
     bool is_length() const { return type() == Type::Length; }
+    bool is_position() const { return type() == Type::Position; }
 
 
     virtual String to_string() const = 0;
     virtual String to_string() const = 0;
     virtual Length to_length() const { return {}; }
     virtual Length to_length() const { return {}; }

+ 3 - 0
Libraries/LibWeb/Layout/BoxModelMetrics.h

@@ -39,10 +39,12 @@ public:
     LengthBox& margin() { return m_margin; }
     LengthBox& margin() { return m_margin; }
     LengthBox& padding() { return m_padding; }
     LengthBox& padding() { return m_padding; }
     LengthBox& border() { return m_border; }
     LengthBox& border() { return m_border; }
+    LengthBox& offset() { return m_offset; }
 
 
     const LengthBox& margin() const { return m_margin; }
     const LengthBox& margin() const { return m_margin; }
     const LengthBox& padding() const { return m_padding; }
     const LengthBox& padding() const { return m_padding; }
     const LengthBox& border() const { return m_border; }
     const LengthBox& border() const { return m_border; }
+    const LengthBox& offset() const { return m_offset; }
 
 
     struct PixelBox {
     struct PixelBox {
         float top;
         float top;
@@ -57,6 +59,7 @@ private:
     LengthBox m_margin;
     LengthBox m_margin;
     LengthBox m_padding;
     LengthBox m_padding;
     LengthBox m_border;
     LengthBox m_border;
+    LengthBox m_offset;
 };
 };
 
 
 }
 }

+ 39 - 11
Libraries/LibWeb/Layout/LayoutBlock.cpp

@@ -296,24 +296,52 @@ void LayoutBlock::compute_position()
 
 
     auto width = style.length_or_fallback(CSS::PropertyID::Width, auto_value);
     auto width = style.length_or_fallback(CSS::PropertyID::Width, auto_value);
 
 
+    if (style.position() == CSS::Position::Absolute) {
+        box_model().offset().top = style.length_or_fallback(CSS::PropertyID::Top, zero_value);
+        box_model().offset().right = style.length_or_fallback(CSS::PropertyID::Right, zero_value);
+        box_model().offset().bottom = style.length_or_fallback(CSS::PropertyID::Bottom, zero_value);
+        box_model().offset().left = style.length_or_fallback(CSS::PropertyID::Left, zero_value);
+    }
+
     box_model().margin().top = style.length_or_fallback(CSS::PropertyID::MarginTop, zero_value);
     box_model().margin().top = style.length_or_fallback(CSS::PropertyID::MarginTop, zero_value);
     box_model().margin().bottom = style.length_or_fallback(CSS::PropertyID::MarginBottom, zero_value);
     box_model().margin().bottom = style.length_or_fallback(CSS::PropertyID::MarginBottom, zero_value);
     box_model().border().top = style.length_or_fallback(CSS::PropertyID::BorderTopWidth, zero_value);
     box_model().border().top = style.length_or_fallback(CSS::PropertyID::BorderTopWidth, zero_value);
     box_model().border().bottom = style.length_or_fallback(CSS::PropertyID::BorderBottomWidth, zero_value);
     box_model().border().bottom = style.length_or_fallback(CSS::PropertyID::BorderBottomWidth, zero_value);
     box_model().padding().top = style.length_or_fallback(CSS::PropertyID::PaddingTop, zero_value);
     box_model().padding().top = style.length_or_fallback(CSS::PropertyID::PaddingTop, zero_value);
     box_model().padding().bottom = style.length_or_fallback(CSS::PropertyID::PaddingBottom, zero_value);
     box_model().padding().bottom = style.length_or_fallback(CSS::PropertyID::PaddingBottom, zero_value);
-    rect().set_x(containing_block()->x() + box_model().margin().left.to_px() + box_model().border().left.to_px() + box_model().padding().left.to_px());
-
-    float top_border = -1;
-    if (previous_sibling() != nullptr) {
-        auto& previous_sibling_rect = previous_sibling()->rect();
-        auto& previous_sibling_style = previous_sibling()->box_model();
-        top_border = previous_sibling_rect.y() + previous_sibling_rect.height();
-        top_border += previous_sibling_style.full_margin().bottom;
-    } else {
-        top_border = containing_block()->y();
+
+    float position_x = box_model().margin().left.to_px()
+        + box_model().border().left.to_px()
+        + box_model().padding().left.to_px()
+        + box_model().offset().left.to_px();
+
+    if (style.position() != CSS::Position::Absolute || containing_block()->style().position() == CSS::Position::Absolute)
+        position_x += containing_block()->x();
+
+    rect().set_x(position_x);
+
+    float position_y = box_model().full_margin().top
+        + box_model().offset().top.to_px();
+
+    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();
+        }
+
+        if (relevant_sibling == nullptr) {
+            position_y += containing_block()->y();
+        } else {
+            auto& previous_sibling_rect = relevant_sibling->rect();
+            auto& previous_sibling_style = relevant_sibling->box_model();
+            position_y += previous_sibling_rect.y() + previous_sibling_rect.height();
+            position_y += previous_sibling_style.full_margin().bottom;
+        }
     }
     }
-    rect().set_y(top_border + box_model().full_margin().top);
+
+    rect().set_y(position_y);
 }
 }
 
 
 void LayoutBlock::compute_height()
 void LayoutBlock::compute_height()