diff --git a/crates/ui/src/lib.rs b/crates/ui/src/lib.rs index c8d6ac4f0d910d8a7df9b2d2f44984fd5a9a1a83..433bc139cfc273e9d086473eabb55cb94ae579ed 100644 --- a/crates/ui/src/lib.rs +++ b/crates/ui/src/lib.rs @@ -866,7 +866,7 @@ impl Ui { } /// Scrolls `scroller`, a scrolling box, until `target` within it starts at its top, both - /// as last laid out; the wheel's easing carries it there. + /// as last laid out, easing there. pub fn scroll_to(&mut self, scroller: Id, target: Id) { let (Some(top), Some(start)) = (self.rect(target), self.rect(scroller)) else { return; @@ -1036,7 +1036,9 @@ impl Ui { Some(id) => { let state = self.states.entry(id).or_default(); let most = (state.content - (state.rect[3] - state.rect[1])).max(0.0); - state.scroll_target = (state.scroll_target - delta[1]).clamp(0.0, most); + // The wheel moves the content at once, as on the page; only jumps ease. + state.scroll = (state.scroll - delta[1]).clamp(0.0, most); + state.scroll_target = state.scroll; self.signals.entry(id).or_default().events.push(event); } None => {} diff --git a/crates/ui/src/list.rs b/crates/ui/src/list.rs index f2239326690c3c4c58d91304f11c15aa5b336f40..93c5c4ed2e14468f984adf3fb2d9d4c405bd1145 100644 --- a/crates/ui/src/list.rs +++ b/crates/ui/src/list.rs @@ -132,7 +132,8 @@ pub fn list( for event in ui.signal(id).events { if let Event::Wheel(delta) = event { - state.target = (state.target - f64::from(delta[1])).clamp(0.0, most); + state.scroll = (state.scroll - f64::from(delta[1])).clamp(0.0, most); + state.target = state.scroll; } } let mut clicked = None; diff --git a/crates/ui/src/shell.rs b/crates/ui/src/shell.rs index 8cee9f9eaf614fbd0ab377f0f51125e6e5a3af1f..e530d0523391bf391f5b4f7a41557d2c379d1533 100644 --- a/crates/ui/src/shell.rs +++ b/crates/ui/src/shell.rs @@ -65,12 +65,18 @@ pub fn section_tabs( let wide = left + lean(height) + crate::SHADOW[0]; let view = ui.rect(row).map_or(wide, |rect| rect[2] - rect[0]); let most = (wide - view).max(0.0); - let [scroll, shown] = [row.child("scroll"), row.child("shown")]; + let [scroll, shown, shift] = [row.child("scroll"), row.child("shown"), row.child("offset")]; let held_value = |ui: &Ui, id: Id| ui.states.get(&id).and_then(|state| state.tween); let mut target = held_value(ui, scroll).map_or(0.0, |[_, target]| target); + // The wheel moves the tabs at once from where they show, as on the page; only jumps ease. + let mut wheeled = false; for event in ui.signal(row).events { if let crate::Event::Wheel([x, y]) = event { + if !wheeled { + target = held_value(ui, shift).map_or(target, |[value, _]| value); + } target -= x + y; + wheeled = true; } } if held_value(ui, shown).is_none_or(|[_, was]| was != active as f32) @@ -91,7 +97,11 @@ pub fn section_tabs( Some(start) }); let target = ui.hold(scroll, target.clamp(0.0, most)); - let offset = ui.animate(row.child("offset"), target); + let offset = if wheeled { + ui.hold(shift, target) + } else { + ui.animate(shift, target) + }; let fade = |cut: bool| if cut { EDGE } else { 0.0 }; ui.open_as( row, diff --git a/crates/ui/src/tests.rs b/crates/ui/src/tests.rs index 3bc93204a6bb413a7b51552742e7562a42105736..97f317480f7bc253eba8e4aff47b2cd2cbf40fb3 100644 --- a/crates/ui/src/tests.rs +++ b/crates/ui/src/tests.rs @@ -542,10 +542,12 @@ fn wheel_scrolls_within_the_content_and_clips_children() { build(&mut ui); ui.event(Event::PointerMoved([50.0, 50.0])); ui.event(Event::Wheel([0.0, -1000.0])); - for _ in 0..60 { - build(&mut ui); - } - assert_eq!(ui.states[&list.unwrap()].scroll, 200.0); + build(&mut ui); + assert_eq!( + ui.states[&list.unwrap()].scroll, + 200.0, + "at once, as on the page" + ); assert_eq!(ui.rect(last.unwrap()), Some([0.0, 70.0, 100.0, 100.0])); let layers = ui.layers(); assert!(matches!( @@ -1906,10 +1908,10 @@ fn lists_hold_the_selection_still_as_items_arrive_and_leave_above() { list_frame(&mut ui, &rows, &mut selected, &[]); rows = Keyed::new((20_000..20_050).chain(rows.keys.iter().copied())); list_frame(&mut ui, &rows, &mut selected, &[]); - let moving = row_top(&ui, 500).unwrap(); - assert!( - moving < at && moving > at - 60.0, - "the wheel eases on through arrivals" + assert_eq!( + row_top(&ui, 500), + Some(at - 60.0), + "the wheel moves the rows at once, and they hold through arrivals" ); settle_list(&mut ui, &rows, &mut selected); assert_eq!(row_top(&ui, 500), Some(at - 60.0)); @@ -2950,16 +2952,15 @@ fn the_wheel_scrolls_overflowing_tabs_sideways_either_way_it_turns() { let first = ui.rect(shell::tab_id(row, 0)).unwrap()[0]; ui.event(Event::PointerMoved([100.0, 14.0])); ui.event(Event::Wheel([0.0, -40.0])); - settle_tabs(&mut ui, 0); + tab_row(&mut ui, 0); + tab_row(&mut ui, 0); let scrolled = ui.rect(shell::tab_id(row, 0)).unwrap()[0]; - assert!( - (first - scrolled - 40.0).abs() < 0.5, - "{first} to {scrolled}" - ); + assert_eq!(first - scrolled, 40.0, "at once, as on the page"); ui.event(Event::Wheel([30.0, 0.0])); - settle_tabs(&mut ui, 0); + tab_row(&mut ui, 0); + tab_row(&mut ui, 0); let back = ui.rect(shell::tab_id(row, 0)).unwrap()[0]; - assert!((back - scrolled - 30.0).abs() < 0.5, "{scrolled} to {back}"); + assert_eq!(back - scrolled, 30.0); } #[test]