LibWeb: Change StackingContext::hit_test() to accept callback
This change modifies hit_test() to no longer return the first paintable encountered at a specified position. Instead, this function accepts a callback that is invoked for each paintable located at a position, in hit-testing order. This modification will allow us to reuse this call for `Document.elementsFromPoint()` in upcoming changes.
This commit is contained in:
parent
15d151ee66
commit
9c99182b1e
Notes:
sideshowbarker
2024-07-16 20:12:13 +09:00
Author: https://github.com/kalenikaliaksandr Commit: https://github.com/SerenityOS/serenity/commit/9c99182b1e Pull-request: https://github.com/SerenityOS/serenity/pull/23189 Reviewed-by: https://github.com/ADKaster ✅
11 changed files with 120 additions and 104 deletions
|
@ -5,8 +5,8 @@ Coordinates outside the viewport return empty array: true
|
|||
<HTML >
|
||||
== FIXME: Elements at (550, 60) ==
|
||||
<DIV id="small-box" >
|
||||
<DIV id="large-box" >
|
||||
<DIV id="large-box" >
|
||||
<PRE id="out" >
|
||||
<PRE id="out" >
|
||||
<DIV id="large-box" >
|
||||
<DIV id="small-box" >
|
||||
<PRE id="out" >
|
||||
|
|
|
@ -199,39 +199,42 @@ void InlinePaintable::for_each_fragment(Callback callback) const
|
|||
}
|
||||
}
|
||||
|
||||
Optional<HitTestResult> InlinePaintable::hit_test(CSSPixelPoint position, HitTestType type) const
|
||||
TraversalDecision InlinePaintable::hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const
|
||||
{
|
||||
if (m_clip_rect.has_value() && !m_clip_rect.value().contains(position))
|
||||
return {};
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
auto position_adjusted_by_scroll_offset = position;
|
||||
if (enclosing_scroll_frame_offset().has_value())
|
||||
position_adjusted_by_scroll_offset.translate_by(-enclosing_scroll_frame_offset().value());
|
||||
|
||||
for (auto& fragment : m_fragments) {
|
||||
for (auto const& fragment : m_fragments) {
|
||||
if (fragment.paintable().stacking_context())
|
||||
continue;
|
||||
auto fragment_absolute_rect = fragment.absolute_rect();
|
||||
if (fragment_absolute_rect.contains(position_adjusted_by_scroll_offset)) {
|
||||
if (auto result = fragment.paintable().hit_test(position, type); result.has_value())
|
||||
return result;
|
||||
return HitTestResult { const_cast<Paintable&>(fragment.paintable()),
|
||||
fragment.text_index_at(position_adjusted_by_scroll_offset.x()) };
|
||||
if (fragment.paintable().hit_test(position, type, callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
auto hit_test_result = HitTestResult { const_cast<Paintable&>(fragment.paintable()), fragment.text_index_at(position_adjusted_by_scroll_offset.x()) };
|
||||
if (callback(hit_test_result) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
|
||||
Optional<HitTestResult> hit_test_result;
|
||||
bool should_exit = false;
|
||||
for_each_child([&](Paintable const& child) {
|
||||
if (should_exit)
|
||||
return;
|
||||
if (child.stacking_context())
|
||||
return IterationDecision::Continue;
|
||||
if (auto result = child.hit_test(position, type); result.has_value()) {
|
||||
hit_test_result = result;
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
return IterationDecision::Continue;
|
||||
return;
|
||||
if (child.hit_test(position, type, callback) == TraversalDecision::Break)
|
||||
should_exit = true;
|
||||
});
|
||||
|
||||
return hit_test_result;
|
||||
if (should_exit)
|
||||
return TraversalDecision::Break;
|
||||
|
||||
return TraversalDecision::Continue;
|
||||
}
|
||||
|
||||
CSSPixelRect InlinePaintable::bounding_rect() const
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
|
||||
virtual bool is_inline_paintable() const override { return true; }
|
||||
|
||||
virtual Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const override;
|
||||
virtual TraversalDecision hit_test(CSSPixelPoint, HitTestType, Function<TraversalDecision(HitTestResult)> const& callback) const override;
|
||||
|
||||
void set_box_shadow_data(Vector<ShadowData>&& box_shadow_data) { m_box_shadow_data = move(box_shadow_data); }
|
||||
Vector<ShadowData> const& box_shadow_data() const { return m_box_shadow_data; }
|
||||
|
|
|
@ -98,9 +98,9 @@ bool Paintable::handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned,
|
|||
return false;
|
||||
}
|
||||
|
||||
Optional<HitTestResult> Paintable::hit_test(CSSPixelPoint, HitTestType) const
|
||||
TraversalDecision Paintable::hit_test(CSSPixelPoint, HitTestType, Function<TraversalDecision(HitTestResult)> const&) const
|
||||
{
|
||||
return {};
|
||||
return TraversalDecision::Continue;
|
||||
}
|
||||
|
||||
StackingContext* Paintable::enclosing_stacking_context()
|
||||
|
|
|
@ -143,7 +143,7 @@ public:
|
|||
virtual void apply_clip_overflow_rect(PaintContext&, PaintPhase) const { }
|
||||
virtual void clear_clip_overflow_rect(PaintContext&, PaintPhase) const { }
|
||||
|
||||
virtual Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const;
|
||||
[[nodiscard]] virtual TraversalDecision hit_test(CSSPixelPoint, HitTestType, Function<TraversalDecision(HitTestResult)> const& callback) const;
|
||||
|
||||
virtual bool wants_mouse_events() const { return false; }
|
||||
|
||||
|
|
|
@ -719,17 +719,17 @@ Layout::BlockContainer& PaintableWithLines::layout_box()
|
|||
return static_cast<Layout::BlockContainer&>(PaintableBox::layout_box());
|
||||
}
|
||||
|
||||
Optional<HitTestResult> PaintableBox::hit_test(CSSPixelPoint position, HitTestType type) const
|
||||
TraversalDecision PaintableBox::hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const
|
||||
{
|
||||
if (clip_rect().has_value() && !clip_rect()->contains(position))
|
||||
return {};
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
auto position_adjusted_by_scroll_offset = position;
|
||||
if (enclosing_scroll_frame_offset().has_value())
|
||||
position_adjusted_by_scroll_offset.translate_by(-enclosing_scroll_frame_offset().value());
|
||||
|
||||
if (!is_visible())
|
||||
return {};
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
if (layout_box().is_viewport()) {
|
||||
auto& viewport_paintable = const_cast<ViewportPaintable&>(static_cast<ViewportPaintable const&>(*this));
|
||||
|
@ -737,46 +737,55 @@ Optional<HitTestResult> PaintableBox::hit_test(CSSPixelPoint position, HitTestTy
|
|||
viewport_paintable.document().update_paint_and_hit_testing_properties_if_needed();
|
||||
viewport_paintable.refresh_scroll_state();
|
||||
viewport_paintable.refresh_clip_state();
|
||||
return stacking_context()->hit_test(position, type);
|
||||
return stacking_context()->hit_test(position, type, callback);
|
||||
}
|
||||
|
||||
if (!absolute_border_box_rect().contains(position_adjusted_by_scroll_offset.x(), position_adjusted_by_scroll_offset.y()))
|
||||
return {};
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
auto result = child->hit_test(position, type);
|
||||
if (!result.has_value())
|
||||
for (auto const* child = last_child(); child; child = child->previous_sibling()) {
|
||||
auto z_index = child->computed_values().z_index();
|
||||
if (child->layout_node().is_positioned() && z_index.value_or(0) == 0)
|
||||
continue;
|
||||
if (!result->paintable->visible_for_hit_testing())
|
||||
continue;
|
||||
return result;
|
||||
if (child->hit_test(position, type, callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
|
||||
if (!visible_for_hit_testing())
|
||||
return {};
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
return HitTestResult { const_cast<PaintableBox&>(*this) };
|
||||
return callback(HitTestResult { const_cast<PaintableBox&>(*this) });
|
||||
}
|
||||
|
||||
Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, HitTestType type) const
|
||||
Optional<HitTestResult> PaintableBox::hit_test(CSSPixelPoint position, HitTestType type) const
|
||||
{
|
||||
Optional<HitTestResult> result;
|
||||
(void)PaintableBox::hit_test(position, type, [&](HitTestResult candidate) {
|
||||
VERIFY(!result.has_value());
|
||||
if (!candidate.paintable->visible_for_hit_testing())
|
||||
return Painting::TraversalDecision::Continue;
|
||||
result = move(candidate);
|
||||
return Painting::TraversalDecision::Break;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
TraversalDecision PaintableWithLines::hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const
|
||||
{
|
||||
if (clip_rect().has_value() && !clip_rect()->contains(position))
|
||||
return {};
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
auto position_adjusted_by_scroll_offset = position;
|
||||
if (enclosing_scroll_frame_offset().has_value())
|
||||
position_adjusted_by_scroll_offset.translate_by(-enclosing_scroll_frame_offset().value());
|
||||
|
||||
if (!layout_box().children_are_inline() || m_fragments.is_empty())
|
||||
return PaintableBox::hit_test(position, type);
|
||||
if (!layout_box().children_are_inline() || m_fragments.is_empty()) {
|
||||
return PaintableBox::hit_test(position, type, callback);
|
||||
}
|
||||
|
||||
for (auto* child = first_child(); child; child = child->next_sibling()) {
|
||||
auto result = child->hit_test(position, type);
|
||||
if (!result.has_value())
|
||||
continue;
|
||||
if (!result->paintable->visible_for_hit_testing())
|
||||
continue;
|
||||
return result;
|
||||
for (auto const* child = last_child(); child; child = child->previous_sibling()) {
|
||||
if (child->hit_test(position, type, callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
|
||||
Optional<HitTestResult> last_good_candidate;
|
||||
|
@ -785,9 +794,11 @@ Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, Hit
|
|||
continue;
|
||||
auto fragment_absolute_rect = fragment.absolute_rect();
|
||||
if (fragment_absolute_rect.contains(position_adjusted_by_scroll_offset)) {
|
||||
if (auto result = fragment.paintable().hit_test(position, type); result.has_value())
|
||||
return result;
|
||||
return HitTestResult { const_cast<Paintable&>(fragment.paintable()), fragment.text_index_at(position_adjusted_by_scroll_offset.x()) };
|
||||
if (fragment.paintable().hit_test(position, type, callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
HitTestResult hit_test_result { const_cast<Paintable&>(fragment.paintable()), fragment.text_index_at(position_adjusted_by_scroll_offset.x()) };
|
||||
if (callback(hit_test_result) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
|
||||
// If we reached this point, the position is not within the fragment. However, the fragment start or end might be the place to place the cursor.
|
||||
|
@ -808,11 +819,16 @@ Optional<HitTestResult> PaintableWithLines::hit_test(CSSPixelPoint position, Hit
|
|||
}
|
||||
}
|
||||
|
||||
if (type == HitTestType::TextCursor && last_good_candidate.has_value())
|
||||
return last_good_candidate;
|
||||
if (is_visible() && absolute_border_box_rect().contains(position_adjusted_by_scroll_offset.x(), position_adjusted_by_scroll_offset.y()))
|
||||
return HitTestResult { const_cast<PaintableWithLines&>(*this) };
|
||||
return {};
|
||||
if (type == HitTestType::TextCursor && last_good_candidate.has_value()) {
|
||||
if (callback(last_good_candidate.value()) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
if (!stacking_context() && is_visible() && absolute_border_box_rect().contains(position_adjusted_by_scroll_offset.x(), position_adjusted_by_scroll_offset.y())) {
|
||||
if (callback(HitTestResult { const_cast<PaintableWithLines&>(*this) }) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
|
||||
return TraversalDecision::Continue;
|
||||
}
|
||||
|
||||
void PaintableBox::set_needs_display() const
|
||||
|
|
|
@ -136,7 +136,8 @@ public:
|
|||
virtual void apply_clip_overflow_rect(PaintContext&, PaintPhase) const override;
|
||||
virtual void clear_clip_overflow_rect(PaintContext&, PaintPhase) const override;
|
||||
|
||||
virtual Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const override;
|
||||
[[nodiscard]] virtual TraversalDecision hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const override;
|
||||
Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const;
|
||||
|
||||
virtual bool handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y) override;
|
||||
|
||||
|
@ -286,7 +287,7 @@ public:
|
|||
virtual void paint(PaintContext&, PaintPhase) const override;
|
||||
virtual bool wants_mouse_events() const override { return false; }
|
||||
|
||||
virtual Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const override;
|
||||
[[nodiscard]] virtual TraversalDecision hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const override;
|
||||
|
||||
virtual void visit_edges(Cell::Visitor& visitor) override
|
||||
{
|
||||
|
|
|
@ -26,15 +26,14 @@ Layout::SVGGraphicsBox const& SVGPathPaintable::layout_box() const
|
|||
return static_cast<Layout::SVGGraphicsBox const&>(layout_node());
|
||||
}
|
||||
|
||||
Optional<HitTestResult> SVGPathPaintable::hit_test(CSSPixelPoint position, HitTestType type) const
|
||||
TraversalDecision SVGPathPaintable::hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const
|
||||
{
|
||||
auto result = SVGGraphicsPaintable::hit_test(position, type);
|
||||
if (!result.has_value() || !computed_path().has_value())
|
||||
return {};
|
||||
if (!computed_path().has_value())
|
||||
return TraversalDecision::Continue;
|
||||
auto transformed_bounding_box = computed_transforms().svg_to_css_pixels_transform().map_to_quad(computed_path()->bounding_box());
|
||||
if (!transformed_bounding_box.contains(position.to_type<float>()))
|
||||
return {};
|
||||
return result;
|
||||
return TraversalDecision::Continue;
|
||||
return SVGGraphicsPaintable::hit_test(position, type, callback);
|
||||
}
|
||||
|
||||
static Gfx::Painter::WindingRule to_gfx_winding_rule(SVG::FillRule fill_rule)
|
||||
|
|
|
@ -17,7 +17,7 @@ class SVGPathPaintable final : public SVGGraphicsPaintable {
|
|||
public:
|
||||
static JS::NonnullGCPtr<SVGPathPaintable> create(Layout::SVGGraphicsBox const&);
|
||||
|
||||
virtual Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const override;
|
||||
virtual TraversalDecision hit_test(CSSPixelPoint, HitTestType, Function<TraversalDecision(HitTestResult)> const& callback) const override;
|
||||
|
||||
virtual void paint(PaintContext&, PaintPhase) const override;
|
||||
|
||||
|
|
|
@ -356,10 +356,10 @@ static TraversalDecision for_each_in_subtree_within_same_stacking_context_in_rev
|
|||
return TraversalDecision::Continue;
|
||||
}
|
||||
|
||||
Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTestType type) const
|
||||
TraversalDecision StackingContext::hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const
|
||||
{
|
||||
if (!paintable().is_visible())
|
||||
return {};
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
CSSPixelPoint transform_origin { 0, 0 };
|
||||
if (paintable().is_paintable_box())
|
||||
|
@ -385,33 +385,33 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
|
|||
auto const& child = *m_children[i];
|
||||
if (child.paintable().computed_values().z_index().value_or(0) <= 0)
|
||||
break;
|
||||
auto result = child.hit_test(transformed_position, type);
|
||||
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
||||
return result;
|
||||
if (child.hit_test(transformed_position, type, callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
|
||||
bool should_exit = false;
|
||||
|
||||
// 6. the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
|
||||
Optional<HitTestResult> result;
|
||||
for_each_in_subtree_within_same_stacking_context_in_reverse(paintable(), [&](Paintable const& paintable) {
|
||||
VERIFY(!should_exit);
|
||||
if (!paintable.is_paintable_box())
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
auto const& paintable_box = verify_cast<PaintableBox>(paintable);
|
||||
|
||||
auto const& z_index = paintable_box.computed_values().z_index();
|
||||
if (z_index.value_or(0) == 0 && paintable_box.is_positioned() && !paintable_box.stacking_context()) {
|
||||
auto candidate = paintable_box.hit_test(transformed_position, type);
|
||||
if (candidate.has_value() && candidate->paintable->visible_for_hit_testing()) {
|
||||
result = move(candidate);
|
||||
auto positioned_element_without_stacking_context = paintable_box.is_positioned() && !paintable_box.stacking_context();
|
||||
if (z_index.value_or(0) == 0 && (positioned_element_without_stacking_context || paintable_box.layout_node().is_grid_item())) {
|
||||
if (paintable_box.hit_test(transformed_position, type, callback) == TraversalDecision::Break) {
|
||||
should_exit = true;
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
|
||||
if (paintable_box.stacking_context()) {
|
||||
if (z_index.value_or(0) == 0) {
|
||||
auto candidate = paintable_box.stacking_context()->hit_test(transformed_position, type);
|
||||
if (candidate.has_value() && candidate->paintable->visible_for_hit_testing()) {
|
||||
result = move(candidate);
|
||||
if (paintable_box.stacking_context()->hit_test(transformed_position, type, callback) == TraversalDecision::Break) {
|
||||
should_exit = true;
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
|
@ -419,50 +419,47 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
|
|||
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
if (result.has_value())
|
||||
return result;
|
||||
|
||||
if (should_exit)
|
||||
return TraversalDecision::Break;
|
||||
|
||||
// 5. the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
|
||||
if (paintable().layout_node().children_are_inline() && is<Layout::BlockContainer>(paintable().layout_node())) {
|
||||
auto result = paintable_box().hit_test(transformed_position, type);
|
||||
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
||||
return result;
|
||||
if (paintable_box().hit_test(transformed_position, type, callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
|
||||
// 4. the non-positioned floats.
|
||||
for_each_in_subtree_within_same_stacking_context_in_reverse(paintable(), [&](Paintable const& paintable) {
|
||||
VERIFY(!should_exit);
|
||||
if (!paintable.is_paintable_box())
|
||||
return TraversalDecision::Continue;
|
||||
|
||||
auto const& paintable_box = verify_cast<PaintableBox>(paintable);
|
||||
if (paintable_box.is_floating()) {
|
||||
if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
|
||||
result = move(candidate);
|
||||
if (paintable_box.hit_test(transformed_position, type, callback) == TraversalDecision::Break) {
|
||||
should_exit = true;
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
||||
return result;
|
||||
|
||||
if (should_exit)
|
||||
return TraversalDecision::Break;
|
||||
|
||||
// 3. the in-flow, non-inline-level, non-positioned descendants.
|
||||
if (!paintable().layout_node().children_are_inline()) {
|
||||
for_each_in_subtree_within_same_stacking_context_in_reverse(paintable(), [&](Paintable const& paintable) {
|
||||
if (!paintable.is_paintable_box())
|
||||
return TraversalDecision::Continue;
|
||||
for (auto const* child = paintable().last_child(); child; child = child->previous_sibling()) {
|
||||
if (!child->is_paintable_box())
|
||||
continue;
|
||||
|
||||
auto const& paintable_box = verify_cast<PaintableBox>(paintable);
|
||||
if (!paintable_box.is_absolutely_positioned() && !paintable_box.is_floating()) {
|
||||
if (auto candidate = paintable_box.hit_test(transformed_position, type); candidate.has_value()) {
|
||||
result = move(candidate);
|
||||
auto const& paintable_box = verify_cast<PaintableBox>(*child);
|
||||
if (!paintable_box.is_absolutely_positioned() && !paintable_box.is_floating() && !paintable_box.stacking_context()) {
|
||||
if (paintable_box.hit_test(transformed_position, type, callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
return TraversalDecision::Continue;
|
||||
});
|
||||
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. the child stacking contexts with negative stack levels (most negative first).
|
||||
|
@ -471,21 +468,20 @@ Optional<HitTestResult> StackingContext::hit_test(CSSPixelPoint position, HitTes
|
|||
auto const& child = *m_children[i];
|
||||
if (child.paintable().computed_values().z_index().value_or(0) >= 0)
|
||||
break;
|
||||
auto result = child.hit_test(transformed_position, type);
|
||||
if (result.has_value() && result->paintable->visible_for_hit_testing())
|
||||
return result;
|
||||
if (child.hit_test(transformed_position, type, callback) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
|
||||
// 1. the background and borders of the element forming the stacking context.
|
||||
if (paintable().is_paintable_box()) {
|
||||
if (paintable_box().absolute_border_box_rect().contains(transformed_position.x(), transformed_position.y())) {
|
||||
return HitTestResult {
|
||||
.paintable = const_cast<PaintableBox&>(paintable_box()),
|
||||
};
|
||||
auto hit_test_result = HitTestResult { .paintable = const_cast<PaintableBox&>(paintable_box()) };
|
||||
if (callback(hit_test_result) == TraversalDecision::Break)
|
||||
return TraversalDecision::Break;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
return TraversalDecision::Continue;
|
||||
}
|
||||
|
||||
void StackingContext::dump(int indent) const
|
||||
|
|
|
@ -35,7 +35,8 @@ public:
|
|||
static void paint_node_as_stacking_context(Paintable const&, PaintContext&);
|
||||
static void paint_descendants(PaintContext&, Paintable const&, StackingContextPaintPhase);
|
||||
void paint(PaintContext&) const;
|
||||
Optional<HitTestResult> hit_test(CSSPixelPoint, HitTestType) const;
|
||||
|
||||
[[nodiscard]] TraversalDecision hit_test(CSSPixelPoint, HitTestType, Function<TraversalDecision(HitTestResult)> const& callback) const;
|
||||
|
||||
Gfx::AffineTransform affine_transform_matrix() const;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue