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.)
This commit is contained in:
parent
f7a900367f
commit
392b055806
Notes:
sideshowbarker
2024-07-19 05:14:22 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/392b0558069
8 changed files with 36 additions and 1 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue