From 41e425ced33c8ada3849a9b3a411616450719b87 Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Tue, 4 Jun 2024 00:29:32 +0300 Subject: [PATCH] LibWeb: Implement scrollbars dragging (cherry picked from commit 881e97084625184543b93cee235cb0b96ee055ae) --- .../LibWeb/Painting/PaintableBox.cpp | 88 +++++++++++++++++++ .../Libraries/LibWeb/Painting/PaintableBox.h | 18 +++- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index b8d203e35a01de..6e9aec4003c938 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -213,6 +214,15 @@ Optional PaintableBox::get_clip_rect() const return {}; } +bool PaintableBox::wants_mouse_events() const +{ + if (scroll_thumb_rect(ScrollDirection::Vertical).has_value()) + return true; + if (scroll_thumb_rect(ScrollDirection::Horizontal).has_value()) + return true; + return false; +} + void PaintableBox::before_paint(PaintContext& context, [[maybe_unused]] PaintPhase phase) const { if (!is_visible()) @@ -745,6 +755,67 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const } } +Paintable::DispatchEventOfSameName PaintableBox::handle_mousedown(Badge, CSSPixelPoint position, unsigned, unsigned) +{ + auto vertical_scroll_thumb_rect = scroll_thumb_rect(ScrollDirection::Vertical); + auto horizontal_scroll_thumb_rect = scroll_thumb_rect(ScrollDirection::Horizontal); + if (vertical_scroll_thumb_rect.has_value() && vertical_scroll_thumb_rect.value().contains(position)) { + if (is_viewport()) + position.translate_by(-scroll_offset()); + m_last_mouse_tracking_position = position; + m_scroll_thumb_dragging_direction = ScrollDirection::Vertical; + const_cast(*navigable()).event_handler().set_mouse_event_tracking_paintable(this); + } else if (horizontal_scroll_thumb_rect.has_value() && horizontal_scroll_thumb_rect.value().contains(position)) { + if (is_viewport()) + position.translate_by(-scroll_offset()); + m_last_mouse_tracking_position = position; + m_scroll_thumb_dragging_direction = ScrollDirection::Horizontal; + const_cast(*navigable()).event_handler().set_mouse_event_tracking_paintable(this); + } + return Paintable::DispatchEventOfSameName::Yes; +} + +Paintable::DispatchEventOfSameName PaintableBox::handle_mouseup(Badge, CSSPixelPoint, unsigned, unsigned) +{ + if (m_last_mouse_tracking_position.has_value()) { + m_last_mouse_tracking_position.clear(); + m_scroll_thumb_dragging_direction.clear(); + const_cast(*navigable()).event_handler().set_mouse_event_tracking_paintable(nullptr); + } + return Paintable::DispatchEventOfSameName::Yes; +} + +Paintable::DispatchEventOfSameName PaintableBox::handle_mousemove(Badge, CSSPixelPoint position, unsigned, unsigned) +{ + if (m_last_mouse_tracking_position.has_value()) { + if (is_viewport()) + position.translate_by(-scroll_offset()); + + Gfx::Point scroll_delta; + if (m_scroll_thumb_dragging_direction == ScrollDirection::Horizontal) + scroll_delta.set_x((position.x() - m_last_mouse_tracking_position->x()).to_double()); + else + scroll_delta.set_y((position.y() - m_last_mouse_tracking_position->y()).to_double()); + + auto padding_rect = absolute_padding_box_rect(); + auto scrollable_overflow_rect = this->scrollable_overflow_rect().value(); + auto scroll_overflow_size = m_scroll_thumb_dragging_direction == ScrollDirection::Horizontal ? scrollable_overflow_rect.width() : scrollable_overflow_rect.height(); + auto scrollport_size = m_scroll_thumb_dragging_direction == ScrollDirection::Horizontal ? padding_rect.width() : padding_rect.height(); + auto scroll_px_per_mouse_position_delta_px = scroll_overflow_size.to_double() / scrollport_size.to_double(); + scroll_delta *= scroll_px_per_mouse_position_delta_px; + + if (is_viewport()) { + document().window()->scroll_by(scroll_delta.x(), scroll_delta.y()); + } else { + scroll_by(scroll_delta.x(), scroll_delta.y()); + } + + m_last_mouse_tracking_position = position; + return Paintable::DispatchEventOfSameName::No; + } + return Paintable::DispatchEventOfSameName::Yes; +} + bool PaintableBox::handle_mousewheel(Badge, CSSPixelPoint, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y) { if (!layout_box().is_user_scrollable()) @@ -768,6 +839,17 @@ Layout::BlockContainer& PaintableWithLines::layout_box() return static_cast(PaintableBox::layout_box()); } +TraversalDecision PaintableBox::hit_test_scrollbars(CSSPixelPoint position, Function const& callback) const +{ + auto vertical_scroll_thumb_rect = scroll_thumb_rect(ScrollDirection::Vertical); + if (vertical_scroll_thumb_rect.has_value() && vertical_scroll_thumb_rect.value().contains(position)) + return callback(HitTestResult { const_cast(*this) }); + auto horizontal_scroll_thumb_rect = scroll_thumb_rect(ScrollDirection::Horizontal); + if (horizontal_scroll_thumb_rect.has_value() && horizontal_scroll_thumb_rect.value().contains(position)) + return callback(HitTestResult { const_cast(*this) }); + return TraversalDecision::Continue; +} + TraversalDecision PaintableBox::hit_test(CSSPixelPoint position, HitTestType type, Function const& callback) const { if (clip_rect().has_value() && !clip_rect()->contains(position)) @@ -780,6 +862,9 @@ TraversalDecision PaintableBox::hit_test(CSSPixelPoint position, HitTestType typ if (!is_visible()) return TraversalDecision::Continue; + if (hit_test_scrollbars(position_adjusted_by_scroll_offset, callback) == TraversalDecision::Break) + return TraversalDecision::Break; + if (layout_box().is_viewport()) { auto& viewport_paintable = const_cast(static_cast(*this)); viewport_paintable.build_stacking_context_tree_if_needed(); @@ -832,6 +917,9 @@ TraversalDecision PaintableWithLines::hit_test(CSSPixelPoint position, HitTestTy return PaintableBox::hit_test(position, type, callback); } + if (hit_test_scrollbars(position_adjusted_by_scroll_offset, callback) == TraversalDecision::Break) + return TraversalDecision::Break; + for (auto const* child = last_child(); child; child = child->previous_sibling()) { if (child->hit_test(position, type, callback) == TraversalDecision::Break) return TraversalDecision::Break; diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h index 5c4816054fe10f..6be7a260dce29d 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -205,6 +205,8 @@ class PaintableBox : public Paintable bool is_viewport() const { return layout_box().is_viewport(); } + virtual bool wants_mouse_events() const override; + protected: explicit PaintableBox(Layout::Box const&); @@ -216,9 +218,6 @@ class PaintableBox : public Paintable virtual CSSPixelRect compute_absolute_rect() const; virtual CSSPixelRect compute_absolute_paint_rect() const; -private: - [[nodiscard]] virtual bool is_paintable_box() const final { return true; } - enum class ScrollDirection { Horizontal, Vertical, @@ -226,6 +225,15 @@ class PaintableBox : public Paintable [[nodiscard]] Optional scroll_thumb_rect(ScrollDirection) const; [[nodiscard]] bool is_scrollable(ScrollDirection) const; + TraversalDecision hit_test_scrollbars(CSSPixelPoint position, Function const& callback) const; + +private: + [[nodiscard]] virtual bool is_paintable_box() const final { return true; } + + virtual DispatchEventOfSameName handle_mousedown(Badge, CSSPixelPoint, unsigned button, unsigned modifiers) override; + virtual DispatchEventOfSameName handle_mouseup(Badge, CSSPixelPoint, unsigned button, unsigned modifiers) override; + virtual DispatchEventOfSameName handle_mousemove(Badge, CSSPixelPoint, unsigned buttons, unsigned modifiers) override; + Optional m_overflow_data; CSSPixelPoint m_offset; @@ -250,6 +258,9 @@ class PaintableBox : public Paintable Optional m_outline_data; CSSPixels m_outline_offset { 0 }; + + Optional m_last_mouse_tracking_position; + Optional m_scroll_thumb_dragging_direction; }; class PaintableWithLines : public PaintableBox { @@ -282,7 +293,6 @@ class PaintableWithLines : public PaintableBox { } virtual void paint(PaintContext&, PaintPhase) const override; - virtual bool wants_mouse_events() const override { return false; } [[nodiscard]] virtual TraversalDecision hit_test(CSSPixelPoint position, HitTestType type, Function const& callback) const override;