ladybird/Userland/Libraries/LibWeb/Painting/Paintable.h
Aliaksandr Kalenik f3d57e1157 LibWeb: Clip hidden overflow by absolute rect of containing block
Since handling overflow: hidden in PaintableBox::before_children_paint
while following paint traversal order can't result in correctly computed
clip rectangle for elements that create their own stacking context
(because before_children_paint is called only for parent but overflow:
hidden can be set somewhere deeper but not in direct ancestor), here
introduced new function PaintableBox::clip_rect() that computes clip
rectangle by looking into containing block.

should_clip_overflow flag that disables clip for absolutely positioned
elements in before_children_paint and after_children_paint is removed
because after changing clip rectangle to be computed from not parent
but containing block it is not needed anymore (absolutely positioned
item is clipped if it's containing block has hidden overflow)
2022-11-15 22:53:47 +01:00

160 lines
5.1 KiB
C++

/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullOwnPtr.h>
#include <LibWeb/Layout/Box.h>
#include <LibWeb/Layout/LineBox.h>
#include <LibWeb/Layout/TextNode.h>
namespace Web::Painting {
enum class TraversalDecision {
Continue,
SkipChildrenAndContinue,
Break,
};
enum class PaintPhase {
Background,
Border,
Foreground,
FocusOutline,
Overlay,
};
struct HitTestResult {
NonnullRefPtr<Painting::Paintable> paintable;
int index_in_node { 0 };
enum InternalPosition {
None,
Before,
Inside,
After,
};
InternalPosition internal_position { None };
DOM::Node* dom_node();
DOM::Node const* dom_node() const;
};
enum class HitTestType {
Exact, // Exact matches only
TextCursor, // Clicking past the right/bottom edge of text will still hit the text
};
class Paintable : public RefCounted<Paintable> {
AK_MAKE_NONMOVABLE(Paintable);
AK_MAKE_NONCOPYABLE(Paintable);
public:
virtual ~Paintable() = default;
Paintable const* first_child() const;
Paintable const* last_child() const;
Paintable const* next_sibling() const;
Paintable const* previous_sibling() const;
template<typename U, typename Callback>
TraversalDecision for_each_in_inclusive_subtree_of_type(Callback callback) const
{
if (is<U>(*this)) {
if (auto decision = callback(static_cast<U const&>(*this)); decision != TraversalDecision::Continue)
return decision;
}
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
template<typename U, typename Callback>
TraversalDecision for_each_in_subtree_of_type(Callback callback) const
{
for (auto* child = first_child(); child; child = child->next_sibling()) {
if (child->template for_each_in_inclusive_subtree_of_type<U>(callback) == TraversalDecision::Break)
return TraversalDecision::Break;
}
return TraversalDecision::Continue;
}
virtual void paint(PaintContext&, PaintPhase) const { }
virtual void before_children_paint(PaintContext&, PaintPhase) const { }
virtual void after_children_paint(PaintContext&, PaintPhase) const { }
virtual Optional<HitTestResult> hit_test(Gfx::FloatPoint const&, HitTestType) const;
virtual bool wants_mouse_events() const { return false; }
enum class DispatchEventOfSameName {
Yes,
No,
};
// When these methods return true, the DOM event with the same name will be
// dispatch at the mouse_event_target if it returns a valid DOM::Node, or
// the layout node's associated DOM node if it doesn't.
virtual DispatchEventOfSameName handle_mousedown(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers);
virtual DispatchEventOfSameName handle_mouseup(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers);
virtual DispatchEventOfSameName handle_mousemove(Badge<EventHandler>, Gfx::IntPoint const&, unsigned buttons, unsigned modifiers);
virtual DOM::Node* mouse_event_target() const { return nullptr; }
virtual bool handle_mousewheel(Badge<EventHandler>, Gfx::IntPoint const&, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y);
Layout::Node const& layout_node() const { return m_layout_node; }
Layout::Node& layout_node() { return const_cast<Layout::Node&>(m_layout_node); }
DOM::Node* dom_node() { return layout_node().dom_node(); }
DOM::Node const* dom_node() const { return layout_node().dom_node(); }
auto const& computed_values() const { return m_layout_node.computed_values(); }
bool visible_for_hit_testing() const { return computed_values().pointer_events() != CSS::PointerEvents::None; }
HTML::BrowsingContext const& browsing_context() const { return m_layout_node.browsing_context(); }
HTML::BrowsingContext& browsing_context() { return layout_node().browsing_context(); }
void set_needs_display() const { const_cast<Layout::Node&>(m_layout_node).set_needs_display(); }
Layout::BlockContainer const* containing_block() const
{
if (!m_containing_block.has_value())
m_containing_block = const_cast<Layout::Node&>(m_layout_node).containing_block();
return *m_containing_block;
}
template<typename T>
bool fast_is() const = delete;
protected:
explicit Paintable(Layout::Node const& layout_node)
: m_layout_node(layout_node)
{
}
private:
Layout::Node const& m_layout_node;
Optional<Layout::BlockContainer*> mutable m_containing_block;
};
inline DOM::Node* HitTestResult::dom_node()
{
return paintable->dom_node();
}
inline DOM::Node const* HitTestResult::dom_node() const
{
return paintable->dom_node();
}
template<>
inline bool Paintable::fast_is<PaintableBox>() const { return m_layout_node.is_box(); }
}