changeset 3776:570bfb897381

Popup scrollbar (#4449) * init * cargo fmt * optimisation of the scrollbar render both for Menu and Popup. Toggling off scrollbar for Popup<Menu>, since Menu has its own * rendering scroll track * removed unnecessary cast * improve memory allocation * small correction
author Manos Mertzianis <15073846+Manosmer@users.noreply.github.com>
date Tue, 15 Nov 2022 16:15:52 +0200
parents fa0ecc84598a
children be80057c58d2
files helix-term/src/commands/lsp.rs helix-term/src/ui/completion.rs helix-term/src/ui/menu.rs helix-term/src/ui/popup.rs
diffstat 4 files changed, 61 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/helix-term/src/commands/lsp.rs	Tue Nov 15 15:14:18 2022 +0100
+++ b/helix-term/src/commands/lsp.rs	Tue Nov 15 16:15:52 2022 +0200
@@ -598,7 +598,7 @@
             });
             picker.move_down(); // pre-select the first item
 
-            let popup = Popup::new("code-action", picker);
+            let popup = Popup::new("code-action", picker).with_scrollbar(false);
             compositor.replace_or_push("code-action", popup);
         },
     )
--- a/helix-term/src/ui/completion.rs	Tue Nov 15 15:14:18 2022 +0100
+++ b/helix-term/src/ui/completion.rs	Tue Nov 15 16:15:52 2022 +0200
@@ -227,7 +227,7 @@
                 }
             };
         });
-        let popup = Popup::new(Self::ID, menu);
+        let popup = Popup::new(Self::ID, menu).with_scrollbar(false);
         let mut completion = Self {
             popup,
             start_offset,
--- a/helix-term/src/ui/menu.rs	Tue Nov 15 15:14:18 2022 +0100
+++ b/helix-term/src/ui/menu.rs	Tue Nov 15 16:15:52 2022 +0200
@@ -320,11 +320,6 @@
             (a + b - 1) / b
         }
 
-        let scroll_height = std::cmp::min(div_ceil(win_height.pow(2), len), win_height as usize);
-
-        let scroll_line = (win_height - scroll_height) * scroll
-            / std::cmp::max(1, len.saturating_sub(win_height));
-
         let rows = options.iter().map(|option| option.row(&self.editor_data));
         let table = Table::new(rows)
             .style(style)
@@ -357,20 +352,24 @@
         let fits = len <= win_height;
 
         let scroll_style = theme.get("ui.menu.scroll");
-        for (i, _) in (scroll..(scroll + win_height).min(len)).enumerate() {
-            let cell = &mut surface[(area.x + area.width - 1, area.y + i as u16)];
+        if !fits {
+            let scroll_height = div_ceil(win_height.pow(2), len).min(win_height);
+            let scroll_line = (win_height - scroll_height) * scroll
+                / std::cmp::max(1, len.saturating_sub(win_height));
 
-            if !fits {
-                // Draw scroll track
+            let mut cell;
+            for i in 0..win_height {
+                cell = &mut surface[(area.right() - 1, area.top() + i as u16)];
+
                 cell.set_symbol("▐"); // right half block
-                cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
-            }
 
-            let is_marked = i >= scroll_line && i < scroll_line + scroll_height;
-
-            if !fits && is_marked {
-                // Draw scroll thumb
-                cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
+                if scroll_line <= i && i < scroll_line + scroll_height {
+                    // Draw scroll thumb
+                    cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
+                } else {
+                    // Draw scroll track
+                    cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
+                }
             }
         }
     }
--- a/helix-term/src/ui/popup.rs	Tue Nov 15 15:14:18 2022 +0100
+++ b/helix-term/src/ui/popup.rs	Tue Nov 15 16:15:52 2022 +0200
@@ -22,6 +22,7 @@
     auto_close: bool,
     ignore_escape_key: bool,
     id: &'static str,
+    has_scrollbar: bool,
 }
 
 impl<T: Component> Popup<T> {
@@ -37,6 +38,7 @@
             auto_close: false,
             ignore_escape_key: false,
             id,
+            has_scrollbar: true,
         }
     }
 
@@ -128,6 +130,14 @@
         }
     }
 
+    /// Toggles the Popup's scrollbar.
+    /// Consider disabling the scrollbar in case the child
+    /// already has its own.
+    pub fn with_scrollbar(mut self, enable_scrollbar: bool) -> Self {
+        self.has_scrollbar = enable_scrollbar;
+        self
+    }
+
     pub fn contents(&self) -> &T {
         &self.contents
     }
@@ -228,6 +238,40 @@
 
         let inner = area.inner(&self.margin);
         self.contents.render(inner, surface, cx);
+
+        // render scrollbar if contents do not fit
+        if self.has_scrollbar {
+            let win_height = inner.height as usize;
+            let len = self.child_size.1 as usize;
+            let fits = len <= win_height;
+            let scroll = self.scroll;
+            let scroll_style = cx.editor.theme.get("ui.menu.scroll");
+
+            const fn div_ceil(a: usize, b: usize) -> usize {
+                (a + b - 1) / b
+            }
+
+            if !fits {
+                let scroll_height = div_ceil(win_height.pow(2), len).min(win_height);
+                let scroll_line = (win_height - scroll_height) * scroll
+                    / std::cmp::max(1, len.saturating_sub(win_height));
+
+                let mut cell;
+                for i in 0..win_height {
+                    cell = &mut surface[(inner.right() - 1, inner.top() + i as u16)];
+
+                    cell.set_symbol("▐"); // right half block
+
+                    if scroll_line <= i && i < scroll_line + scroll_height {
+                        // Draw scroll thumb
+                        cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
+                    } else {
+                        // Draw scroll track
+                        cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
+                    }
+                }
+            }
+        }
     }
 
     fn id(&self) -> Option<&'static str> {