LibWeb: Add basic support for position:fixed

Fixed position elements have the ICB as their containing block.
The magic of fixed positioning is implemented at the rendering stage,
where we temporarily translate painting by the current scroll offset.

Note that "absolutely positioned" includes both position:absolute
and position:fixed.
This commit is contained in:
Andreas Kling 2020-06-12 14:19:03 +02:00
parent bd33bfd120
commit 137f6d44ec
Notes: sideshowbarker 2024-07-19 05:42:43 +09:00
8 changed files with 27 additions and 11 deletions

View file

@ -29,6 +29,7 @@
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Dump.h>
#include <LibWeb/Layout/LayoutBlock.h>
#include <LibWeb/Layout/LayoutDocument.h>
#include <LibWeb/Layout/LayoutInline.h>
#include <LibWeb/Layout/LayoutReplaced.h>
#include <LibWeb/Layout/LayoutText.h>
@ -67,10 +68,10 @@ void LayoutBlock::layout(LayoutMode layout_mode)
compute_height();
if (layout_mode == LayoutMode::Default)
layout_absolute_descendants();
layout_absolutely_positioned_descendants();
}
void LayoutBlock::layout_absolute_descendants()
void LayoutBlock::layout_absolutely_positioned_descendants()
{
for (auto& box : m_absolutely_positioned_descendants) {
box->layout(LayoutMode::Default);
@ -441,9 +442,7 @@ void LayoutBlock::compute_width()
void LayoutBlock::compute_position()
{
// 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;
}

View file

@ -72,7 +72,7 @@ protected:
void compute_width();
void compute_position();
void compute_height();
void layout_absolute_descendants();
void layout_absolutely_positioned_descendants();
private:
virtual bool is_block() const override { return true; }

View file

@ -194,6 +194,10 @@ void LayoutBox::render(RenderingContext& context)
if (!is_visible())
return;
Gfx::PainterStateSaver saver(context.painter());
if (is_fixed_position())
context.painter().translate(context.scroll_offset());
#ifdef DRAW_BOXES_AROUND_LAYOUT_NODES
context.painter().draw_rect(m_rect, Color::Blue);
#endif

View file

@ -58,7 +58,7 @@ void LayoutDocument::layout(LayoutMode layout_mode)
});
set_height(lowest_bottom);
layout_absolute_descendants();
layout_absolutely_positioned_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.

View file

@ -72,7 +72,9 @@ const LayoutBlock* LayoutNode::containing_block() const
if (is_text())
return nearest_block_ancestor();
if (is_absolutely_positioned()) {
auto position = style().position();
if (position == CSS::Position::Absolute) {
auto* ancestor = parent();
while (ancestor && !ancestor->can_contain_boxes_with_position_absolute())
ancestor = ancestor->parent();
@ -81,7 +83,7 @@ const LayoutBlock* LayoutNode::containing_block() const
return to<LayoutBlock>(ancestor);
}
if (style().position() == CSS::Position::Fixed)
if (position == CSS::Position::Fixed)
return &root();
return nearest_block_ancestor();
@ -189,7 +191,13 @@ Gfx::FloatPoint LayoutNode::box_type_agnostic_position() const
bool LayoutNode::is_absolutely_positioned() const
{
return style().position() == CSS::Position::Absolute;
auto position = style().position();
return position == CSS::Position::Absolute || position == CSS::Position::Fixed;
}
bool LayoutNode::is_fixed_position() const
{
return style().position() == CSS::Position::Fixed;
}
}

View file

@ -171,6 +171,7 @@ public:
virtual void render(RenderingContext&);
bool is_absolutely_positioned() const;
bool is_fixed_position() const;
const LayoutBlock* containing_block() const;

View file

@ -207,7 +207,7 @@ void PageView::paint_event(GUI::PaintEvent& event)
painter.translate(frame_thickness(), frame_thickness());
painter.translate(-horizontal_scrollbar().value(), -vertical_scrollbar().value());
RenderingContext context(painter, palette());
RenderingContext context(painter, palette(), { horizontal_scrollbar().value(), vertical_scrollbar().value() });
context.set_should_show_line_box_borders(m_should_show_line_box_borders);
context.set_viewport_rect(viewport_rect_in_content_coordinates());
layout_root()->render(context);

View file

@ -34,9 +34,10 @@ namespace Web {
class RenderingContext {
public:
explicit RenderingContext(GUI::Painter& painter, const Palette& palette)
explicit RenderingContext(GUI::Painter& painter, const Palette& palette, const Gfx::IntPoint& scroll_offset)
: m_painter(painter)
, m_palette(palette)
, m_scroll_offset(scroll_offset)
{
}
@ -49,10 +50,13 @@ public:
Gfx::IntRect viewport_rect() const { return m_viewport_rect; }
void set_viewport_rect(const Gfx::IntRect& rect) { m_viewport_rect = rect; }
const Gfx::IntPoint& scroll_offset() const { return m_scroll_offset; }
private:
GUI::Painter& m_painter;
Palette m_palette;
Gfx::IntRect m_viewport_rect;
Gfx::IntPoint m_scroll_offset;
bool m_should_show_line_box_borders { false };
};