From ac2466d14f3461edfe0691c0c1546ea2b263d56a Mon Sep 17 00:00:00 2001 From: valadaptive <79560998+valadaptive@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:12:36 -0400 Subject: [PATCH] Update ScrollArea drag velocity when drag stopped (#5175) Fixes #5174. The drag velocity was not being updated unless the cursor counted as "dragging", which only happens when it's in motion. This effectively guarantees that the drag velocity will never be zero, even if the cursor is not moving, and results in spurious scroll velocity being applied when the cursor is released. Instead, we update the velocity only when the drag is stopped, which is when the kinetic scrolling actually needs to begin. Note that we immediately *apply* the scroll velocity on the same frame that we first set it, to avoid a 1-frame gap where the scroll area doesn't move. I believe that *not* setting `scroll_stuck_to_end` and `offset_target` when the drag is released is the correct thing to do, as they should apply immediately once the user stops dragging. Should we maybe clear the drag velocity instead if `scroll_stuck_to_end` is true or `offset_target` exists? * Closes #5174 * [x] I have followed the instructions in the PR template --- crates/egui/src/containers/scroll_area.rs | 16 ++++++++++++---- crates/emath/src/vec2b.rs | 8 ++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index ab7da8aff..3b3925c9d 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -621,20 +621,28 @@ impl ScrollArea { .interact_rect .map(|rect| ui.interact(rect, id.with("area"), Sense::drag())); - if content_response_option.map(|response| response.dragged()) == Some(true) { + if content_response_option + .as_ref() + .is_some_and(|response| response.dragged()) + { for d in 0..2 { if scroll_enabled[d] { ui.input(|input| { state.offset[d] -= input.pointer.delta()[d]; - state.vel[d] = input.pointer.velocity()[d]; }); state.scroll_stuck_to_end[d] = false; state.offset_target[d] = None; - } else { - state.vel[d] = 0.0; } } } else { + // Apply the cursor velocity to the scroll area when the user releases the drag. + if content_response_option + .as_ref() + .is_some_and(|response| response.drag_stopped()) + { + state.vel = + scroll_enabled.to_vec2() * ui.input(|input| input.pointer.velocity()); + } for d in 0..2 { // Kinetic scrolling let stop_speed = 20.0; // Pixels per second. diff --git a/crates/emath/src/vec2b.rs b/crates/emath/src/vec2b.rs index f241de64e..673f2959e 100644 --- a/crates/emath/src/vec2b.rs +++ b/crates/emath/src/vec2b.rs @@ -1,3 +1,5 @@ +use crate::Vec2; + /// Two bools, one for each axis (X and Y). #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -43,6 +45,12 @@ impl Vec2b { y: self.y || other.y, } } + + /// Convert to a float `Vec2` where the components are 1.0 for `true` and 0.0 for `false`. + #[inline] + pub fn to_vec2(self) -> Vec2 { + Vec2::new(self.x.into(), self.y.into()) + } } impl From for Vec2b {