diff options
author | Andreas Kling <kling@serenityos.org> | 2020-07-01 19:02:28 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-07-01 19:10:58 +0200 |
commit | 392b05580690b271d21dde89a9f9b2b05988f234 (patch) | |
tree | 2834e39d37bf47e46b299b5277d6f64a81fb2bae | |
parent | f7a900367f802796d94742c5bbb272aa07b4450e (diff) | |
download | serenity-392b05580690b271d21dde89a9f9b2b05988f234.zip |
LibWeb: Use the StackingContext tree for hit testing
This makes it possible to click links that are above other content due
to stacking context order (e.g via CSS z-index.)
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutBlock.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutBox.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutBox.h | 1 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutDocument.cpp | 5 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutDocument.h | 3 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutNode.cpp | 4 | ||||
-rw-r--r-- | Libraries/LibWeb/Painting/StackingContext.cpp | 19 | ||||
-rw-r--r-- | Libraries/LibWeb/Painting/StackingContext.h | 1 |
8 files changed, 36 insertions, 1 deletions
diff --git a/Libraries/LibWeb/Layout/LayoutBlock.cpp b/Libraries/LibWeb/Layout/LayoutBlock.cpp index a043f3c216..d475fc37a9 100644 --- a/Libraries/LibWeb/Layout/LayoutBlock.cpp +++ b/Libraries/LibWeb/Layout/LayoutBlock.cpp @@ -725,6 +725,8 @@ HitTestResult LayoutBlock::hit_test(const Gfx::IntPoint& position) const HitTestResult last_good_candidate; for (auto& line_box : m_line_boxes) { for (auto& fragment : line_box.fragments()) { + if (is<LayoutBox>(fragment.layout_node()) && to<LayoutBox>(fragment.layout_node()).stacking_context()) + continue; if (enclosing_int_rect(fragment.absolute_rect()).contains(position)) { if (fragment.layout_node().is_block()) return to<LayoutBlock>(fragment.layout_node()).hit_test(position); diff --git a/Libraries/LibWeb/Layout/LayoutBox.cpp b/Libraries/LibWeb/Layout/LayoutBox.cpp index cfddbf21bd..4e84d22324 100644 --- a/Libraries/LibWeb/Layout/LayoutBox.cpp +++ b/Libraries/LibWeb/Layout/LayoutBox.cpp @@ -231,6 +231,8 @@ HitTestResult LayoutBox::hit_test(const Gfx::IntPoint& position) const // m_rect.contains() since inline text rects can't be trusted.. HitTestResult result { absolute_rect().contains(position.x(), position.y()) ? this : nullptr }; for_each_child([&](auto& child) { + if (is<LayoutBox>(child) && to<LayoutBox>(child).stacking_context()) + return; auto child_result = child.hit_test(position); if (child_result.layout_node) result = child_result; diff --git a/Libraries/LibWeb/Layout/LayoutBox.h b/Libraries/LibWeb/Layout/LayoutBox.h index c719994a8d..1387a9ad0d 100644 --- a/Libraries/LibWeb/Layout/LayoutBox.h +++ b/Libraries/LibWeb/Layout/LayoutBox.h @@ -64,6 +64,7 @@ public: bool establishes_stacking_context() const; StackingContext* stacking_context() { return m_stacking_context; } + const StackingContext* stacking_context() const { return m_stacking_context; } void set_stacking_context(NonnullOwnPtr<StackingContext> context) { m_stacking_context = move(context); } StackingContext* enclosing_stacking_context(); diff --git a/Libraries/LibWeb/Layout/LayoutDocument.cpp b/Libraries/LibWeb/Layout/LayoutDocument.cpp index 3f0fd27603..b30b6486ef 100644 --- a/Libraries/LibWeb/Layout/LayoutDocument.cpp +++ b/Libraries/LibWeb/Layout/LayoutDocument.cpp @@ -113,4 +113,9 @@ void LayoutDocument::paint(PaintContext& context, PaintPhase phase) stacking_context()->paint(context, phase); } +HitTestResult LayoutDocument::hit_test(const Gfx::IntPoint& position) const +{ + return stacking_context()->hit_test(position); +} + } diff --git a/Libraries/LibWeb/Layout/LayoutDocument.h b/Libraries/LibWeb/Layout/LayoutDocument.h index 6a3c04bfe5..eb37187e65 100644 --- a/Libraries/LibWeb/Layout/LayoutDocument.h +++ b/Libraries/LibWeb/Layout/LayoutDocument.h @@ -41,9 +41,10 @@ public: virtual void layout(LayoutMode = LayoutMode::Default) override; void paint_all_phases(PaintContext&); - virtual void paint(PaintContext&, PaintPhase) override; + virtual HitTestResult hit_test(const Gfx::IntPoint&) const override; + const LayoutRange& selection() const { return m_selection; } LayoutRange& selection() { return m_selection; } diff --git a/Libraries/LibWeb/Layout/LayoutNode.cpp b/Libraries/LibWeb/Layout/LayoutNode.cpp index a410c930a5..0e6647de1e 100644 --- a/Libraries/LibWeb/Layout/LayoutNode.cpp +++ b/Libraries/LibWeb/Layout/LayoutNode.cpp @@ -106,6 +106,10 @@ HitTestResult LayoutNode::hit_test(const Gfx::IntPoint& position) const { HitTestResult result; for_each_child([&](auto& child) { + // Skip over children that establish their own stacking context. + // The outer loop who called us will take care of those. + if (is<LayoutBox>(child) && to<LayoutBox>(child).stacking_context()) + return; auto child_result = child.hit_test(position); if (child_result.layout_node) result = child_result; diff --git a/Libraries/LibWeb/Painting/StackingContext.cpp b/Libraries/LibWeb/Painting/StackingContext.cpp index 01e98c3ce3..8cae2c81ed 100644 --- a/Libraries/LibWeb/Painting/StackingContext.cpp +++ b/Libraries/LibWeb/Painting/StackingContext.cpp @@ -61,6 +61,25 @@ void StackingContext::paint(PaintContext& context, LayoutNode::PaintPhase phase) } } +HitTestResult StackingContext::hit_test(const Gfx::IntPoint& position) const +{ + HitTestResult result; + if (!m_box.is_root()) { + result = m_box.hit_test(position); + } else { + // NOTE: LayoutDocument::hit_test() merely calls StackingContext::hit_test() + // so we call its base class instead. + result = to<LayoutDocument>(m_box).LayoutBlock::hit_test(position); + } + + for (auto* child : m_children) { + auto result_here = child->hit_test(position); + if (result_here.layout_node) + result = result_here; + } + return result; +} + void StackingContext::dump(int indent) const { for (int i = 0; i < indent; ++i) diff --git a/Libraries/LibWeb/Painting/StackingContext.h b/Libraries/LibWeb/Painting/StackingContext.h index 72751a198a..f817a5ff67 100644 --- a/Libraries/LibWeb/Painting/StackingContext.h +++ b/Libraries/LibWeb/Painting/StackingContext.h @@ -41,6 +41,7 @@ public: const StackingContext* parent() const { return m_parent; } void paint(PaintContext&, LayoutNode::PaintPhase); + HitTestResult hit_test(const Gfx::IntPoint&) const; void dump(int indent = 0) const; |