on master help sign in
Popup scrollbar (#4449)
77be98cManos Mertzianis
helix-term/src/commands/lsp.rs+1−1
@@ -598,7 +598,7 @@ pub fn code_action(cx: &mut Context) {
598 });
598 });
599 picker.move_down(); // pre-select the first item
599 picker.move_down(); // pre-select the first item
600
600
601− let popup = Popup::new("code-action", picker);
601+ let popup = Popup::new("code-action", picker).with_scrollbar(false);
602 compositor.replace_or_push("code-action", popup);
602 compositor.replace_or_push("code-action", popup);
603 },
603 },
604 )
604 )
helix-term/src/ui/completion.rs+1−1
@@ -227,7 +227,7 @@ impl Completion {
227 }
227 }
228 };
228 };
229 });
229 });
230− let popup = Popup::new(Self::ID, menu);
230+ let popup = Popup::new(Self::ID, menu).with_scrollbar(false);
231 let mut completion = Self {
231 let mut completion = Self {
232 popup,
232 popup,
233 start_offset,
233 start_offset,
helix-term/src/ui/menu.rs+15−16
@@ -320,11 +320,6 @@ impl<T: Item + 'static> Component for Menu<T> {
320 (a + b - 1) / b
320 (a + b - 1) / b
321 }
321 }
322
322
323− let scroll_height = std::cmp::min(div_ceil(win_height.pow(2), len), win_height as usize);
324−
325− let scroll_line = (win_height - scroll_height) * scroll
326− / std::cmp::max(1, len.saturating_sub(win_height));
327−
328 let rows = options.iter().map(|option| option.row(&self.editor_data));
323 let rows = options.iter().map(|option| option.row(&self.editor_data));
329 let table = Table::new(rows)
324 let table = Table::new(rows)
330 .style(style)
325 .style(style)
@@ -357,20 +352,24 @@ impl<T: Item + 'static> Component for Menu<T> {
357 let fits = len <= win_height;
352 let fits = len <= win_height;
358
353
359 let scroll_style = theme.get("ui.menu.scroll");
354 let scroll_style = theme.get("ui.menu.scroll");
360− for (i, _) in (scroll..(scroll + win_height).min(len)).enumerate() {
355+ if !fits {
361− let cell = &mut surface[(area.x + area.width - 1, area.y + i as u16)];
356+ let scroll_height = div_ceil(win_height.pow(2), len).min(win_height);
357+ let scroll_line = (win_height - scroll_height) * scroll
358+ / std::cmp::max(1, len.saturating_sub(win_height));
362
359
363− if !fits {
360+ let mut cell;
364− // Draw scroll track
361+ for i in 0..win_height {
365− cell.set_symbol("▐"); // right half block
362+ cell = &mut surface[(area.right() - 1, area.top() + i as u16)];
366− cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
367− }
368
363
369− let is_marked = i >= scroll_line && i < scroll_line + scroll_height;
364+ cell.set_symbol("▐"); // right half block
370
365
371− if !fits && is_marked {
366+ if scroll_line <= i && i < scroll_line + scroll_height {
372− // Draw scroll thumb
367+ // Draw scroll thumb
373− cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
368+ cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
369+ } else {
370+ // Draw scroll track
371+ cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
372+ }
374 }
373 }
375 }
374 }
376 }
375 }
helix-term/src/ui/popup.rs+44
@@ -22,6 +22,7 @@ pub struct Popup<T: Component> {
22 auto_close: bool,
22 auto_close: bool,
23 ignore_escape_key: bool,
23 ignore_escape_key: bool,
24 id: &'static str,
24 id: &'static str,
25+ has_scrollbar: bool,
25}
26}
26
27
27impl<T: Component> Popup<T> {
28impl<T: Component> Popup<T> {
@@ -37,6 +38,7 @@ impl<T: Component> Popup<T> {
37 auto_close: false,
38 auto_close: false,
38 ignore_escape_key: false,
39 ignore_escape_key: false,
39 id,
40 id,
41+ has_scrollbar: true,
40 }
42 }
41 }
43 }
42
44
@@ -128,6 +130,14 @@ impl<T: Component> Popup<T> {
128 }
130 }
129 }
131 }
130
132
133+ /// Toggles the Popup's scrollbar.
134+ /// Consider disabling the scrollbar in case the child
135+ /// already has its own.
136+ pub fn with_scrollbar(mut self, enable_scrollbar: bool) -> Self {
137+ self.has_scrollbar = enable_scrollbar;
138+ self
139+ }
140+
131 pub fn contents(&self) -> &T {
141 pub fn contents(&self) -> &T {
132 &self.contents
142 &self.contents
133 }
143 }
@@ -228,6 +238,40 @@ impl<T: Component> Component for Popup<T> {
228
238
229 let inner = area.inner(&self.margin);
239 let inner = area.inner(&self.margin);
230 self.contents.render(inner, surface, cx);
240 self.contents.render(inner, surface, cx);
241+
242+ // render scrollbar if contents do not fit
243+ if self.has_scrollbar {
244+ let win_height = inner.height as usize;
245+ let len = self.child_size.1 as usize;
246+ let fits = len <= win_height;
247+ let scroll = self.scroll;
248+ let scroll_style = cx.editor.theme.get("ui.menu.scroll");
249+
250+ const fn div_ceil(a: usize, b: usize) -> usize {
251+ (a + b - 1) / b
252+ }
253+
254+ if !fits {
255+ let scroll_height = div_ceil(win_height.pow(2), len).min(win_height);
256+ let scroll_line = (win_height - scroll_height) * scroll
257+ / std::cmp::max(1, len.saturating_sub(win_height));
258+
259+ let mut cell;
260+ for i in 0..win_height {
261+ cell = &mut surface[(inner.right() - 1, inner.top() + i as u16)];
262+
263+ cell.set_symbol("▐"); // right half block
264+
265+ if scroll_line <= i && i < scroll_line + scroll_height {
266+ // Draw scroll thumb
267+ cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
268+ } else {
269+ // Draw scroll track
270+ cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
271+ }
272+ }
273+ }
274+ }
231 }
275 }
232
276
233 fn id(&self) -> Option<&'static str> {
277 fn id(&self) -> Option<&'static str> {