Skip to content

Commit

Permalink
LibWeb: Implement scrollbars dragging
Browse files Browse the repository at this point in the history
(cherry picked from commit 881e97084625184543b93cee235cb0b96ee055ae)
  • Loading branch information
kalenikaliaksandr authored and jamierocks committed Jul 6, 2024
1 parent 2095e28 commit 41e425c
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 4 deletions.
88 changes: 88 additions & 0 deletions Userland/Libraries/LibWeb/Painting/PaintableBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <LibWeb/CSS/SystemColor.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/HTMLHtmlElement.h>
#include <LibWeb/HTML/Window.h>
#include <LibWeb/Layout/BlockContainer.h>
#include <LibWeb/Layout/Viewport.h>
#include <LibWeb/Painting/BackgroundPainting.h>
Expand Down Expand Up @@ -213,6 +214,15 @@ Optional<CSSPixelRect> 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())
Expand Down Expand Up @@ -745,6 +755,67 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
}
}

Paintable::DispatchEventOfSameName PaintableBox::handle_mousedown(Badge<EventHandler>, 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<HTML::Navigable&>(*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<HTML::Navigable&>(*navigable()).event_handler().set_mouse_event_tracking_paintable(this);
}
return Paintable::DispatchEventOfSameName::Yes;
}

Paintable::DispatchEventOfSameName PaintableBox::handle_mouseup(Badge<EventHandler>, 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<HTML::Navigable&>(*navigable()).event_handler().set_mouse_event_tracking_paintable(nullptr);
}
return Paintable::DispatchEventOfSameName::Yes;
}

Paintable::DispatchEventOfSameName PaintableBox::handle_mousemove(Badge<EventHandler>, CSSPixelPoint position, unsigned, unsigned)
{
if (m_last_mouse_tracking_position.has_value()) {
if (is_viewport())
position.translate_by(-scroll_offset());

Gfx::Point<double> 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<EventHandler>, CSSPixelPoint, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y)
{
if (!layout_box().is_user_scrollable())
Expand All @@ -768,6 +839,17 @@ Layout::BlockContainer& PaintableWithLines::layout_box()
return static_cast<Layout::BlockContainer&>(PaintableBox::layout_box());
}

TraversalDecision PaintableBox::hit_test_scrollbars(CSSPixelPoint position, Function<TraversalDecision(HitTestResult)> 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<PaintableBox&>(*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<PaintableBox&>(*this) });
return TraversalDecision::Continue;
}

TraversalDecision PaintableBox::hit_test(CSSPixelPoint position, HitTestType type, Function<TraversalDecision(HitTestResult)> const& callback) const
{
if (clip_rect().has_value() && !clip_rect()->contains(position))
Expand All @@ -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<ViewportPaintable&>(static_cast<ViewportPaintable const&>(*this));
viewport_paintable.build_stacking_context_tree_if_needed();
Expand Down Expand Up @@ -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;
Expand Down
18 changes: 14 additions & 4 deletions Userland/Libraries/LibWeb/Painting/PaintableBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -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&);

Expand All @@ -216,16 +218,22 @@ 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,
};
[[nodiscard]] Optional<CSSPixelRect> scroll_thumb_rect(ScrollDirection) const;
[[nodiscard]] bool is_scrollable(ScrollDirection) const;

TraversalDecision hit_test_scrollbars(CSSPixelPoint position, Function<TraversalDecision(HitTestResult)> const& callback) const;

private:
[[nodiscard]] virtual bool is_paintable_box() const final { return true; }

virtual DispatchEventOfSameName handle_mousedown(Badge<EventHandler>, CSSPixelPoint, unsigned button, unsigned modifiers) override;
virtual DispatchEventOfSameName handle_mouseup(Badge<EventHandler>, CSSPixelPoint, unsigned button, unsigned modifiers) override;
virtual DispatchEventOfSameName handle_mousemove(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers) override;

Optional<OverflowData> m_overflow_data;

CSSPixelPoint m_offset;
Expand All @@ -250,6 +258,9 @@ class PaintableBox : public Paintable

Optional<BordersData> m_outline_data;
CSSPixels m_outline_offset { 0 };

Optional<CSSPixelPoint> m_last_mouse_tracking_position;
Optional<ScrollDirection> m_scroll_thumb_dragging_direction;
};

class PaintableWithLines : public PaintableBox {
Expand Down Expand Up @@ -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<TraversalDecision(HitTestResult)> const& callback) const override;

Expand Down

0 comments on commit 41e425c

Please sign in to comment.