Skip to content

Commit 74dce78

Browse files
committed
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.
1 parent d5320fe commit 74dce78

File tree

1 file changed

+76
-59
lines changed

1 file changed

+76
-59
lines changed

crates/egui/src/containers/scroll_area.rs

Lines changed: 76 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
use std::ops::{Add, AddAssign, BitOr, BitOrAssign};
44

55
use crate::{
6-
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Sense, Ui, UiBuilder, UiKind,
7-
UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp,
6+
Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder,
7+
UiKind, UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp,
88
};
99

1010
#[derive(Clone, Copy, Debug)]
@@ -659,6 +659,9 @@ struct Prepared {
659659
/// not for us to handle so we save it and restore it after this [`ScrollArea`] is done.
660660
saved_scroll_target: [Option<pass_state::ScrollTarget>; 2],
661661

662+
/// The response from dragging the background (if enabled)
663+
background_drag_response: Option<Response>,
664+
662665
animated: bool,
663666
}
664667

@@ -772,70 +775,72 @@ impl ScrollArea {
772775
let viewport = Rect::from_min_size(Pos2::ZERO + state.offset, inner_size);
773776
let dt = ui.input(|i| i.stable_dt).at_most(0.1);
774777

775-
if scroll_source.drag
776-
&& ui.is_enabled()
777-
&& (state.content_is_too_large[0] || state.content_is_too_large[1])
778-
{
779-
// Drag contents to scroll (for touch screens mostly).
780-
// We must do this BEFORE adding content to the `ScrollArea`,
781-
// or we will steal input from the widgets we contain.
782-
let content_response_option = state
783-
.interact_rect
784-
.map(|rect| ui.interact(rect, id.with("area"), Sense::drag()));
785-
786-
if content_response_option
787-
.as_ref()
788-
.is_some_and(|response| response.dragged())
789-
{
790-
for d in 0..2 {
791-
if direction_enabled[d] {
792-
ui.input(|input| {
793-
state.offset[d] -= input.pointer.delta()[d];
794-
});
795-
state.scroll_stuck_to_end[d] = false;
796-
state.offset_target[d] = None;
797-
}
798-
}
799-
} else {
800-
// Apply the cursor velocity to the scroll area when the user releases the drag.
778+
let background_drag_response =
779+
if scroll_source.drag && ui.is_enabled() && state.content_is_too_large.any() {
780+
// Drag contents to scroll (for touch screens mostly).
781+
// We must do this BEFORE adding content to the `ScrollArea`,
782+
// or we will steal input from the widgets we contain.
783+
let content_response_option = state
784+
.interact_rect
785+
.map(|rect| ui.interact(rect, id.with("area"), Sense::drag()));
786+
801787
if content_response_option
802788
.as_ref()
803-
.is_some_and(|response| response.drag_stopped())
789+
.is_some_and(|response| response.dragged())
804790
{
805-
state.vel =
806-
direction_enabled.to_vec2() * ui.input(|input| input.pointer.velocity());
807-
}
808-
for d in 0..2 {
809-
// Kinetic scrolling
810-
let stop_speed = 20.0; // Pixels per second.
811-
let friction_coeff = 1000.0; // Pixels per second squared.
812-
813-
let friction = friction_coeff * dt;
814-
if friction > state.vel[d].abs() || state.vel[d].abs() < stop_speed {
815-
state.vel[d] = 0.0;
816-
} else {
817-
state.vel[d] -= friction * state.vel[d].signum();
818-
// Offset has an inverted coordinate system compared to
819-
// the velocity, so we subtract it instead of adding it
820-
state.offset[d] -= state.vel[d] * dt;
821-
ctx.request_repaint();
791+
for d in 0..2 {
792+
if direction_enabled[d] {
793+
ui.input(|input| {
794+
state.offset[d] -= input.pointer.delta()[d];
795+
});
796+
state.scroll_stuck_to_end[d] = false;
797+
state.offset_target[d] = None;
798+
}
799+
}
800+
} else {
801+
// Apply the cursor velocity to the scroll area when the user releases the drag.
802+
if content_response_option
803+
.as_ref()
804+
.is_some_and(|response| response.drag_stopped())
805+
{
806+
state.vel = direction_enabled.to_vec2()
807+
* ui.input(|input| input.pointer.velocity());
808+
}
809+
for d in 0..2 {
810+
// Kinetic scrolling
811+
let stop_speed = 20.0; // Pixels per second.
812+
let friction_coeff = 1000.0; // Pixels per second squared.
813+
814+
let friction = friction_coeff * dt;
815+
if friction > state.vel[d].abs() || state.vel[d].abs() < stop_speed {
816+
state.vel[d] = 0.0;
817+
} else {
818+
state.vel[d] -= friction * state.vel[d].signum();
819+
// Offset has an inverted coordinate system compared to
820+
// the velocity, so we subtract it instead of adding it
821+
state.offset[d] -= state.vel[d] * dt;
822+
ctx.request_repaint();
823+
}
822824
}
823825
}
824-
}
825826

826-
// Set the desired mouse cursors.
827-
if let Some(response) = content_response_option {
828-
if response.dragged() {
829-
if let Some(cursor) = on_drag_cursor {
830-
response.on_hover_cursor(cursor);
827+
// Set the desired mouse cursors.
828+
if let Some(response) = &content_response_option {
829+
if response.dragged()
830+
&& let Some(cursor) = on_drag_cursor
831+
{
832+
ui.ctx().set_cursor_icon(cursor);
833+
} else if response.hovered()
834+
&& let Some(cursor) = on_hover_cursor
835+
{
836+
ui.ctx().set_cursor_icon(cursor);
831837
}
832-
} else if response.hovered()
833-
&& let Some(cursor) = on_hover_cursor
834-
{
835-
response.on_hover_cursor(cursor);
836838
}
837-
}
838-
}
839+
840+
content_response_option
841+
} else {
842+
None
843+
};
839844

840845
// Scroll with an animation if we have a target offset (that hasn't been cleared by the code
841846
// above).
@@ -888,6 +893,7 @@ impl ScrollArea {
888893
wheel_scroll_multiplier,
889894
stick_to_end,
890895
saved_scroll_target,
896+
background_drag_response,
891897
animated,
892898
}
893899
}
@@ -1003,6 +1009,7 @@ impl Prepared {
10031009
wheel_scroll_multiplier,
10041010
stick_to_end,
10051011
saved_scroll_target,
1012+
background_drag_response,
10061013
animated,
10071014
} = self;
10081015

@@ -1118,7 +1125,16 @@ impl Prepared {
11181125
);
11191126

11201127
let max_offset = content_size - inner_rect.size();
1121-
let is_hovering_outer_rect = ui.rect_contains_pointer(outer_rect);
1128+
1129+
// Drag-to-scroll?
1130+
let is_dragging_background = background_drag_response
1131+
.as_ref()
1132+
.is_some_and(|r| r.dragged());
1133+
1134+
let is_hovering_outer_rect = ui.rect_contains_pointer(outer_rect)
1135+
&& ui.ctx().dragged_id().is_none()
1136+
|| is_dragging_background;
1137+
11221138
if scroll_source.mouse_wheel && ui.is_enabled() && is_hovering_outer_rect {
11231139
let always_scroll_enabled_direction = ui.style().always_scroll_the_only_direction
11241140
&& direction_enabled[0] != direction_enabled[1];
@@ -1204,6 +1220,7 @@ impl Prepared {
12041220

12051221
let is_hovering_bar_area = is_hovering_outer_rect
12061222
&& ui.rect_contains_pointer(max_bar_rect)
1223+
&& !is_dragging_background
12071224
|| state.scroll_bar_interaction[d];
12081225

12091226
let is_hovering_bar_area_t = ui

0 commit comments

Comments
 (0)