diff --git a/crates/snowbound/src/main.rs b/crates/snowbound/src/main.rs index 79d8287032e6335f1cec0fcdb7f20f5e8b486fcb..4c95b3685fa48e6cf82529db8a65919415c9edae 100644 --- a/crates/snowbound/src/main.rs +++ b/crates/snowbound/src/main.rs @@ -1754,6 +1754,7 @@ impl State { -self.view.viewport.origin[index], [scroll.min[index], scroll.max[index]], self.view.viewport.size[index] as f32, + scroll.max[1 - index] > scroll.min[1 - index], [0.18, 0.18, 0.18, 0.45], ) { let response = self.view.scroll_to(index, offset)?; diff --git a/crates/ui/src/list.rs b/crates/ui/src/list.rs index ef1b977f391b929ad9bd84fa8a59881e49cc85d1..9ab81d72671a2c69a90f9c0b894e5a758807a744 100644 --- a/crates/ui/src/list.rs +++ b/crates/ui/src/list.rs @@ -246,6 +246,7 @@ pub fn list( scroll as f32, [0.0, most as f32], view as f32, + false, thumb, ) { // Rows keep their places relative to the new offset, or next frame's hold on the diff --git a/crates/ui/src/tests.rs b/crates/ui/src/tests.rs index 22e36ee77a917b6ce1a7c54a07726c151e9ed9fa..84c3b4b8c2831a0e04740111aeff06aec4175e13 100644 --- a/crates/ui/src/tests.rs +++ b/crates/ui/src/tests.rs @@ -862,13 +862,20 @@ fn scrollbar_thumbs_track_the_offset_and_drags_reach_both_ends() { "view", Spec { flags: Flags::CUSTOM, - size: [px(200.0), px(220.0)], + size: [px(200.0), px(218.0)], ..Spec::default() }, ); - if let Some(chosen) = - scrollbar(ui, "bar", Axis::Y, *offset, [0.0, 1000.0], 400.0, [0.5; 4]) - { + if let Some(chosen) = scrollbar( + ui, + "bar", + Axis::Y, + *offset, + [0.0, 1000.0], + 400.0, + true, + [0.5; 4], + ) { *offset = chosen; } ui.close(); @@ -902,6 +909,36 @@ fn scrollbar_thumbs_track_the_offset_and_drags_reach_both_ends() { assert_eq!(offset, 0.0); } +#[test] +fn a_scrollbar_without_a_corner_ends_as_far_from_its_end_as_from_its_start() { + let mut ui = Ui::new(Theme::dark(), DOUBLE_CLICK); + for _ in 0..2 { + frame(&mut ui, |ui| { + ui.open( + "view", + Spec { + flags: Flags::CUSTOM, + size: [px(200.0), px(208.0)], + ..Spec::default() + }, + ); + scrollbar( + ui, + "bar", + Axis::Y, + 1000.0, + [0.0, 1000.0], + 400.0, + false, + [0.5; 4], + ); + ui.close(); + }); + } + let rect = ui.rect(Id::ROOT.child("view").child("bar")).unwrap(); + assert_eq!(rect[3], 204.0); +} + #[test] fn system_scrollers_step_page_and_drag_by_the_parts_the_platform_paints() { let mut ui = Ui::new(Theme::dark(), DOUBLE_CLICK); @@ -936,9 +973,16 @@ fn system_scrollers_step_page_and_drag_by_the_parts_the_platform_paints() { ..Spec::default() }, ); - if let Some(chosen) = - scrollbar(ui, "bar", Axis::Y, *offset, [0.0, 1000.0], 400.0, [0.5; 4]) - { + if let Some(chosen) = scrollbar( + ui, + "bar", + Axis::Y, + *offset, + [0.0, 1000.0], + 400.0, + true, + [0.5; 4], + ) { *offset = chosen; } ui.close(); diff --git a/crates/ui/src/widgets.rs b/crates/ui/src/widgets.rs index de1b7bc458adb119567447cba7c79516bac69a05..912c46e201adc48c50125b56edabbb8236814ae1 100644 --- a/crates/ui/src/widgets.rs +++ b/crates/ui/src/widgets.rs @@ -130,8 +130,10 @@ const REPEAT: Duration = Duration::from_millis(50); /// A scrollbar along the far edge of the current box, for content whose scroll offset /// ranges over `range` while `view` of it shows, all in one unit: the platform's scroller -/// where `Ui::scrollers` has one, otherwise a thumb in `color` over the content. Returns -/// the offset the scrollbar chose. +/// where `Ui::scrollers` has one, otherwise a thumb in `color` over the content. With a +/// `corner`, its far end leaves room for the other axis's scrollbar. Returns the offset +/// the scrollbar chose. +#[allow(clippy::too_many_arguments)] pub fn scrollbar( ui: &mut Ui, part: impl Hash, @@ -139,18 +141,19 @@ pub fn scrollbar( offset: f32, range: [f32; 2], view: f32, + corner: bool, color: [f32; 4], ) -> Option { if ui.scrollers.is_some() { - return system_scrollbar(ui, part, axis, offset, range, view); + return system_scrollbar(ui, part, axis, offset, range, view, corner); } let along = usize::from(axis == Axis::Y); let rect = ui.rect(ui.current())?; let length = rect[along + 2] - rect[along]; let cross = rect[3 - along] - rect[1 - along]; let span = range[1] - range[0]; - // The far end leaves room for the other axis's bar. - let track = length - 20.0; + // A thumb keeps 4 from each end and the box's far edge, and is 6 across. + let track = length - 8.0 - if corner { 10.0 } else { 0.0 }; if span <= 0.0 || track <= 0.0 { return None; } @@ -197,13 +200,13 @@ fn system_scrollbar( offset: f32, range: [f32; 2], view: f32, + corner: bool, ) -> Option { let along = usize::from(axis == Axis::Y); let rect = ui.rect(ui.current())?; let span = range[1] - range[0]; let thickness = ui.scrollers.as_ref()?.thickness; - // The far end leaves the corner beside the other axis's scroller. - let length = rect[along + 2] - rect[along] - thickness; + let length = rect[along + 2] - rect[along] - if corner { thickness } else { 0.0 }; if span <= 0.0 || length <= 0.0 { return None; } @@ -283,7 +286,7 @@ fn system_scrollbar( ..Spec::default() }, ); - if axis == Axis::Y { + if axis == Axis::Y && corner { // The corner between the two scrollers shows the content's white, as AppKit's does. ui.leaf( ("corner", &part),