diff options
Diffstat (limited to 'Userland/Libraries')
35 files changed, 560 insertions, 429 deletions
diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index da7b9397b8..1635d1e59b 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -246,6 +246,7 @@ set(SOURCES Layout/InlineLevelIterator.cpp Layout/InlineNode.cpp Layout/Label.cpp + Layout/LabelableNode.cpp Layout/LayoutPosition.cpp Layout/LineBox.cpp Layout/LineBoxFragment.cpp @@ -289,6 +290,7 @@ set(SOURCES Painting/CheckBoxPaintable.cpp Painting/ImagePaintable.cpp Painting/InlinePaintable.cpp + Painting/LabelablePaintable.cpp Painting/MarkerPaintable.cpp Painting/NestedBrowsingContextPaintable.cpp Painting/PaintContext.cpp diff --git a/Userland/Libraries/LibWeb/DOM/Node.cpp b/Userland/Libraries/LibWeb/DOM/Node.cpp index 5241cb9f1d..b1eb86a63d 100644 --- a/Userland/Libraries/LibWeb/DOM/Node.cpp +++ b/Userland/Libraries/LibWeb/DOM/Node.cpp @@ -1010,6 +1010,13 @@ size_t Node::length() const return child_count(); } +Painting::Paintable const* Node::paintable() const +{ + if (!layout_node()) + return nullptr; + return layout_node()->paintable(); +} + Painting::PaintableBox const* Node::paint_box() const { if (!layout_node()) diff --git a/Userland/Libraries/LibWeb/DOM/Node.h b/Userland/Libraries/LibWeb/DOM/Node.h index 8b9b641de2..536738c9f0 100644 --- a/Userland/Libraries/LibWeb/DOM/Node.h +++ b/Userland/Libraries/LibWeb/DOM/Node.h @@ -159,6 +159,7 @@ public: Layout::Node* layout_node() { return m_layout_node; } Painting::PaintableBox const* paint_box() const; + Painting::Paintable const* paintable() const; void set_layout_node(Badge<Layout::Node>, Layout::Node*) const; diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index f088b61bbe..730437e732 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -265,9 +265,13 @@ class PerformanceTiming; namespace Web::Painting { enum class PaintPhase; +class ButtonPaintable; +class CheckBoxPaintable; +class LabelablePaintable; class Paintable; class PaintableBox; class PaintableWithLines; +class TextPaintable; } namespace Web::RequestIdleCallback { diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index 535c405ed5..f71f8b1259 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -29,7 +29,7 @@ HTMLInputElement::~HTMLInputElement() { } -void HTMLInputElement::did_click_button(Badge<Layout::ButtonBox>) +void HTMLInputElement::did_click_button(Badge<Painting::ButtonPaintable>) { // FIXME: This should be a PointerEvent. dispatch_event(DOM::Event::create(EventNames::click)); @@ -42,7 +42,7 @@ void HTMLInputElement::did_click_button(Badge<Layout::ButtonBox>) } } -void HTMLInputElement::did_click_checkbox(Badge<Layout::CheckBox>) +void HTMLInputElement::did_click_checkbox(Badge<Painting::CheckBoxPaintable>) { // FIXME: This should be a PointerEvent. auto click_event = DOM::Event::create(EventNames::click); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index 5027ea12a4..7d5e6b2ec1 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -73,8 +73,8 @@ public: }; void set_checked(bool, ChangeSource = ChangeSource::Programmatic, ShouldRunActivationBehavior = ShouldRunActivationBehavior::Yes); - void did_click_button(Badge<Layout::ButtonBox>); - void did_click_checkbox(Badge<Layout::CheckBox>); + void did_click_button(Badge<Painting::ButtonPaintable>); + void did_click_checkbox(Badge<Painting::CheckBoxPaintable>); void did_edit_text_node(Badge<BrowsingContext>); diff --git a/Userland/Libraries/LibWeb/Layout/BlockContainer.cpp b/Userland/Libraries/LibWeb/Layout/BlockContainer.cpp index 5b320da24c..dcfa305ae5 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockContainer.cpp +++ b/Userland/Libraries/LibWeb/Layout/BlockContainer.cpp @@ -69,17 +69,6 @@ void BlockContainer::set_scroll_offset(const Gfx::FloatPoint& offset) set_needs_display(); } -bool BlockContainer::handle_mousewheel(Badge<EventHandler>, const Gfx::IntPoint&, unsigned int, unsigned int, int wheel_delta_x, int wheel_delta_y) -{ - if (!is_scrollable()) - return false; - auto new_offset = m_scroll_offset; - new_offset.translate_by(wheel_delta_x, wheel_delta_y); - set_scroll_offset(new_offset); - - return true; -} - Painting::PaintableWithLines const* BlockContainer::paint_box() const { return static_cast<Painting::PaintableWithLines const*>(Box::paint_box()); diff --git a/Userland/Libraries/LibWeb/Layout/BlockContainer.h b/Userland/Libraries/LibWeb/Layout/BlockContainer.h index 5cf325d7a3..162f40f12e 100644 --- a/Userland/Libraries/LibWeb/Layout/BlockContainer.h +++ b/Userland/Libraries/LibWeb/Layout/BlockContainer.h @@ -35,8 +35,6 @@ public: private: virtual bool is_block_container() const final { return true; } - virtual bool wants_mouse_events() const override { return false; } - virtual bool handle_mousewheel(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y) override; Gfx::FloatPoint m_scroll_offset; }; diff --git a/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp index 5ed1ba9a2d..c0db4bafd3 100644 --- a/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/ButtonBox.cpp @@ -31,81 +31,6 @@ void ButtonBox::prepare_for_replaced_layout() set_intrinsic_height(font().glyph_height()); } -void ButtonBox::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned) -{ - if (button != GUI::MouseButton::Primary || !dom_node().enabled()) - return; - - m_being_pressed = true; - set_needs_display(); - - m_tracking_mouse = true; - browsing_context().event_handler().set_mouse_event_tracking_layout_node(this); -} - -void ButtonBox::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) -{ - if (!m_tracking_mouse || button != GUI::MouseButton::Primary || !dom_node().enabled()) - return; - - // NOTE: Handling the click may run arbitrary JS, which could disappear this node. - NonnullRefPtr protected_this = *this; - NonnullRefPtr protected_browsing_context = browsing_context(); - - bool is_inside_node_or_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position); - if (!is_inside_node_or_label) - is_inside_node_or_label = Label::is_inside_associated_label(*this, position); - - if (is_inside_node_or_label) - dom_node().did_click_button({}); - - m_being_pressed = false; - m_tracking_mouse = false; - - protected_browsing_context->event_handler().set_mouse_event_tracking_layout_node(nullptr); -} - -void ButtonBox::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned) -{ - if (!m_tracking_mouse || !dom_node().enabled()) - return; - - bool is_inside_node_or_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position); - if (!is_inside_node_or_label) - is_inside_node_or_label = Label::is_inside_associated_label(*this, position); - - if (m_being_pressed == is_inside_node_or_label) - return; - - m_being_pressed = is_inside_node_or_label; - set_needs_display(); -} - -void ButtonBox::handle_associated_label_mousedown(Badge<Label>) -{ - m_being_pressed = true; - set_needs_display(); -} - -void ButtonBox::handle_associated_label_mouseup(Badge<Label>) -{ - // NOTE: Handling the click may run arbitrary JS, which could disappear this node. - NonnullRefPtr protected_this = *this; - NonnullRefPtr protected_browsing_context = browsing_context(); - - dom_node().did_click_button({}); - m_being_pressed = false; -} - -void ButtonBox::handle_associated_label_mousemove(Badge<Label>, bool is_inside_node_or_label) -{ - if (m_being_pressed == is_inside_node_or_label) - return; - - m_being_pressed = is_inside_node_or_label; - set_needs_display(); -} - RefPtr<Painting::Paintable> ButtonBox::create_paintable() const { return Painting::ButtonPaintable::create(*this); diff --git a/Userland/Libraries/LibWeb/Layout/ButtonBox.h b/Userland/Libraries/LibWeb/Layout/ButtonBox.h index c57076a765..1c0b11283e 100644 --- a/Userland/Libraries/LibWeb/Layout/ButtonBox.h +++ b/Userland/Libraries/LibWeb/Layout/ButtonBox.h @@ -21,22 +21,8 @@ public: const HTML::HTMLInputElement& dom_node() const { return static_cast<const HTML::HTMLInputElement&>(LabelableNode::dom_node()); } HTML::HTMLInputElement& dom_node() { return static_cast<HTML::HTMLInputElement&>(LabelableNode::dom_node()); } - bool being_pressed() const { return m_being_pressed; } - - virtual RefPtr<Painting::Paintable> create_paintable() const override; - private: - virtual bool wants_mouse_events() const override { return true; } - virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; - virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; - virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers) override; - - virtual void handle_associated_label_mousedown(Badge<Label>) override; - virtual void handle_associated_label_mouseup(Badge<Label>) override; - virtual void handle_associated_label_mousemove(Badge<Label>, bool is_inside_node_or_label) override; - - bool m_being_pressed { false }; - bool m_tracking_mouse { false }; + virtual RefPtr<Painting::Paintable> create_paintable() const override; }; } diff --git a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp index 2331491e39..4a3b348554 100644 --- a/Userland/Libraries/LibWeb/Layout/CheckBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/CheckBox.cpp @@ -4,10 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include <LibGUI/Event.h> #include <LibGfx/Font.h> -#include <LibGfx/Painter.h> -#include <LibGfx/StylePainter.h> #include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/HTML/HTMLInputElement.h> #include <LibWeb/Layout/CheckBox.h> @@ -27,87 +24,6 @@ CheckBox::~CheckBox() { } -void CheckBox::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned) -{ - if (button != GUI::MouseButton::Primary || !dom_node().enabled()) - return; - - m_being_pressed = true; - set_needs_display(); - - m_tracking_mouse = true; - browsing_context().event_handler().set_mouse_event_tracking_layout_node(this); -} - -void CheckBox::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) -{ - if (!m_tracking_mouse || button != GUI::MouseButton::Primary || !dom_node().enabled()) - return; - - // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. - NonnullRefPtr protect = *this; - - bool is_inside_node_or_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position); - if (!is_inside_node_or_label) - is_inside_node_or_label = Label::is_inside_associated_label(*this, position); - - if (is_inside_node_or_label) { - dom_node().did_click_checkbox({}); - dom_node().set_checked(!dom_node().checked(), HTML::HTMLInputElement::ChangeSource::User); - } - - m_being_pressed = false; - m_tracking_mouse = false; - browsing_context().event_handler().set_mouse_event_tracking_layout_node(nullptr); -} - -void CheckBox::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned) -{ - if (!m_tracking_mouse || !dom_node().enabled()) - return; - - bool is_inside_node_or_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position); - if (!is_inside_node_or_label) - is_inside_node_or_label = Label::is_inside_associated_label(*this, position); - - if (m_being_pressed == is_inside_node_or_label) - return; - - m_being_pressed = is_inside_node_or_label; - set_needs_display(); -} - -void CheckBox::handle_associated_label_mousedown(Badge<Label>) -{ - if (!dom_node().enabled()) - return; - - m_being_pressed = true; - set_needs_display(); -} - -void CheckBox::handle_associated_label_mouseup(Badge<Label>) -{ - if (!dom_node().enabled()) - return; - - // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. - NonnullRefPtr protect = *this; - - dom_node().did_click_checkbox({}); - dom_node().set_checked(!dom_node().checked(), HTML::HTMLInputElement::ChangeSource::User); - m_being_pressed = false; -} - -void CheckBox::handle_associated_label_mousemove(Badge<Label>, bool is_inside_node_or_label) -{ - if (m_being_pressed == is_inside_node_or_label || !dom_node().enabled()) - return; - - m_being_pressed = is_inside_node_or_label; - set_needs_display(); -} - RefPtr<Painting::Paintable> CheckBox::create_paintable() const { return Painting::CheckBoxPaintable::create(*this); diff --git a/Userland/Libraries/LibWeb/Layout/CheckBox.h b/Userland/Libraries/LibWeb/Layout/CheckBox.h index 7a20d3f6e0..f1c77d3769 100644 --- a/Userland/Libraries/LibWeb/Layout/CheckBox.h +++ b/Userland/Libraries/LibWeb/Layout/CheckBox.h @@ -19,22 +19,8 @@ public: const HTML::HTMLInputElement& dom_node() const { return static_cast<const HTML::HTMLInputElement&>(LabelableNode::dom_node()); } HTML::HTMLInputElement& dom_node() { return static_cast<HTML::HTMLInputElement&>(LabelableNode::dom_node()); } - bool being_pressed() const { return m_being_pressed; } - - virtual RefPtr<Painting::Paintable> create_paintable() const override; - private: - virtual bool wants_mouse_events() const override { return true; } - virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; - virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; - virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers) override; - - virtual void handle_associated_label_mousedown(Badge<Label>) override; - virtual void handle_associated_label_mouseup(Badge<Label>) override; - virtual void handle_associated_label_mousemove(Badge<Label>, bool is_inside_node_or_label) override; - - bool m_being_pressed { false }; - bool m_tracking_mouse { false }; + virtual RefPtr<Painting::Paintable> create_paintable() const override; }; } diff --git a/Userland/Libraries/LibWeb/Layout/Label.cpp b/Userland/Libraries/LibWeb/Layout/Label.cpp index f68a0b864c..1f6930c9ba 100644 --- a/Userland/Libraries/LibWeb/Layout/Label.cpp +++ b/Userland/Libraries/LibWeb/Layout/Label.cpp @@ -5,16 +5,14 @@ */ #include <LibGUI/Event.h> -#include <LibGfx/Painter.h> #include <LibGfx/StylePainter.h> #include <LibWeb/DOM/Document.h> #include <LibWeb/DOM/Element.h> -#include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/Layout/InitialContainingBlock.h> #include <LibWeb/Layout/Label.h> #include <LibWeb/Layout/LabelableNode.h> #include <LibWeb/Layout/TextNode.h> -#include <LibWeb/Painting/Paintable.h> +#include <LibWeb/Painting/LabelablePaintable.h> namespace Web::Layout { @@ -27,18 +25,18 @@ Label::~Label() { } -void Label::handle_mousedown_on_label(Badge<TextNode>, const Gfx::IntPoint&, unsigned button) +void Label::handle_mousedown_on_label(Badge<Painting::TextPaintable>, Gfx::IntPoint const&, unsigned button) { if (button != GUI::MouseButton::Primary) return; if (auto* control = labeled_control(); control) - control->handle_associated_label_mousedown({}); + control->paintable()->handle_associated_label_mousedown({}); m_tracking_mouse = true; } -void Label::handle_mouseup_on_label(Badge<TextNode>, const Gfx::IntPoint& position, unsigned button) +void Label::handle_mouseup_on_label(Badge<Painting::TextPaintable>, Gfx::IntPoint const& position, unsigned button) { if (!m_tracking_mouse || button != GUI::MouseButton::Primary) return; @@ -51,13 +49,13 @@ void Label::handle_mouseup_on_label(Badge<TextNode>, const Gfx::IntPoint& positi bool is_inside_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position); if (is_inside_control || is_inside_label) - control->handle_associated_label_mouseup({}); + control->paintable()->handle_associated_label_mouseup({}); } m_tracking_mouse = false; } -void Label::handle_mousemove_on_label(Badge<TextNode>, const Gfx::IntPoint& position, unsigned) +void Label::handle_mousemove_on_label(Badge<Painting::TextPaintable>, Gfx::IntPoint const& position, unsigned) { if (!m_tracking_mouse) return; @@ -66,18 +64,18 @@ void Label::handle_mousemove_on_label(Badge<TextNode>, const Gfx::IntPoint& posi bool is_inside_control = enclosing_int_rect(control->paint_box()->absolute_rect()).contains(position); bool is_inside_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position); - control->handle_associated_label_mousemove({}, is_inside_control || is_inside_label); + control->paintable()->handle_associated_label_mousemove({}, is_inside_control || is_inside_label); } } -bool Label::is_inside_associated_label(LabelableNode& control, const Gfx::IntPoint& position) +bool Label::is_inside_associated_label(LabelableNode const& control, const Gfx::IntPoint& position) { if (auto* label = label_for_control_node(control); label) return enclosing_int_rect(label->paint_box()->absolute_rect()).contains(position); return false; } -bool Label::is_associated_label_hovered(LabelableNode& control) +bool Label::is_associated_label_hovered(LabelableNode const& control) { if (auto* label = label_for_control_node(control); label) { if (label->document().hovered_node() == &label->dom_node()) @@ -91,7 +89,7 @@ bool Label::is_associated_label_hovered(LabelableNode& control) } // https://html.spec.whatwg.org/multipage/forms.html#labeled-control -Label* Label::label_for_control_node(LabelableNode& control) +Label const* Label::label_for_control_node(LabelableNode const& control) { if (!control.document().layout_node()) return nullptr; @@ -102,7 +100,7 @@ Label* Label::label_for_control_node(LabelableNode& control) // whose ID is equal to the value of the for attribute, and the first such element in tree order is // a labelable element, then that element is the label element's labeled control. if (auto id = control.dom_node().attribute(HTML::AttributeNames::id); !id.is_empty()) { - Label* label = nullptr; + Label const* label = nullptr; control.document().layout_node()->for_each_in_inclusive_subtree_of_type<Label>([&](auto& node) { if (node.dom_node().for_() == id) { diff --git a/Userland/Libraries/LibWeb/Layout/Label.h b/Userland/Libraries/LibWeb/Layout/Label.h index 620365223c..22c3ccc018 100644 --- a/Userland/Libraries/LibWeb/Layout/Label.h +++ b/Userland/Libraries/LibWeb/Layout/Label.h @@ -16,20 +16,20 @@ public: Label(DOM::Document&, HTML::HTMLLabelElement*, NonnullRefPtr<CSS::StyleProperties>); virtual ~Label() override; - static bool is_inside_associated_label(LabelableNode&, const Gfx::IntPoint&); - static bool is_associated_label_hovered(LabelableNode&); + static bool is_inside_associated_label(LabelableNode const&, Gfx::IntPoint const&); + static bool is_associated_label_hovered(LabelableNode const&); const HTML::HTMLLabelElement& dom_node() const { return static_cast<const HTML::HTMLLabelElement&>(*BlockContainer::dom_node()); } HTML::HTMLLabelElement& dom_node() { return static_cast<HTML::HTMLLabelElement&>(*BlockContainer::dom_node()); } - void handle_mousedown_on_label(Badge<TextNode>, const Gfx::IntPoint&, unsigned button); - void handle_mouseup_on_label(Badge<TextNode>, const Gfx::IntPoint&, unsigned button); - void handle_mousemove_on_label(Badge<TextNode>, const Gfx::IntPoint&, unsigned button); + void handle_mousedown_on_label(Badge<Painting::TextPaintable>, const Gfx::IntPoint&, unsigned button); + void handle_mouseup_on_label(Badge<Painting::TextPaintable>, const Gfx::IntPoint&, unsigned button); + void handle_mousemove_on_label(Badge<Painting::TextPaintable>, const Gfx::IntPoint&, unsigned button); private: virtual bool is_label() const override { return true; } - static Label* label_for_control_node(LabelableNode&); + static Label const* label_for_control_node(LabelableNode const&); LabelableNode* labeled_control(); bool m_tracking_mouse { false }; diff --git a/Userland/Libraries/LibWeb/Layout/LabelableNode.cpp b/Userland/Libraries/LibWeb/Layout/LabelableNode.cpp new file mode 100644 index 0000000000..65a0898c57 --- /dev/null +++ b/Userland/Libraries/LibWeb/Layout/LabelableNode.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibWeb/Layout/LabelableNode.h> +#include <LibWeb/Painting/LabelablePaintable.h> + +namespace Web::Layout { + +Painting::LabelablePaintable* LabelableNode::paintable() +{ + return static_cast<Painting::LabelablePaintable*>(ReplacedBox::paintable()); +} + +Painting::LabelablePaintable const* LabelableNode::paintable() const +{ + return static_cast<Painting::LabelablePaintable const*>(ReplacedBox::paintable()); +} + +} diff --git a/Userland/Libraries/LibWeb/Layout/LabelableNode.h b/Userland/Libraries/LibWeb/Layout/LabelableNode.h index 49fa9842fc..6c579783c9 100644 --- a/Userland/Libraries/LibWeb/Layout/LabelableNode.h +++ b/Userland/Libraries/LibWeb/Layout/LabelableNode.h @@ -13,9 +13,8 @@ namespace Web::Layout { class LabelableNode : public ReplacedBox { public: - virtual void handle_associated_label_mousedown(Badge<Label>) { } - virtual void handle_associated_label_mouseup(Badge<Label>) { } - virtual void handle_associated_label_mousemove(Badge<Label>, [[maybe_unused]] bool is_inside_node_or_label) { } + Painting::LabelablePaintable* paintable(); + Painting::LabelablePaintable const* paintable() const; protected: LabelableNode(DOM::Document& document, DOM::Element& element, NonnullRefPtr<CSS::StyleProperties> style) diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index 0f1e519908..ec0ec6a724 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -514,32 +514,6 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& specified_style) } } -void Node::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned) -{ -} - -void Node::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned) -{ -} - -void Node::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned) -{ -} - -bool Node::handle_mousewheel(Badge<EventHandler>, const Gfx::IntPoint&, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y) -{ - if (auto* containing_block = this->containing_block()) { - if (!containing_block->is_scrollable()) - return false; - auto new_offset = containing_block->scroll_offset(); - new_offset.translate_by(wheel_delta_x, wheel_delta_y); - containing_block->set_scroll_offset(new_offset); - return true; - } - - return false; -} - bool Node::is_root_element() const { if (is_anonymous()) diff --git a/Userland/Libraries/LibWeb/Layout/Node.h b/Userland/Libraries/LibWeb/Layout/Node.h index a5d535b203..e25fe1e0d1 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.h +++ b/Userland/Libraries/LibWeb/Layout/Node.h @@ -83,13 +83,6 @@ public: bool is_inline_block() const; - virtual bool wants_mouse_events() const { return false; } - - virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers); - virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers); - virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers); - virtual bool handle_mousewheel(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y); - virtual void paint_fragment(PaintContext&, const LineBoxFragment&, Painting::PaintPhase) const { } // These are used to optimize hot is<T> variants for some classes where dynamic_cast is too slow. diff --git a/Userland/Libraries/LibWeb/Layout/RadioButton.cpp b/Userland/Libraries/LibWeb/Layout/RadioButton.cpp index ffb2965197..790976af76 100644 --- a/Userland/Libraries/LibWeb/Layout/RadioButton.cpp +++ b/Userland/Libraries/LibWeb/Layout/RadioButton.cpp @@ -27,93 +27,6 @@ RadioButton::~RadioButton() { } -void RadioButton::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned) -{ - if (button != GUI::MouseButton::Primary || !dom_node().enabled()) - return; - - m_being_pressed = true; - set_needs_display(); - - m_tracking_mouse = true; - browsing_context().event_handler().set_mouse_event_tracking_layout_node(this); -} - -void RadioButton::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) -{ - if (!m_tracking_mouse || button != GUI::MouseButton::Primary || !dom_node().enabled()) - return; - - // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. - NonnullRefPtr protect = *this; - - bool is_inside_node_or_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position); - if (!is_inside_node_or_label) - is_inside_node_or_label = Label::is_inside_associated_label(*this, position); - - if (is_inside_node_or_label) - set_checked_within_group(); - - m_being_pressed = false; - m_tracking_mouse = false; - browsing_context().event_handler().set_mouse_event_tracking_layout_node(nullptr); -} - -void RadioButton::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned) -{ - if (!m_tracking_mouse || !dom_node().enabled()) - return; - - bool is_inside_node_or_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position); - if (!is_inside_node_or_label) - is_inside_node_or_label = Label::is_inside_associated_label(*this, position); - - if (m_being_pressed == is_inside_node_or_label) - return; - - m_being_pressed = is_inside_node_or_label; - set_needs_display(); -} - -void RadioButton::handle_associated_label_mousedown(Badge<Label>) -{ - m_being_pressed = true; - set_needs_display(); -} - -void RadioButton::handle_associated_label_mouseup(Badge<Label>) -{ - // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. - NonnullRefPtr protect = *this; - - set_checked_within_group(); - m_being_pressed = false; -} - -void RadioButton::handle_associated_label_mousemove(Badge<Label>, bool is_inside_node_or_label) -{ - if (m_being_pressed == is_inside_node_or_label) - return; - - m_being_pressed = is_inside_node_or_label; - set_needs_display(); -} - -void RadioButton::set_checked_within_group() -{ - if (dom_node().checked()) - return; - - dom_node().set_checked(true, HTML::HTMLInputElement::ChangeSource::User); - String name = dom_node().name(); - - document().for_each_in_inclusive_subtree_of_type<HTML::HTMLInputElement>([&](auto& element) { - if (element.checked() && (element.layout_node() != this) && (element.name() == name)) - element.set_checked(false, HTML::HTMLInputElement::ChangeSource::User); - return IterationDecision::Continue; - }); -} - RefPtr<Painting::Paintable> RadioButton::create_paintable() const { return Painting::RadioButtonPaintable::create(*this); diff --git a/Userland/Libraries/LibWeb/Layout/RadioButton.h b/Userland/Libraries/LibWeb/Layout/RadioButton.h index 6c7abdd544..ab2e05708c 100644 --- a/Userland/Libraries/LibWeb/Layout/RadioButton.h +++ b/Userland/Libraries/LibWeb/Layout/RadioButton.h @@ -19,24 +19,8 @@ public: const HTML::HTMLInputElement& dom_node() const { return static_cast<const HTML::HTMLInputElement&>(LabelableNode::dom_node()); } HTML::HTMLInputElement& dom_node() { return static_cast<HTML::HTMLInputElement&>(LabelableNode::dom_node()); } - virtual RefPtr<Painting::Paintable> create_paintable() const override; - - bool being_pressed() const { return m_being_pressed; } - private: - virtual bool wants_mouse_events() const override { return true; } - virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; - virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; - virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers) override; - - virtual void handle_associated_label_mousedown(Badge<Label>) override; - virtual void handle_associated_label_mouseup(Badge<Label>) override; - virtual void handle_associated_label_mousemove(Badge<Label>, bool is_inside_node_or_label) override; - - void set_checked_within_group(); - - bool m_being_pressed { false }; - bool m_tracking_mouse { false }; + virtual RefPtr<Painting::Paintable> create_paintable() const override; }; } diff --git a/Userland/Libraries/LibWeb/Layout/TextNode.cpp b/Userland/Libraries/LibWeb/Layout/TextNode.cpp index 57680671ff..fb242f6212 100644 --- a/Userland/Libraries/LibWeb/Layout/TextNode.cpp +++ b/Userland/Libraries/LibWeb/Layout/TextNode.cpp @@ -241,41 +241,6 @@ void TextNode::compute_text_for_rendering(bool collapse, bool previous_is_empty_ m_text_for_rendering = builder.to_string(); } -bool TextNode::wants_mouse_events() const -{ - return first_ancestor_of_type<Label>(); -} - -void TextNode::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) -{ - auto* label = first_ancestor_of_type<Label>(); - if (!label) - return; - label->handle_mousedown_on_label({}, position, button); - browsing_context().event_handler().set_mouse_event_tracking_layout_node(this); -} - -void TextNode::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) -{ - auto* label = first_ancestor_of_type<Label>(); - if (!label) - return; - - // NOTE: Changing the state of the DOM node may run arbitrary JS, which could disappear this node. - NonnullRefPtr protect = *this; - - label->handle_mouseup_on_label({}, position, button); - browsing_context().event_handler().set_mouse_event_tracking_layout_node(nullptr); -} - -void TextNode::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) -{ - auto* label = first_ancestor_of_type<Label>(); - if (!label) - return; - label->handle_mousemove_on_label({}, position, button); -} - TextNode::ChunkIterator::ChunkIterator(StringView text, LayoutMode layout_mode, bool wrap_lines, bool respect_linebreaks) : m_layout_mode(layout_mode) , m_wrap_lines(wrap_lines) diff --git a/Userland/Libraries/LibWeb/Layout/TextNode.h b/Userland/Libraries/LibWeb/Layout/TextNode.h index e3ded59569..b84b85d27b 100644 --- a/Userland/Libraries/LibWeb/Layout/TextNode.h +++ b/Userland/Libraries/LibWeb/Layout/TextNode.h @@ -56,10 +56,6 @@ public: private: virtual bool is_text_node() const final { return true; } - virtual bool wants_mouse_events() const override; - virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; - virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; - virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; void paint_cursor_if_needed(PaintContext&, const LineBoxFragment&) const; void paint_text_decoration(Gfx::Painter&, LineBoxFragment const&) const; diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp index f16e99934c..39bb1e4c4b 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp @@ -16,6 +16,7 @@ #include <LibWeb/Layout/InitialContainingBlock.h> #include <LibWeb/Page/EventHandler.h> #include <LibWeb/Page/Page.h> +#include <LibWeb/Painting/Paintable.h> #include <LibWeb/UIEvents/EventNames.h> #include <LibWeb/UIEvents/KeyboardEvent.h> #include <LibWeb/UIEvents/MouseEvent.h> @@ -123,9 +124,10 @@ bool EventHandler::handle_mousewheel(const Gfx::IntPoint& position, unsigned int // FIXME: Support wheel events in nested browsing contexts. auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact); - if (result.layout_node) { - if (result.layout_node->handle_mousewheel({}, position, buttons, modifiers, wheel_delta_x, wheel_delta_y)) - return true; + if (result.layout_node + && result.layout_node->paintable() + && result.layout_node->paintable()->handle_mousewheel({}, position, buttons, modifiers, wheel_delta_x, wheel_delta_y)) { + return true; } if (auto* page = m_browsing_context.page()) { @@ -142,7 +144,7 @@ bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button return false; if (m_mouse_event_tracking_layout_node) { - m_mouse_event_tracking_layout_node->handle_mouseup({}, position, button, modifiers); + m_mouse_event_tracking_layout_node->paintable()->handle_mouseup({}, position, button, modifiers); return true; } @@ -150,8 +152,8 @@ bool EventHandler::handle_mouseup(const Gfx::IntPoint& position, unsigned button auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact); - if (result.layout_node && result.layout_node->wants_mouse_events()) { - result.layout_node->handle_mouseup({}, position, button, modifiers); + if (result.layout_node && result.layout_node->paintable() && result.layout_node->paintable()->wants_mouse_events()) { + result.layout_node->paintable()->handle_mouseup({}, position, button, modifiers); // Things may have changed as a consequence of Layout::Node::handle_mouseup(). Hit test again. if (!layout_root()) @@ -186,7 +188,7 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt return false; if (m_mouse_event_tracking_layout_node) { - m_mouse_event_tracking_layout_node->handle_mousedown({}, position, button, modifiers); + m_mouse_event_tracking_layout_node->paintable()->handle_mousedown({}, position, button, modifiers); return true; } @@ -207,8 +209,8 @@ bool EventHandler::handle_mousedown(const Gfx::IntPoint& position, unsigned butt node = result.layout_node->dom_node(); document->set_hovered_node(node); - if (result.layout_node->wants_mouse_events()) { - result.layout_node->handle_mousedown({}, position, button, modifiers); + if (result.layout_node->paintable() && result.layout_node->paintable()->wants_mouse_events()) { + result.layout_node->paintable()->handle_mousedown({}, position, button, modifiers); return true; } @@ -304,7 +306,7 @@ bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned butt return false; if (m_mouse_event_tracking_layout_node) { - m_mouse_event_tracking_layout_node->handle_mousemove({}, position, buttons, modifiers); + m_mouse_event_tracking_layout_node->paintable()->handle_mousemove({}, position, buttons, modifiers); return true; } @@ -316,10 +318,9 @@ bool EventHandler::handle_mousemove(const Gfx::IntPoint& position, unsigned butt auto result = layout_root()->hit_test(position, Layout::HitTestType::Exact); const HTML::HTMLAnchorElement* hovered_link_element = nullptr; if (result.layout_node) { - - if (result.layout_node->wants_mouse_events()) { + if (result.layout_node->paintable() && result.layout_node->paintable()->wants_mouse_events()) { document.set_hovered_node(result.layout_node->dom_node()); - result.layout_node->handle_mousemove({}, position, buttons, modifiers); + result.layout_node->paintable()->handle_mousemove({}, position, buttons, modifiers); // FIXME: It feels a bit aggressive to always update the cursor like this. if (auto* page = m_browsing_context.page()) page->client().page_did_request_cursor_change(Gfx::StandardCursor::None); diff --git a/Userland/Libraries/LibWeb/Painting/ButtonPaintable.cpp b/Userland/Libraries/LibWeb/Painting/ButtonPaintable.cpp index 87c343ca83..dd406293bb 100644 --- a/Userland/Libraries/LibWeb/Painting/ButtonPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/ButtonPaintable.cpp @@ -4,8 +4,11 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include <LibGUI/Event.h> +#include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/HTML/HTMLImageElement.h> #include <LibWeb/Layout/ButtonBox.h> +#include <LibWeb/Layout/Label.h> #include <LibWeb/Painting/ButtonPaintable.h> namespace Web::Painting { @@ -16,7 +19,7 @@ NonnullRefPtr<ButtonPaintable> ButtonPaintable::create(Layout::ButtonBox const& } ButtonPaintable::ButtonPaintable(Layout::ButtonBox const& layout_box) - : PaintableBox(layout_box) + : LabelablePaintable(layout_box) { } @@ -25,6 +28,11 @@ Layout::ButtonBox const& ButtonPaintable::layout_box() const return static_cast<Layout::ButtonBox const&>(layout_node()); } +Layout::ButtonBox& ButtonPaintable::layout_box() +{ + return static_cast<Layout::ButtonBox&>(layout_node()); +} + void ButtonPaintable::paint(PaintContext& context, PaintPhase phase) const { if (!is_visible()) @@ -34,10 +42,85 @@ void ButtonPaintable::paint(PaintContext& context, PaintPhase phase) const if (phase == PaintPhase::Foreground) { auto text_rect = enclosing_int_rect(absolute_rect()); - if (layout_box().being_pressed()) + if (m_being_pressed) text_rect.translate_by(1, 1); context.painter().draw_text(text_rect, layout_box().dom_node().value(), layout_box().font(), Gfx::TextAlignment::Center, computed_values().color()); } } +void ButtonPaintable::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned) +{ + if (button != GUI::MouseButton::Primary || !layout_box().dom_node().enabled()) + return; + + m_being_pressed = true; + set_needs_display(); + + m_tracking_mouse = true; + browsing_context().event_handler().set_mouse_event_tracking_layout_node(&layout_box()); +} + +void ButtonPaintable::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) +{ + if (!m_tracking_mouse || button != GUI::MouseButton::Primary || !layout_box().dom_node().enabled()) + return; + + // NOTE: Handling the click may run arbitrary JS, which could disappear this node. + NonnullRefPtr protected_this = *this; + NonnullRefPtr protected_browsing_context = browsing_context(); + + bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + if (!is_inside_node_or_label) + is_inside_node_or_label = Layout::Label::is_inside_associated_label(layout_box(), position); + + if (is_inside_node_or_label) + const_cast<Layout::ButtonBox&>(layout_box()).dom_node().did_click_button({}); + + m_being_pressed = false; + m_tracking_mouse = false; + + protected_browsing_context->event_handler().set_mouse_event_tracking_layout_node(nullptr); +} + +void ButtonPaintable::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned) +{ + if (!m_tracking_mouse || !layout_box().dom_node().enabled()) + return; + + bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + if (!is_inside_node_or_label) + is_inside_node_or_label = Layout::Label::is_inside_associated_label(layout_box(), position); + + if (m_being_pressed == is_inside_node_or_label) + return; + + m_being_pressed = is_inside_node_or_label; + set_needs_display(); +} + +void ButtonPaintable::handle_associated_label_mousedown(Badge<Layout::Label>) +{ + m_being_pressed = true; + set_needs_display(); +} + +void ButtonPaintable::handle_associated_label_mouseup(Badge<Layout::Label>) +{ + // NOTE: Handling the click may run arbitrary JS, which could disappear this node. + NonnullRefPtr protected_this = *this; + NonnullRefPtr protected_browsing_context = browsing_context(); + + layout_box().dom_node().did_click_button({}); + m_being_pressed = false; +} + +void ButtonPaintable::handle_associated_label_mousemove(Badge<Layout::Label>, bool is_inside_node_or_label) +{ + if (m_being_pressed == is_inside_node_or_label) + return; + + m_being_pressed = is_inside_node_or_label; + set_needs_display(); +} + } diff --git a/Userland/Libraries/LibWeb/Painting/ButtonPaintable.h b/Userland/Libraries/LibWeb/Painting/ButtonPaintable.h index 421f82896d..ce32439406 100644 --- a/Userland/Libraries/LibWeb/Painting/ButtonPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/ButtonPaintable.h @@ -7,20 +7,33 @@ #pragma once #include <LibWeb/Layout/ButtonBox.h> -#include <LibWeb/Painting/Paintable.h> +#include <LibWeb/Painting/LabelablePaintable.h> namespace Web::Painting { -class ButtonPaintable final : public PaintableBox { +class ButtonPaintable final : public LabelablePaintable { public: static NonnullRefPtr<ButtonPaintable> create(Layout::ButtonBox const&); virtual void paint(PaintContext&, PaintPhase) const override; Layout::ButtonBox const& layout_box() const; + Layout::ButtonBox& layout_box(); + + virtual bool wants_mouse_events() const override { return true; } + virtual void handle_mousedown(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers) override; + virtual void handle_mouseup(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers) override; + virtual void handle_mousemove(Badge<EventHandler>, Gfx::IntPoint const&, unsigned buttons, unsigned modifiers) override; private: ButtonPaintable(Layout::ButtonBox const&); + + virtual void handle_associated_label_mousedown(Badge<Layout::Label>) override; + virtual void handle_associated_label_mouseup(Badge<Layout::Label>) override; + virtual void handle_associated_label_mousemove(Badge<Layout::Label>, bool is_inside_node_or_label) override; + + bool m_being_pressed { false }; + bool m_tracking_mouse { false }; }; } diff --git a/Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.cpp b/Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.cpp index fdfacfffd3..1d42e64be8 100644 --- a/Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.cpp @@ -4,9 +4,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include <LibGUI/Event.h> #include <LibGfx/StylePainter.h> +#include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/HTML/HTMLImageElement.h> #include <LibWeb/Layout/CheckBox.h> +#include <LibWeb/Layout/Label.h> #include <LibWeb/Painting/CheckBoxPaintable.h> namespace Web::Painting { @@ -17,7 +20,7 @@ NonnullRefPtr<CheckBoxPaintable> CheckBoxPaintable::create(Layout::CheckBox cons } CheckBoxPaintable::CheckBoxPaintable(Layout::CheckBox const& layout_box) - : PaintableBox(layout_box) + : LabelablePaintable(layout_box) { } @@ -26,6 +29,11 @@ Layout::CheckBox const& CheckBoxPaintable::layout_box() const return static_cast<Layout::CheckBox const&>(layout_node()); } +Layout::CheckBox& CheckBoxPaintable::layout_box() +{ + return static_cast<Layout::CheckBox&>(layout_node()); +} + void CheckBoxPaintable::paint(PaintContext& context, PaintPhase phase) const { if (!is_visible()) @@ -34,7 +42,88 @@ void CheckBoxPaintable::paint(PaintContext& context, PaintPhase phase) const PaintableBox::paint(context, phase); if (phase == PaintPhase::Foreground) - Gfx::StylePainter::paint_check_box(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), layout_box().dom_node().enabled(), layout_box().dom_node().checked(), layout_box().being_pressed()); + Gfx::StylePainter::paint_check_box(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), layout_box().dom_node().enabled(), layout_box().dom_node().checked(), m_being_pressed); +} + +void CheckBoxPaintable::handle_mousedown(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned) +{ + if (button != GUI::MouseButton::Primary || !layout_box().dom_node().enabled()) + return; + + m_being_pressed = true; + set_needs_display(); + + m_tracking_mouse = true; + browsing_context().event_handler().set_mouse_event_tracking_layout_node(&layout_box()); +} + +void CheckBoxPaintable::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) +{ + if (!m_tracking_mouse || button != GUI::MouseButton::Primary || !layout_box().dom_node().enabled()) + return; + + // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. + NonnullRefPtr protect = *this; + + bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + if (!is_inside_node_or_label) + is_inside_node_or_label = Layout::Label::is_inside_associated_label(layout_box(), position); + + if (is_inside_node_or_label) { + layout_box().dom_node().did_click_checkbox({}); + layout_box().dom_node().set_checked(!layout_box().dom_node().checked(), HTML::HTMLInputElement::ChangeSource::User); + } + + m_being_pressed = false; + m_tracking_mouse = false; + browsing_context().event_handler().set_mouse_event_tracking_layout_node(nullptr); +} + +void CheckBoxPaintable::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned) +{ + if (!m_tracking_mouse || !layout_box().dom_node().enabled()) + return; + + bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + if (!is_inside_node_or_label) + is_inside_node_or_label = Layout::Label::is_inside_associated_label(layout_box(), position); + + if (m_being_pressed == is_inside_node_or_label) + return; + + m_being_pressed = is_inside_node_or_label; + set_needs_display(); +} + +void CheckBoxPaintable::handle_associated_label_mousedown(Badge<Layout::Label>) +{ + if (!layout_box().dom_node().enabled()) + return; + + m_being_pressed = true; + set_needs_display(); +} + +void CheckBoxPaintable::handle_associated_label_mouseup(Badge<Layout::Label>) +{ + if (!layout_box().dom_node().enabled()) + return; + + // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. + NonnullRefPtr protect = *this; + + layout_box().dom_node().did_click_checkbox({}); + layout_box().dom_node().set_checked(!layout_box().dom_node().checked(), HTML::HTMLInputElement::ChangeSource::User); + m_being_pressed = false; +} + +void CheckBoxPaintable::handle_associated_label_mousemove(Badge<Layout::Label>, bool is_inside_node_or_label) +{ + if (m_being_pressed == is_inside_node_or_label || !layout_box().dom_node().enabled()) + return; + + m_being_pressed = is_inside_node_or_label; + set_needs_display(); } } diff --git a/Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.h b/Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.h index f12b514f1b..46fe7b8933 100644 --- a/Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.h @@ -7,20 +7,33 @@ #pragma once #include <LibWeb/Layout/CheckBox.h> -#include <LibWeb/Painting/Paintable.h> +#include <LibWeb/Painting/LabelablePaintable.h> namespace Web::Painting { -class CheckBoxPaintable final : public PaintableBox { +class CheckBoxPaintable final : public LabelablePaintable { public: static NonnullRefPtr<CheckBoxPaintable> create(Layout::CheckBox const&); virtual void paint(PaintContext&, PaintPhase) const override; Layout::CheckBox const& layout_box() const; + Layout::CheckBox& layout_box(); + + virtual bool wants_mouse_events() const override { return true; } + virtual void handle_mousedown(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers) override; + virtual void handle_mouseup(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers) override; + virtual void handle_mousemove(Badge<EventHandler>, Gfx::IntPoint const&, unsigned buttons, unsigned modifiers) override; private: CheckBoxPaintable(Layout::CheckBox const&); + + virtual void handle_associated_label_mousedown(Badge<Layout::Label>) override; + virtual void handle_associated_label_mouseup(Badge<Layout::Label>) override; + virtual void handle_associated_label_mousemove(Badge<Layout::Label>, bool is_inside_node_or_label) override; + + bool m_being_pressed { false }; + bool m_tracking_mouse { false }; }; } diff --git a/Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp b/Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp new file mode 100644 index 0000000000..4c0b0527f6 --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibWeb/Painting/LabelablePaintable.h> + +namespace Web::Painting { + +Layout::LabelableNode const& LabelablePaintable::layout_box() const +{ + return static_cast<Layout::LabelableNode const&>(PaintableBox::layout_box()); +} + +Layout::LabelableNode& LabelablePaintable::layout_box() +{ + return static_cast<Layout::LabelableNode&>(PaintableBox::layout_box()); +} + +LabelablePaintable::LabelablePaintable(Layout::LabelableNode const& layout_node) + : PaintableBox(layout_node) +{ +} + +} diff --git a/Userland/Libraries/LibWeb/Painting/LabelablePaintable.h b/Userland/Libraries/LibWeb/Painting/LabelablePaintable.h new file mode 100644 index 0000000000..33538cad54 --- /dev/null +++ b/Userland/Libraries/LibWeb/Painting/LabelablePaintable.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022, Andreas Kling <kling@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibWeb/Layout/LabelableNode.h> +#include <LibWeb/Painting/Paintable.h> + +namespace Web::Painting { + +class LabelablePaintable : public PaintableBox { +public: + Layout::LabelableNode const& layout_box() const; + Layout::LabelableNode& layout_box(); + + virtual void handle_associated_label_mousedown(Badge<Layout::Label>) { } + virtual void handle_associated_label_mouseup(Badge<Layout::Label>) { } + virtual void handle_associated_label_mousemove(Badge<Layout::Label>, [[maybe_unused]] bool is_inside_node_or_label) { } + +protected: + LabelablePaintable(Layout::LabelableNode const&); +}; + +} diff --git a/Userland/Libraries/LibWeb/Painting/Paintable.cpp b/Userland/Libraries/LibWeb/Painting/Paintable.cpp index 2e9eda8f9c..8747e80a16 100644 --- a/Userland/Libraries/LibWeb/Painting/Paintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/Paintable.cpp @@ -287,4 +287,52 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const } } +void Paintable::handle_mousedown(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned) +{ +} + +void Paintable::handle_mouseup(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned) +{ +} + +void Paintable::handle_mousemove(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned) +{ +} + +bool Paintable::handle_mousewheel(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y) +{ + if (auto* containing_block = layout_node().containing_block()) { + if (!containing_block->is_scrollable()) + return false; + auto new_offset = containing_block->scroll_offset(); + new_offset.translate_by(wheel_delta_x, wheel_delta_y); + // FIXME: This const_cast is gross. + // FIXME: Scroll offset shouldn't live in the layout tree. + const_cast<Layout::BlockContainer*>(containing_block)->set_scroll_offset(new_offset); + return true; + } + + return false; +} + +bool PaintableWithLines::handle_mousewheel(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y) +{ + if (!layout_box().is_scrollable()) + return false; + auto new_offset = layout_box().scroll_offset(); + new_offset.translate_by(wheel_delta_x, wheel_delta_y); + const_cast<Layout::BlockContainer&>(layout_box()).set_scroll_offset(new_offset); + return true; +} + +Layout::BlockContainer const& PaintableWithLines::layout_box() const +{ + return static_cast<Layout::BlockContainer const&>(PaintableBox::layout_box()); +} + +Layout::BlockContainer& PaintableWithLines::layout_box() +{ + return static_cast<Layout::BlockContainer&>(PaintableBox::layout_box()); +} + } diff --git a/Userland/Libraries/LibWeb/Painting/Paintable.h b/Userland/Libraries/LibWeb/Painting/Paintable.h index c632bd2601..364dd9fc7f 100644 --- a/Userland/Libraries/LibWeb/Painting/Paintable.h +++ b/Userland/Libraries/LibWeb/Painting/Paintable.h @@ -25,9 +25,22 @@ public: virtual void before_children_paint(PaintContext&, PaintPhase) const { } virtual void after_children_paint(PaintContext&, PaintPhase) const { } + virtual bool wants_mouse_events() const { return false; } + virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers); + virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers); + virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers); + virtual bool handle_mousewheel(Badge<EventHandler>, const Gfx::IntPoint&, 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); } + auto const& computed_values() const { return m_layout_node.computed_values(); } + 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(); } + protected: explicit Paintable(Layout::Node const& layout_node) : m_layout_node(layout_node) @@ -47,6 +60,7 @@ public: bool is_visible() const { return layout_box().is_visible(); } + Layout::Box& layout_box() { return static_cast<Layout::Box&>(Paintable::layout_node()); } Layout::Box const& layout_box() const { return static_cast<Layout::Box const&>(Paintable::layout_node()); } auto const& box_model() const { return layout_box().box_model(); } @@ -134,7 +148,10 @@ public: StackingContext* enclosing_stacking_context(); DOM::Node const* dom_node() const { return layout_box().dom_node(); } + DOM::Node* dom_node() { return layout_box().dom_node(); } + DOM::Document const& document() const { return layout_box().document(); } + DOM::Document& document() { return layout_box().document(); } virtual void before_children_paint(PaintContext&, PaintPhase) const override; virtual void after_children_paint(PaintContext&, PaintPhase) const override; @@ -160,6 +177,9 @@ public: } virtual ~PaintableWithLines() override; + Layout::BlockContainer const& layout_box() const; + Layout::BlockContainer& layout_box(); + Vector<Layout::LineBox> const& line_boxes() const { return m_line_boxes; } void set_line_boxes(Vector<Layout::LineBox>&& line_boxes) { m_line_boxes = move(line_boxes); } @@ -175,6 +195,8 @@ public: } virtual void paint(PaintContext&, PaintPhase) const override; + virtual bool wants_mouse_events() const override { return false; } + virtual bool handle_mousewheel(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers, int wheel_delta_x, int wheel_delta_y) override; private: PaintableWithLines(Layout::BlockContainer const&); diff --git a/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp b/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp index 4e424e7a60..ff46cc2c88 100644 --- a/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp @@ -4,8 +4,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include <LibGUI/Event.h> #include <LibGfx/StylePainter.h> +#include <LibWeb/DOM/Document.h> +#include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/HTML/HTMLImageElement.h> +#include <LibWeb/Layout/Label.h> #include <LibWeb/Layout/RadioButton.h> #include <LibWeb/Painting/RadioButtonPaintable.h> @@ -17,7 +21,7 @@ NonnullRefPtr<RadioButtonPaintable> RadioButtonPaintable::create(Layout::RadioBu } RadioButtonPaintable::RadioButtonPaintable(Layout::RadioButton const& layout_box) - : PaintableBox(layout_box) + : LabelablePaintable(layout_box) { } @@ -26,6 +30,11 @@ Layout::RadioButton const& RadioButtonPaintable::layout_box() const return static_cast<Layout::RadioButton const&>(layout_node()); } +Layout::RadioButton& RadioButtonPaintable::layout_box() +{ + return static_cast<Layout::RadioButton&>(layout_node()); +} + void RadioButtonPaintable::paint(PaintContext& context, PaintPhase phase) const { if (!is_visible()) @@ -34,7 +43,94 @@ void RadioButtonPaintable::paint(PaintContext& context, PaintPhase phase) const PaintableBox::paint(context, phase); if (phase == PaintPhase::Foreground) - Gfx::StylePainter::paint_radio_button(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), layout_box().dom_node().checked(), layout_box().being_pressed()); + Gfx::StylePainter::paint_radio_button(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), layout_box().dom_node().checked(), m_being_pressed); +} + +void RadioButtonPaintable::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned) +{ + if (button != GUI::MouseButton::Primary || !layout_box().dom_node().enabled()) + return; + + m_being_pressed = true; + set_needs_display(); + + m_tracking_mouse = true; + browsing_context().event_handler().set_mouse_event_tracking_layout_node(&layout_box()); +} + +void RadioButtonPaintable::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) +{ + if (!m_tracking_mouse || button != GUI::MouseButton::Primary || !layout_box().dom_node().enabled()) + return; + + // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. + NonnullRefPtr protect = *this; + + bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + if (!is_inside_node_or_label) + is_inside_node_or_label = Layout::Label::is_inside_associated_label(layout_box(), position); + + if (is_inside_node_or_label) + set_checked_within_group(); + + m_being_pressed = false; + m_tracking_mouse = false; + browsing_context().event_handler().set_mouse_event_tracking_layout_node(nullptr); +} + +void RadioButtonPaintable::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned) +{ + if (!m_tracking_mouse || !layout_box().dom_node().enabled()) + return; + + bool is_inside_node_or_label = enclosing_int_rect(absolute_rect()).contains(position); + if (!is_inside_node_or_label) + is_inside_node_or_label = Layout::Label::is_inside_associated_label(layout_box(), position); + + if (m_being_pressed == is_inside_node_or_label) + return; + + m_being_pressed = is_inside_node_or_label; + set_needs_display(); +} + +void RadioButtonPaintable::handle_associated_label_mousedown(Badge<Layout::Label>) +{ + m_being_pressed = true; + set_needs_display(); +} + +void RadioButtonPaintable::handle_associated_label_mouseup(Badge<Layout::Label>) +{ + // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node. + NonnullRefPtr protect = *this; + + set_checked_within_group(); + m_being_pressed = false; +} + +void RadioButtonPaintable::handle_associated_label_mousemove(Badge<Layout::Label>, bool is_inside_node_or_label) +{ + if (m_being_pressed == is_inside_node_or_label) + return; + + m_being_pressed = is_inside_node_or_label; + set_needs_display(); +} + +void RadioButtonPaintable::set_checked_within_group() +{ + if (layout_box().dom_node().checked()) + return; + + layout_box().dom_node().set_checked(true, HTML::HTMLInputElement::ChangeSource::User); + String name = layout_box().dom_node().name(); + + document().for_each_in_inclusive_subtree_of_type<HTML::HTMLInputElement>([&](auto& element) { + if (element.checked() && (element.paintable() != this) && (element.name() == name)) + element.set_checked(false, HTML::HTMLInputElement::ChangeSource::User); + return IterationDecision::Continue; + }); } } diff --git a/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.h b/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.h index 274fb9069f..be4b95e13d 100644 --- a/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.h @@ -7,20 +7,35 @@ #pragma once #include <LibWeb/Layout/RadioButton.h> -#include <LibWeb/Painting/Paintable.h> +#include <LibWeb/Painting/LabelablePaintable.h> namespace Web::Painting { -class RadioButtonPaintable final : public PaintableBox { +class RadioButtonPaintable final : public LabelablePaintable { public: static NonnullRefPtr<RadioButtonPaintable> create(Layout::RadioButton const&); virtual void paint(PaintContext&, PaintPhase) const override; Layout::RadioButton const& layout_box() const; + Layout::RadioButton& layout_box(); + + virtual bool wants_mouse_events() const override { return true; } + virtual void handle_mousedown(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers) override; + virtual void handle_mouseup(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers) override; + virtual void handle_mousemove(Badge<EventHandler>, Gfx::IntPoint const&, unsigned buttons, unsigned modifiers) override; private: RadioButtonPaintable(Layout::RadioButton const&); + + virtual void handle_associated_label_mousedown(Badge<Layout::Label>) override; + virtual void handle_associated_label_mouseup(Badge<Layout::Label>) override; + virtual void handle_associated_label_mousemove(Badge<Layout::Label>, bool is_inside_node_or_label) override; + + void set_checked_within_group(); + + bool m_being_pressed { false }; + bool m_tracking_mouse { false }; }; } diff --git a/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp b/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp index 00bf0f9978..a1ca3dcc78 100644 --- a/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/TextPaintable.cpp @@ -21,4 +21,39 @@ TextPaintable::TextPaintable(Layout::TextNode const& layout_node) { } +bool TextPaintable::wants_mouse_events() const +{ + return layout_node().first_ancestor_of_type<Layout::Label>(); +} + +void TextPaintable::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) +{ + auto* label = layout_node().first_ancestor_of_type<Layout::Label>(); + if (!label) + return; + const_cast<Layout::Label*>(label)->handle_mousedown_on_label({}, position, button); + const_cast<HTML::BrowsingContext&>(browsing_context()).event_handler().set_mouse_event_tracking_layout_node(&const_cast<Layout::TextNode&>(layout_node())); +} + +void TextPaintable::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) +{ + auto* label = layout_node().first_ancestor_of_type<Layout::Label>(); + if (!label) + return; + + // NOTE: Changing the state of the DOM node may run arbitrary JS, which could disappear this node. + NonnullRefPtr protect = *this; + + const_cast<Layout::Label*>(label)->handle_mouseup_on_label({}, position, button); + const_cast<HTML::BrowsingContext&>(browsing_context()).event_handler().set_mouse_event_tracking_layout_node(nullptr); +} + +void TextPaintable::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned) +{ + auto* label = layout_node().first_ancestor_of_type<Layout::Label>(); + if (!label) + return; + const_cast<Layout::Label*>(label)->handle_mousemove_on_label({}, position, button); +} + } diff --git a/Userland/Libraries/LibWeb/Painting/TextPaintable.h b/Userland/Libraries/LibWeb/Painting/TextPaintable.h index 3ef3deff31..172bdf4754 100644 --- a/Userland/Libraries/LibWeb/Painting/TextPaintable.h +++ b/Userland/Libraries/LibWeb/Painting/TextPaintable.h @@ -16,6 +16,11 @@ public: Layout::TextNode const& layout_node() const { return static_cast<Layout::TextNode const&>(Paintable::layout_node()); } + virtual bool wants_mouse_events() const override; + virtual void handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; + virtual void handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; + virtual void handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override; + private: explicit TextPaintable(Layout::TextNode const&); }; |