diff --git a/crates/gpui/src/elements/div.rs b/crates/gpui/src/elements/div.rs index 821f155f96d168e5319d9a8981ca4be75df7b854..c80acacce3d714c56dca0cdb65a4477b4c3b3b0e 100644 --- a/crates/gpui/src/elements/div.rs +++ b/crates/gpui/src/elements/div.rs @@ -3193,7 +3193,11 @@ impl ScrollHandle { match active_item.strategy { ScrollStrategy::FirstVisible => { if state.overflow.y == Overflow::Scroll { - if bounds.top() + scroll_offset.y < state.bounds.top() { + let child_height = bounds.size.height; + let viewport_height = state.bounds.size.height; + if child_height > viewport_height { + scroll_offset.y = state.bounds.top() - bounds.top(); + } else if bounds.top() + scroll_offset.y < state.bounds.top() { scroll_offset.y = state.bounds.top() - bounds.top(); } else if bounds.bottom() + scroll_offset.y > state.bounds.bottom() { scroll_offset.y = state.bounds.bottom() - bounds.bottom(); @@ -3206,7 +3210,11 @@ impl ScrollHandle { } if state.overflow.x == Overflow::Scroll { - if bounds.left() + scroll_offset.x < state.bounds.left() { + let child_width = bounds.size.width; + let viewport_width = state.bounds.size.width; + if child_width > viewport_width { + scroll_offset.x = state.bounds.left() - bounds.left(); + } else if bounds.left() + scroll_offset.x < state.bounds.left() { scroll_offset.x = state.bounds.left() - bounds.left(); } else if bounds.right() + scroll_offset.x > state.bounds.right() { scroll_offset.x = state.bounds.right() - bounds.right(); @@ -3268,3 +3276,46 @@ impl ScrollHandle { self.0.borrow().child_bounds.len() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn scroll_handle_aligns_wide_children_to_left_edge() { + let handle = ScrollHandle::new(); + { + let mut state = handle.0.borrow_mut(); + state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(80.), px(20.))); + state.child_bounds = vec![Bounds::new(point(px(25.), px(0.)), size(px(200.), px(20.)))]; + state.overflow.x = Overflow::Scroll; + state.active_item = Some(ScrollActiveItem { + index: 0, + strategy: ScrollStrategy::default(), + }); + } + + handle.scroll_to_active_item(); + + assert_eq!(handle.offset().x, px(-25.)); + } + + #[test] + fn scroll_handle_aligns_tall_children_to_top_edge() { + let handle = ScrollHandle::new(); + { + let mut state = handle.0.borrow_mut(); + state.bounds = Bounds::new(point(px(0.), px(0.)), size(px(20.), px(80.))); + state.child_bounds = vec![Bounds::new(point(px(0.), px(25.)), size(px(20.), px(200.)))]; + state.overflow.y = Overflow::Scroll; + state.active_item = Some(ScrollActiveItem { + index: 0, + strategy: ScrollStrategy::default(), + }); + } + + handle.scroll_to_active_item(); + + assert_eq!(handle.offset().y, px(-25.)); + } +}