gpui: Re-land uniform list scroll fixes (#40899)

Finn Evers and Agus Zubiaga created

Re-lands https://github.com/zed-industries/zed/pull/40719, fixes the
bugs that were discovered with it and improves some more stuff in that
area

Release Notes:

- Fixed a rare issue where the extension page would stutter while
scrolling.

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>

Change summary

crates/gpui/src/elements/uniform_list.rs | 63 ++++++++-----------------
1 file changed, 20 insertions(+), 43 deletions(-)

Detailed changes

crates/gpui/src/elements/uniform_list.rs 🔗

@@ -343,7 +343,7 @@ impl Element for UniformList {
         };
         let content_size = Size {
             width: content_width,
-            height: longest_item_size.height * self.item_count + padding.top + padding.bottom,
+            height: longest_item_size.height * self.item_count,
         };
 
         let shared_scroll_offset = self.interactivity.scroll_offset.clone().unwrap();
@@ -364,17 +364,7 @@ impl Element for UniformList {
             content_size,
             window,
             cx,
-            |style, mut scroll_offset, hitbox, window, cx| {
-                let border = style.border_widths.to_pixels(window.rem_size());
-                let padding = style
-                    .padding
-                    .to_pixels(bounds.size.into(), window.rem_size());
-
-                let padded_bounds = Bounds::from_corners(
-                    bounds.origin + point(border.left + padding.left, border.top),
-                    bounds.bottom_right() - point(border.right + padding.right, border.bottom),
-                );
-
+            |_style, mut scroll_offset, hitbox, window, cx| {
                 let y_flipped = if let Some(scroll_handle) = &self.scroll_handle {
                     let scroll_state = scroll_handle.0.borrow();
                     scroll_state.y_flipped
@@ -383,13 +373,14 @@ impl Element for UniformList {
                 };
 
                 if self.item_count > 0 {
-                    let content_height =
-                        item_height * self.item_count + padding.top + padding.bottom;
+                    let content_height = item_height * self.item_count;
+
                     let is_scrolled_vertically = !scroll_offset.y.is_zero();
-                    let min_vertical_scroll_offset = padded_bounds.size.height - content_height;
-                    if is_scrolled_vertically && scroll_offset.y < min_vertical_scroll_offset {
-                        shared_scroll_offset.borrow_mut().y = min_vertical_scroll_offset;
-                        scroll_offset.y = min_vertical_scroll_offset;
+                    let max_scroll_offset = padded_bounds.size.height - content_height;
+
+                    if is_scrolled_vertically && scroll_offset.y < max_scroll_offset {
+                        shared_scroll_offset.borrow_mut().y = max_scroll_offset;
+                        scroll_offset.y = max_scroll_offset;
                     }
 
                     let content_width = content_size.width + padding.left + padding.right;
@@ -407,18 +398,19 @@ impl Element for UniformList {
                         }
                         let list_height = padded_bounds.size.height;
                         let mut updated_scroll_offset = shared_scroll_offset.borrow_mut();
-                        let item_top = item_height * ix + padding.top;
+                        let item_top = item_height * ix;
                         let item_bottom = item_top + item_height;
                         let scroll_top = -updated_scroll_offset.y;
                         let offset_pixels = item_height * deferred_scroll.offset;
                         let mut scrolled_to_top = false;
 
-                        if item_top < scroll_top + padding.top + offset_pixels {
+                        if item_top < scroll_top + offset_pixels {
                             scrolled_to_top = true;
-                            updated_scroll_offset.y = -(item_top) + padding.top + offset_pixels;
-                        } else if item_bottom > scroll_top + list_height - padding.bottom {
+                            // todo: using the padding here is wrong - this only works well for few scenarios
+                            updated_scroll_offset.y = -item_top + padding.top + offset_pixels;
+                        } else if item_bottom > scroll_top + list_height {
                             scrolled_to_top = true;
-                            updated_scroll_offset.y = -(item_bottom - list_height) - padding.bottom;
+                            updated_scroll_offset.y = -(item_bottom - list_height);
                         }
 
                         if deferred_scroll.scroll_strict
@@ -480,14 +472,9 @@ impl Element for UniformList {
                     window.with_content_mask(Some(content_mask), |window| {
                         for (mut item, ix) in items.into_iter().zip(visible_range.clone()) {
                             let item_origin = padded_bounds.origin
-                                + point(
-                                    if can_scroll_horizontally {
-                                        scroll_offset.x + padding.left
-                                    } else {
-                                        scroll_offset.x
-                                    },
-                                    item_height * ix + scroll_offset.y + padding.top,
-                                );
+                                + scroll_offset
+                                + point(Pixels::ZERO, item_height * ix);
+
                             let available_width = if can_scroll_horizontally {
                                 padded_bounds.size.width + scroll_offset.x.abs()
                             } else {
@@ -502,18 +489,8 @@ impl Element for UniformList {
                             frame_state.items.push(item);
                         }
 
-                        let bounds = Bounds::new(
-                            padded_bounds.origin
-                                + point(
-                                    if can_scroll_horizontally {
-                                        scroll_offset.x + padding.left
-                                    } else {
-                                        scroll_offset.x
-                                    },
-                                    scroll_offset.y + padding.top,
-                                ),
-                            padded_bounds.size,
-                        );
+                        let bounds =
+                            Bounds::new(padded_bounds.origin + scroll_offset, padded_bounds.size);
                         for decoration in &self.decorations {
                             let mut decoration = decoration.as_ref().compute(
                                 visible_range.clone(),