1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 06:47:34 +00:00

LibWeb: Refactor all LabelableNode subclasses + input event handling :^)

This commit is messy due to the Paintable and Layout classes being
tangled together.

The RadioButton, CheckBox and ButtonBox classes are now subclasses of
FormAssociatedLabelableNode. This subclass separates these layout nodes
from LabelableNode, which is also the superclass of non-form associated
labelable nodes (Progress).

ButtonPaintable, CheckBoxPaintable and RadioButtonPaintable no longer
call events on DOM nodes directly from their mouse event handlers;
instead, all the functionality is now directly in EventHandler, which
dispatches the related events. handle_mousedown and related methods
return a bool indicating whether the event handling should proceed.

Paintable classes can now return an alternative DOM::Node which should
be the target of the mouse event. Labels use this to indicate that the
labeled control should be the target of the mouse events.

HTMLInputElement put its activation behavior on run_activation_behavior,
which wasn't actually called anywhere and had to be manually called by
other places. We now use activation_behavior which is used by
EventDispatcher.

This commit also brings HTMLInputElement closer to spec by removing the
did_foo functions that did ad-hoc event dispatching and unifies the
behavior under run_input_activation_behavior.
This commit is contained in:
sin-ack 2022-03-14 23:05:55 +00:00 committed by Andreas Kling
parent 06ccc45157
commit 29583104d2
26 changed files with 409 additions and 466 deletions

View file

@ -40,87 +40,13 @@ void ButtonPaintable::paint(PaintContext& context, PaintPhase phase) const
PaintableBox::paint(context, phase);
if (phase == PaintPhase::Foreground) {
auto const& dom_node = layout_box().dom_node();
if (is<HTML::HTMLInputElement>(dom_node) && phase == PaintPhase::Foreground) {
auto text_rect = enclosing_int_rect(absolute_rect());
if (m_being_pressed)
if (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());
context.painter().draw_text(text_rect, static_cast<HTML::HTMLInputElement const&>(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();
}
}

View file

@ -20,20 +20,8 @@ public:
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 };
};
}

View file

@ -41,89 +41,9 @@ void CheckBoxPaintable::paint(PaintContext& context, PaintPhase phase) const
PaintableBox::paint(context, phase);
auto const& checkbox = static_cast<HTML::HTMLInputElement const&>(layout_box().dom_node());
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(), 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();
Gfx::StylePainter::paint_check_box(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), layout_box().dom_node().enabled(), checkbox.checked(), being_pressed());
}
}

View file

@ -20,20 +20,8 @@ public:
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 };
};
}

View file

@ -1,26 +1,99 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, sin-ack <sin-ack@protonmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGUI/Event.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/Layout/Label.h>
#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)
{
}
void LabelablePaintable::set_being_pressed(bool being_pressed)
{
if (m_being_pressed == being_pressed)
return;
m_being_pressed = being_pressed;
set_needs_display();
}
Layout::FormAssociatedLabelableNode const& LabelablePaintable::layout_box() const
{
return static_cast<Layout::FormAssociatedLabelableNode const&>(PaintableBox::layout_box());
}
Layout::FormAssociatedLabelableNode& LabelablePaintable::layout_box()
{
return static_cast<Layout::FormAssociatedLabelableNode&>(PaintableBox::layout_box());
}
LabelablePaintable::DispatchEventOfSameName LabelablePaintable::handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned)
{
if (button != GUI::MouseButton::Primary || !layout_box().dom_node().enabled())
return DispatchEventOfSameName::No;
set_being_pressed(true);
m_tracking_mouse = true;
browsing_context().event_handler().set_mouse_event_tracking_layout_node(&layout_box());
return DispatchEventOfSameName::Yes;
}
LabelablePaintable::DispatchEventOfSameName LabelablePaintable::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 DispatchEventOfSameName::No;
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);
set_being_pressed(false);
m_tracking_mouse = false;
browsing_context().event_handler().set_mouse_event_tracking_layout_node(nullptr);
return DispatchEventOfSameName::Yes;
}
LabelablePaintable::DispatchEventOfSameName LabelablePaintable::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned, unsigned)
{
if (!m_tracking_mouse || !layout_box().dom_node().enabled())
return DispatchEventOfSameName::No;
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);
set_being_pressed(is_inside_node_or_label);
return DispatchEventOfSameName::Yes;
}
void LabelablePaintable::handle_associated_label_mousedown(Badge<Layout::Label>)
{
set_being_pressed(true);
}
void LabelablePaintable::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();
set_being_pressed(false);
}
void LabelablePaintable::handle_associated_label_mousemove(Badge<Layout::Label>, bool is_inside_node_or_label)
{
if (being_pressed() == is_inside_node_or_label)
return;
set_being_pressed(is_inside_node_or_label);
}
}

View file

@ -1,27 +1,44 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2022, sin-ack <sin-ack@protonmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Layout/LabelableNode.h>
#include <LibWeb/HTML/FormAssociatedElement.h>
#include <LibWeb/Layout/FormAssociatedLabelableNode.h>
#include <LibWeb/Painting/PaintableBox.h>
namespace Web::Painting {
// FIXME: Splinter this into FormAssociatedLabelablePaintable once
// ProgressPaintable switches over to this.
class LabelablePaintable : public PaintableBox {
public:
Layout::LabelableNode const& layout_box() const;
Layout::LabelableNode& layout_box();
Layout::FormAssociatedLabelableNode const& layout_box() const;
Layout::FormAssociatedLabelableNode& 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) { }
virtual bool wants_mouse_events() const override { return true; }
virtual DispatchEventOfSameName handle_mousedown(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers) override;
virtual DispatchEventOfSameName handle_mouseup(Badge<EventHandler>, Gfx::IntPoint const&, unsigned button, unsigned modifiers) override;
virtual DispatchEventOfSameName handle_mousemove(Badge<EventHandler>, Gfx::IntPoint const&, unsigned buttons, unsigned modifiers) override;
void handle_associated_label_mousedown(Badge<Layout::Label>);
void handle_associated_label_mouseup(Badge<Layout::Label>);
void handle_associated_label_mousemove(Badge<Layout::Label>, bool is_inside_node_or_label);
bool being_pressed() const { return m_being_pressed; }
// NOTE: Only the HTML node associated with this paintable should call this!
void set_being_pressed(bool being_pressed);
protected:
LabelablePaintable(Layout::LabelableNode const&);
private:
bool m_being_pressed { false };
bool m_tracking_mouse { false };
};
}

View file

@ -10,16 +10,19 @@
namespace Web::Painting {
void Paintable::handle_mousedown(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned)
Paintable::DispatchEventOfSameName Paintable::handle_mousedown(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned)
{
return DispatchEventOfSameName::Yes;
}
void Paintable::handle_mouseup(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned)
Paintable::DispatchEventOfSameName Paintable::handle_mouseup(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned)
{
return DispatchEventOfSameName::Yes;
}
void Paintable::handle_mousemove(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned)
Paintable::DispatchEventOfSameName Paintable::handle_mousemove(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned)
{
return DispatchEventOfSameName::Yes;
}
bool Paintable::handle_mousewheel(Badge<EventHandler>, Gfx::IntPoint const&, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y)

View file

@ -53,9 +53,19 @@ public:
virtual HitTestResult hit_test(Gfx::IntPoint const&, HitTestType) 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);
enum class DispatchEventOfSameName {
Yes,
No,
};
// When these methods return true, the DOM event with the same name will be
// dispatch at the mouse_event_target if it returns a valid DOM::Node, or
// the layout node's associated DOM node if it doesn't.
virtual DispatchEventOfSameName handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers);
virtual DispatchEventOfSameName handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers);
virtual DispatchEventOfSameName handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned buttons, unsigned modifiers);
virtual DOM::Node* mouse_event_target() const { return nullptr; }
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; }

View file

@ -11,6 +11,9 @@
namespace Web::Painting {
// FIXME: ProgressPaintable should inherit from LabelablePaintable, as it is a LabelableNode.
// LabelablePaintable should be split into FormAssociatedLabelablePaintable once this
// happens.
class ProgressPaintable final : public PaintableBox {
public:
static NonnullRefPtr<ProgressPaintable> create(Layout::Progress const&);

View file

@ -8,7 +8,7 @@
#include <LibGfx/StylePainter.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/HTMLImageElement.h>
#include <LibWeb/HTML/HTMLInputElement.h>
#include <LibWeb/Layout/Label.h>
#include <LibWeb/Layout/RadioButton.h>
#include <LibWeb/Painting/RadioButtonPaintable.h>
@ -25,16 +25,6 @@ RadioButtonPaintable::RadioButtonPaintable(Layout::RadioButton const& layout_box
{
}
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())
@ -42,95 +32,9 @@ void RadioButtonPaintable::paint(PaintContext& context, PaintPhase phase) const
PaintableBox::paint(context, phase);
auto const& radio_box = static_cast<HTML::HTMLInputElement const&>(layout_box().dom_node());
if (phase == PaintPhase::Foreground)
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;
});
Gfx::StylePainter::paint_radio_button(context.painter(), enclosing_int_rect(absolute_rect()), context.palette(), radio_box.checked(), being_pressed());
}
}

View file

@ -17,25 +17,8 @@ public:
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 };
};
}

View file

@ -6,6 +6,7 @@
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/Layout/Label.h>
#include <LibWeb/Layout/LabelableNode.h>
#include <LibWeb/Page/EventHandler.h>
#include <LibWeb/Painting/TextPaintable.h>
@ -26,34 +27,43 @@ 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)
DOM::Node* TextPaintable::mouse_event_target() const
{
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()));
if (auto* label = layout_node().first_ancestor_of_type<Layout::Label>()) {
if (auto* control = const_cast<Layout::Label*>(label)->labeled_control())
return &control->dom_node();
}
return nullptr;
}
void TextPaintable::handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned)
TextPaintable::DispatchEventOfSameName 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;
return DispatchEventOfSameName::No;
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()));
return DispatchEventOfSameName::Yes;
}
// NOTE: Changing the state of the DOM node may run arbitrary JS, which could disappear this node.
NonnullRefPtr protect = *this;
TextPaintable::DispatchEventOfSameName 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 DispatchEventOfSameName::No;
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);
return DispatchEventOfSameName::Yes;
}
void TextPaintable::handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint& position, unsigned button, unsigned)
TextPaintable::DispatchEventOfSameName 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;
return DispatchEventOfSameName::No;
const_cast<Layout::Label*>(label)->handle_mousemove_on_label({}, position, button);
return DispatchEventOfSameName::Yes;
}
}

View file

@ -17,9 +17,10 @@ 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;
virtual DOM::Node* mouse_event_target() const override;
virtual DispatchEventOfSameName handle_mousedown(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
virtual DispatchEventOfSameName handle_mouseup(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
virtual DispatchEventOfSameName handle_mousemove(Badge<EventHandler>, const Gfx::IntPoint&, unsigned button, unsigned modifiers) override;
private:
explicit TextPaintable(Layout::TextNode const&);