Impose min scrollbar height in a way that doesn't impede scrollbar's movement

Max Brunsfeld created

Also, fix the editor's scroll max so that you can scroll to the last
display row.

Change summary

crates/editor/src/editor.rs  |  2 
crates/editor/src/element.rs | 40 ++++++++++++++++---------------------
2 files changed, 18 insertions(+), 24 deletions(-)

Detailed changes

crates/editor/src/editor.rs 🔗

@@ -1271,7 +1271,7 @@ impl Editor {
         let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
             (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
         } else {
-            display_map.max_point().row().saturating_sub(1) as f32
+            display_map.max_point().row() as f32
         };
         if scroll_position.y() > max_scroll_top {
             scroll_position.set_y(max_scroll_top);

crates/editor/src/element.rs 🔗

@@ -916,36 +916,30 @@ impl EditorElement {
 
         let view = self.view.clone();
         let style = &self.style.theme.scrollbar;
-        let min_thumb_height =
-            style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size);
 
         let top = bounds.min_y();
         let bottom = bounds.max_y();
         let right = bounds.max_x();
         let left = right - style.width;
-        let height = bounds.height();
         let row_range = &layout.scrollbar_row_range;
-        let max_row = layout.max_row + ((row_range.end - row_range.start) as u32);
-        let scrollbar_start = row_range.start as f32 / max_row as f32;
-        let scrollbar_end = row_range.end as f32 / max_row as f32;
-
-        let mut thumb_top = top + scrollbar_start * height;
-        let mut thumb_bottom = top + scrollbar_end * height;
-        let thumb_center = (thumb_top + thumb_bottom) / 2.0;
-
-        if thumb_bottom - thumb_top < min_thumb_height {
-            thumb_top = thumb_center - min_thumb_height / 2.0;
-            thumb_bottom = thumb_center + min_thumb_height / 2.0;
-            if thumb_top < top {
-                thumb_top = top;
-                thumb_bottom = top + min_thumb_height;
-            }
-            if thumb_bottom > bottom {
-                thumb_bottom = bottom;
-                thumb_top = bottom - min_thumb_height;
-            }
+        let max_row = layout.max_row as f32 + (row_range.end - row_range.start);
+
+        let mut height = bounds.height();
+        let mut first_row_y_offset = 0.0;
+
+        // Impose a minimum height on the scrollbar thumb
+        let min_thumb_height =
+            style.min_height_factor * cx.font_cache.line_height(self.style.text.font_size);
+        let thumb_height = (row_range.end - row_range.start) * height / max_row;
+        if thumb_height < min_thumb_height {
+            first_row_y_offset = (min_thumb_height - thumb_height) / 2.0;
+            height -= min_thumb_height - thumb_height;
         }
 
+        let y_for_row = |row: f32| -> f32 { top + first_row_y_offset + row * height / max_row };
+
+        let thumb_top = y_for_row(row_range.start) - first_row_y_offset;
+        let thumb_bottom = y_for_row(row_range.end) + first_row_y_offset;
         let track_bounds = RectF::from_points(vec2f(left, top), vec2f(right, bottom));
         let thumb_bounds = RectF::from_points(vec2f(left, thumb_top), vec2f(right, thumb_bottom));
 
@@ -1723,7 +1717,7 @@ impl Element for EditorElement {
 
         let scroll_max = vec2f(
             ((scroll_width - text_size.x()) / em_width).max(0.0),
-            max_row.saturating_sub(1) as f32,
+            max_row as f32,
         );
 
         self.update_view(cx.app, |view, cx| {