mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 04:57:45 +00:00
LibWeb: Move mouse event and label logic from layout to painting tree
Input events have nothing to do with layout, so let's not send them to layout nodes. The job of Paintable starts to become clear. It represents a paintable item that can be rendered into the viewport, which means it can also be targeted by the mouse cursor.
This commit is contained in:
parent
ed84fbce47
commit
cb0c5390ff
35 changed files with 560 additions and 429 deletions
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
26
Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp
Normal file
26
Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp
Normal file
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
27
Userland/Libraries/LibWeb/Painting/LabelablePaintable.h
Normal file
27
Userland/Libraries/LibWeb/Painting/LabelablePaintable.h
Normal file
|
@ -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&);
|
||||
};
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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&);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue