diff --git a/crates/ui-egui/src/field_tests.rs b/crates/ui-egui/src/field_tests.rs index 287f986f6..1ace27233 100644 --- a/crates/ui-egui/src/field_tests.rs +++ b/crates/ui-egui/src/field_tests.rs @@ -317,6 +317,26 @@ fn moving_up_or_down_while_scrubbing_changes_the_speed_and_says_so() { u.frame_with(vec![Event::PointerMoved(to), press(false, to)], shift); u.frame(vec![]); assert_eq!(geom(&u, a).0 - x0, 200.0, "ten points at 2x with Shift"); + // The pointer reported gone from the window (egui's PointerGone) with the button still down + // keeps the speed it had: no notice of 1x, and the scrub goes on at 2x once it is back. + let x0 = geom(&u, a).0; + let from = label(&mut u, "X"); + let m = Modifiers::NONE; + let press = |pressed, pos| Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: m }; + u.frame_with(vec![Event::PointerMoved(from), press(true, from)], m); + let up = pos2(from.x, from.y - 150.0); + u.frame_with(vec![Event::PointerMoved(up)], m); + u.frame_with(vec![Event::PointerMoved(pos2(up.x + 10.0, up.y))], m); + assert!(toast(&u).contains("2x"), "{}", toast(&u)); + u.app.ui.toast = None; + u.frame_with(vec![Event::PointerGone], m); + u.frame_with(vec![Event::PointerMoved(pos2(up.x + 20.0, up.y))], m); + u.frame_with(vec![Event::PointerMoved(pos2(up.x + 30.0, up.y))], m); + assert!(toast(&u).is_empty(), "no notice while the pointer was gone or back at the same height: {}", toast(&u)); + let to = pos2(up.x + 30.0, up.y); + u.frame_with(vec![Event::PointerMoved(to), press(false, to)], m); + u.frame(vec![]); + assert_eq!(geom(&u, a).0 - x0, 40.0, "ten points before and ten after the pointer was gone, at 2x both"); } #[test] diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index 445bf210c..70c785f2c 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -168,7 +168,8 @@ pub(crate) fn scrub(ui: &Ui, id: Id, response: &Response, value: Option, st let scrub_id = id.with("scrub"); if response.dragged() || response.drag_stopped() { let began = response.drag_started(); - let (v0, mut amount, last_speed): (f64, f64, &'static str) = ui.data(|d| d.get_temp(scrub_id)).filter(|_| !began).unwrap_or((v, 0.0, "")); + let (v0, mut amount, last_speed, last_rise): (f64, f64, &'static str, f64) = + ui.data(|d| d.get_temp(scrub_id)).filter(|_| !began).unwrap_or((v, 0.0, "", 0.0)); let (dx, rise, shift, alt) = ui.input(|i| { let p = &i.pointer; // As it begins: all the way from the press (a drag is one only after a few points). @@ -178,10 +179,14 @@ pub(crate) fn scrub(ui: &Ui, id: Id, response: &Response, value: Option, st // does too. Elsewhere the scrub just stops there. let raw = p.motion().map_or(0.0, |m| m.x / i.pixels_per_point); let at_edge = moved == 0.0 && p.hover_pos().is_none_or(|at| at.x <= 1.0 || at.x >= i.content_rect().max.x - 1.0); - // How far above where the scrub began the pointer is: the speed (`scrub_speed`). - let rise = p.hover_pos().zip(p.press_origin()).map_or(0.0, |(at, from)| f64::from(from.y - at.y)); + // How far above where the scrub began the pointer is: the speed (`scrub_speed`); + // None while the pointer is gone from the window. + let rise = p.hover_pos().zip(p.press_origin()).map(|(at, from)| f64::from(from.y - at.y)); (f64::from(if at_edge { raw } else { moved }), rise, i.modifiers.shift, i.modifiers.alt) }); + // Gone from the window (egui says so, the button still down), the pointer keeps the speed + // it had rather than falling back to the usual one. + let rise = rise.unwrap_or(last_rise); let (speed, notice) = scrub_speed(rise); // The frame the button goes up moves nothing: the scrub ends where the last one left it. if response.dragged() { @@ -190,7 +195,7 @@ pub(crate) fn scrub(ui: &Ui, id: Id, response: &Response, value: Option, st if notice != last_speed && !(last_speed.is_empty() && speed == 1.0) { ui.data_mut(|d| d.insert_temp(scrub_notice_id(), notice)); } - ui.data_mut(|d| d.insert_temp(scrub_id, (v0, amount, notice))); + ui.data_mut(|d| d.insert_temp(scrub_id, (v0, amount, notice, rise))); } let quantum = if alt { step * 0.1 } else { step }; return Some(FieldEdit::Scrub(scrubbed(v0, amount, quantum), response.drag_stopped()));