summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibWeb')
-rw-r--r--Userland/Libraries/LibWeb/CMakeLists.txt2
-rw-r--r--Userland/Libraries/LibWeb/DOM/Node.cpp7
-rw-r--r--Userland/Libraries/LibWeb/DOM/Node.h1
-rw-r--r--Userland/Libraries/LibWeb/Forward.h4
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp4
-rw-r--r--Userland/Libraries/LibWeb/HTML/HTMLInputElement.h4
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockContainer.cpp11
-rw-r--r--Userland/Libraries/LibWeb/Layout/BlockContainer.h2
-rw-r--r--Userland/Libraries/LibWeb/Layout/ButtonBox.cpp75
-rw-r--r--Userland/Libraries/LibWeb/Layout/ButtonBox.h16
-rw-r--r--Userland/Libraries/LibWeb/Layout/CheckBox.cpp84
-rw-r--r--Userland/Libraries/LibWeb/Layout/CheckBox.h16
-rw-r--r--Userland/Libraries/LibWeb/Layout/Label.cpp24
-rw-r--r--Userland/Libraries/LibWeb/Layout/Label.h12
-rw-r--r--Userland/Libraries/LibWeb/Layout/LabelableNode.cpp22
-rw-r--r--Userland/Libraries/LibWeb/Layout/LabelableNode.h5
-rw-r--r--Userland/Libraries/LibWeb/Layout/Node.cpp26
-rw-r--r--Userland/Libraries/LibWeb/Layout/Node.h7
-rw-r--r--Userland/Libraries/LibWeb/Layout/RadioButton.cpp87
-rw-r--r--Userland/Libraries/LibWeb/Layout/RadioButton.h18
-rw-r--r--Userland/Libraries/LibWeb/Layout/TextNode.cpp35
-rw-r--r--Userland/Libraries/LibWeb/Layout/TextNode.h4
-rw-r--r--Userland/Libraries/LibWeb/Page/EventHandler.cpp27
-rw-r--r--Userland/Libraries/LibWeb/Painting/ButtonPaintable.cpp87
-rw-r--r--Userland/Libraries/LibWeb/Painting/ButtonPaintable.h17
-rw-r--r--Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.cpp93
-rw-r--r--Userland/Libraries/LibWeb/Painting/CheckBoxPaintable.h17
-rw-r--r--Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp26
-rw-r--r--Userland/Libraries/LibWeb/Painting/LabelablePaintable.h27
-rw-r--r--Userland/Libraries/LibWeb/Painting/Paintable.cpp48
-rw-r--r--Userland/Libraries/LibWeb/Painting/Paintable.h22
-rw-r--r--Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.cpp100
-rw-r--r--Userland/Libraries/LibWeb/Painting/RadioButtonPaintable.h19
-rw-r--r--Userland/Libraries/LibWeb/Painting/TextPaintable.cpp35
-rw-r--r--Userland/Libraries/LibWeb/Painting/TextPaintable.h5
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&);
};