mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 12:19:54 +00:00
LibWeb: Limit scroll bar thumb movement based on grab point
We were calculating the scroll delta based on the last known mouse position, even if that position ventured far beyond the scroll bar's rect. This caused weird behavior such as scrolling when the mouse was clearly not on the scrollbar. This reworks the scrollbar logic to use the mouse's absolute position instead of a delta, and to always calculate and set the absolute scroll offset. This makes it much easier to reason about the scrollbar's position, plus we don't need the last mouse position anymore. As far as I can tell, our scroll bar behavior now closely resembles Firefox' behavior.
This commit is contained in:
parent
727b4f5e89
commit
e56146edec
Notes:
github-actions[bot]
2025-06-19 11:04:59 +00:00
Author: https://github.com/gmta
Commit: e56146edec
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5131
Reviewed-by: https://github.com/trflynn89 ✅
2 changed files with 40 additions and 35 deletions
|
@ -916,24 +916,15 @@ Paintable::DispatchEventOfSameName PaintableBox::handle_mousedown(Badge<EventHan
|
|||
position = adjust_position_for_cumulative_scroll_offset(position);
|
||||
|
||||
auto handle_scrollbar = [&](auto direction) {
|
||||
auto scrollbar_data = compute_scrollbar_data(direction, AdjustThumbRectForScrollOffset::Yes);
|
||||
auto scrollbar_data = compute_scrollbar_data(direction);
|
||||
if (!scrollbar_data.has_value())
|
||||
return false;
|
||||
|
||||
if (scrollbar_data->thumb_rect.contains(position)) {
|
||||
m_last_mouse_tracking_position = position;
|
||||
m_scroll_thumb_dragging_direction = direction;
|
||||
|
||||
navigable()->event_handler().set_mouse_event_tracking_paintable(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (scrollbar_data->gutter_rect.contains(position)) {
|
||||
m_last_mouse_tracking_position = scrollbar_data->thumb_rect.center();
|
||||
m_scroll_thumb_dragging_direction = direction;
|
||||
|
||||
navigable()->event_handler().set_mouse_event_tracking_paintable(this);
|
||||
scroll_to_mouse_postion(position);
|
||||
scroll_to_mouse_position(position);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -950,10 +941,10 @@ Paintable::DispatchEventOfSameName PaintableBox::handle_mousedown(Badge<EventHan
|
|||
|
||||
Paintable::DispatchEventOfSameName PaintableBox::handle_mouseup(Badge<EventHandler>, CSSPixelPoint, unsigned, unsigned)
|
||||
{
|
||||
if (m_last_mouse_tracking_position.has_value()) {
|
||||
m_last_mouse_tracking_position.clear();
|
||||
if (m_scroll_thumb_grab_position.has_value()) {
|
||||
m_scroll_thumb_grab_position.clear();
|
||||
m_scroll_thumb_dragging_direction.clear();
|
||||
const_cast<HTML::Navigable&>(*navigable()).event_handler().set_mouse_event_tracking_paintable(nullptr);
|
||||
navigable()->event_handler().set_mouse_event_tracking_paintable(nullptr);
|
||||
}
|
||||
return Paintable::DispatchEventOfSameName::Yes;
|
||||
}
|
||||
|
@ -962,8 +953,8 @@ Paintable::DispatchEventOfSameName PaintableBox::handle_mousemove(Badge<EventHan
|
|||
{
|
||||
position = adjust_position_for_cumulative_scroll_offset(position);
|
||||
|
||||
if (m_last_mouse_tracking_position.has_value()) {
|
||||
scroll_to_mouse_postion(position);
|
||||
if (m_scroll_thumb_grab_position.has_value()) {
|
||||
scroll_to_mouse_position(position);
|
||||
return Paintable::DispatchEventOfSameName::No;
|
||||
}
|
||||
|
||||
|
@ -995,30 +986,44 @@ bool PaintableBox::scrollbar_contains_mouse_position(ScrollDirection direction,
|
|||
return scrollbar_data->gutter_rect.contains(position);
|
||||
}
|
||||
|
||||
void PaintableBox::scroll_to_mouse_postion(CSSPixelPoint position)
|
||||
void PaintableBox::scroll_to_mouse_position(CSSPixelPoint position)
|
||||
{
|
||||
VERIFY(m_last_mouse_tracking_position.has_value());
|
||||
VERIFY(m_scroll_thumb_dragging_direction.has_value());
|
||||
|
||||
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 scrollbar_data = compute_scrollbar_data(m_scroll_thumb_dragging_direction.value(), AdjustThumbRectForScrollOffset::Yes);
|
||||
VERIFY(scrollbar_data.has_value());
|
||||
|
||||
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;
|
||||
auto orientation = m_scroll_thumb_dragging_direction == ScrollDirection::Horizontal ? Orientation::Horizontal : Orientation::Vertical;
|
||||
auto offset_relative_to_gutter = (position - scrollbar_data->gutter_rect.location()).primary_offset_for_orientation(orientation);
|
||||
auto gutter_size = scrollbar_data->gutter_rect.primary_size_for_orientation(orientation);
|
||||
auto thumb_size = scrollbar_data->thumb_rect.primary_size_for_orientation(orientation);
|
||||
|
||||
// Set the thumb grab position, if we haven't got one already.
|
||||
if (!m_scroll_thumb_grab_position.has_value()) {
|
||||
m_scroll_thumb_grab_position = scrollbar_data->thumb_rect.contains(position)
|
||||
? (position - scrollbar_data->thumb_rect.location()).primary_offset_for_orientation(orientation)
|
||||
: max(min(offset_relative_to_gutter, thumb_size / 2), offset_relative_to_gutter - gutter_size + thumb_size);
|
||||
}
|
||||
|
||||
// Calculate the relative scroll position (0..1) based on the position of the mouse cursor. We only move the thumb
|
||||
// if we are interacting with the grab point on the thumb. E.g. if the thumb is all the way to its minimum position
|
||||
// and the position is beyond the grab point, we should do nothing.
|
||||
auto constrained_offset = AK::clamp(offset_relative_to_gutter - m_scroll_thumb_grab_position.value(), 0, gutter_size - thumb_size);
|
||||
auto scroll_position = constrained_offset.to_double() / (gutter_size - thumb_size).to_double();
|
||||
|
||||
// Calculate the scroll offset we need to apply to the viewport or element.
|
||||
auto scrollable_overflow_size = scrollable_overflow_rect()->primary_size_for_orientation(orientation);
|
||||
auto padding_size = absolute_padding_box_rect().primary_size_for_orientation(orientation);
|
||||
auto scroll_position_in_pixels = CSSPixels::nearest_value_for(scroll_position * (scrollable_overflow_size - padding_size));
|
||||
|
||||
// Set the new scroll offset.
|
||||
auto new_scroll_offset = is_viewport() ? document().navigable()->viewport_scroll_offset() : scroll_offset();
|
||||
new_scroll_offset.set_primary_offset_for_orientation(orientation, scroll_position_in_pixels);
|
||||
|
||||
if (is_viewport())
|
||||
document().window()->scroll_by(scroll_delta.x(), scroll_delta.y());
|
||||
document().navigable()->perform_scroll_of_viewport(new_scroll_offset);
|
||||
else
|
||||
scroll_by(scroll_delta.x(), scroll_delta.y());
|
||||
|
||||
m_last_mouse_tracking_position = position;
|
||||
set_scroll_offset(new_scroll_offset);
|
||||
}
|
||||
|
||||
bool PaintableBox::handle_mousewheel(Badge<EventHandler>, CSSPixelPoint, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y)
|
||||
|
|
|
@ -279,7 +279,7 @@ private:
|
|||
virtual DispatchEventOfSameName handle_mousemove(Badge<EventHandler>, CSSPixelPoint, unsigned buttons, unsigned modifiers) override;
|
||||
|
||||
bool scrollbar_contains_mouse_position(ScrollDirection, CSSPixelPoint);
|
||||
void scroll_to_mouse_postion(CSSPixelPoint);
|
||||
void scroll_to_mouse_position(CSSPixelPoint);
|
||||
|
||||
OwnPtr<StackingContext> m_stacking_context;
|
||||
|
||||
|
@ -305,7 +305,7 @@ private:
|
|||
Optional<BordersData> m_outline_data;
|
||||
CSSPixels m_outline_offset { 0 };
|
||||
|
||||
Optional<CSSPixelPoint> m_last_mouse_tracking_position;
|
||||
Optional<CSSPixels> m_scroll_thumb_grab_position;
|
||||
Optional<ScrollDirection> m_scroll_thumb_dragging_direction;
|
||||
bool m_draw_enlarged_horizontal_scrollbar { false };
|
||||
bool m_draw_enlarged_vertical_scrollbar { false };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue