Hide scroll bars when dragging other things (#7689)

This closes a small visual glitch where scroll bars would show up when
dragging something unrelated, like a slider or a panel side.
This commit is contained in:
Emil Ernerfeldt 2025-11-07 14:43:49 +01:00
parent d5320fe827
commit 74dce787af
1 changed files with 74 additions and 57 deletions

View File

@ -3,8 +3,8 @@
use std::ops::{Add, AddAssign, BitOr, BitOrAssign}; use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
use crate::{ use crate::{
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Sense, Ui, UiBuilder, UiKind, Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp, UiKind, UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp,
}; };
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -659,6 +659,9 @@ struct Prepared {
/// not for us to handle so we save it and restore it after this [`ScrollArea`] is done. /// not for us to handle so we save it and restore it after this [`ScrollArea`] is done.
saved_scroll_target: [Option<pass_state::ScrollTarget>; 2], saved_scroll_target: [Option<pass_state::ScrollTarget>; 2],
/// The response from dragging the background (if enabled)
background_drag_response: Option<Response>,
animated: bool, animated: bool,
} }
@ -772,10 +775,8 @@ impl ScrollArea {
let viewport = Rect::from_min_size(Pos2::ZERO + state.offset, inner_size); let viewport = Rect::from_min_size(Pos2::ZERO + state.offset, inner_size);
let dt = ui.input(|i| i.stable_dt).at_most(0.1); let dt = ui.input(|i| i.stable_dt).at_most(0.1);
if scroll_source.drag let background_drag_response =
&& ui.is_enabled() if scroll_source.drag && ui.is_enabled() && state.content_is_too_large.any() {
&& (state.content_is_too_large[0] || state.content_is_too_large[1])
{
// Drag contents to scroll (for touch screens mostly). // Drag contents to scroll (for touch screens mostly).
// We must do this BEFORE adding content to the `ScrollArea`, // We must do this BEFORE adding content to the `ScrollArea`,
// or we will steal input from the widgets we contain. // or we will steal input from the widgets we contain.
@ -802,8 +803,8 @@ impl ScrollArea {
.as_ref() .as_ref()
.is_some_and(|response| response.drag_stopped()) .is_some_and(|response| response.drag_stopped())
{ {
state.vel = state.vel = direction_enabled.to_vec2()
direction_enabled.to_vec2() * ui.input(|input| input.pointer.velocity()); * ui.input(|input| input.pointer.velocity());
} }
for d in 0..2 { for d in 0..2 {
// Kinetic scrolling // Kinetic scrolling
@ -824,19 +825,23 @@ impl ScrollArea {
} }
// Set the desired mouse cursors. // Set the desired mouse cursors.
if let Some(response) = content_response_option { if let Some(response) = &content_response_option {
if response.dragged() { if response.dragged()
if let Some(cursor) = on_drag_cursor { && let Some(cursor) = on_drag_cursor
response.on_hover_cursor(cursor); {
} ui.ctx().set_cursor_icon(cursor);
} else if response.hovered() } else if response.hovered()
&& let Some(cursor) = on_hover_cursor && let Some(cursor) = on_hover_cursor
{ {
response.on_hover_cursor(cursor); ui.ctx().set_cursor_icon(cursor);
}
} }
} }
content_response_option
} else {
None
};
// Scroll with an animation if we have a target offset (that hasn't been cleared by the code // Scroll with an animation if we have a target offset (that hasn't been cleared by the code
// above). // above).
for d in 0..2 { for d in 0..2 {
@ -888,6 +893,7 @@ impl ScrollArea {
wheel_scroll_multiplier, wheel_scroll_multiplier,
stick_to_end, stick_to_end,
saved_scroll_target, saved_scroll_target,
background_drag_response,
animated, animated,
} }
} }
@ -1003,6 +1009,7 @@ impl Prepared {
wheel_scroll_multiplier, wheel_scroll_multiplier,
stick_to_end, stick_to_end,
saved_scroll_target, saved_scroll_target,
background_drag_response,
animated, animated,
} = self; } = self;
@ -1118,7 +1125,16 @@ impl Prepared {
); );
let max_offset = content_size - inner_rect.size(); let max_offset = content_size - inner_rect.size();
let is_hovering_outer_rect = ui.rect_contains_pointer(outer_rect);
// Drag-to-scroll?
let is_dragging_background = background_drag_response
.as_ref()
.is_some_and(|r| r.dragged());
let is_hovering_outer_rect = ui.rect_contains_pointer(outer_rect)
&& ui.ctx().dragged_id().is_none()
|| is_dragging_background;
if scroll_source.mouse_wheel && ui.is_enabled() && is_hovering_outer_rect { if scroll_source.mouse_wheel && ui.is_enabled() && is_hovering_outer_rect {
let always_scroll_enabled_direction = ui.style().always_scroll_the_only_direction let always_scroll_enabled_direction = ui.style().always_scroll_the_only_direction
&& direction_enabled[0] != direction_enabled[1]; && direction_enabled[0] != direction_enabled[1];
@ -1204,6 +1220,7 @@ impl Prepared {
let is_hovering_bar_area = is_hovering_outer_rect let is_hovering_bar_area = is_hovering_outer_rect
&& ui.rect_contains_pointer(max_bar_rect) && ui.rect_contains_pointer(max_bar_rect)
&& !is_dragging_background
|| state.scroll_bar_interaction[d]; || state.scroll_bar_interaction[d];
let is_hovering_bar_area_t = ui let is_hovering_bar_area_t = ui