Skip to content

Commit 77be98c

Browse files
authored
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
1 parent 3b7760d commit 77be98c

File tree

4 files changed

+61
-18
lines changed

4 files changed

+61
-18
lines changed

helix-term/src/commands/lsp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ pub fn code_action(cx: &mut Context) {
598598
});
599599
picker.move_down(); // pre-select the first item
600600

601-
let popup = Popup::new("code-action", picker);
601+
let popup = Popup::new("code-action", picker).with_scrollbar(false);
602602
compositor.replace_or_push("code-action", popup);
603603
},
604604
)

helix-term/src/ui/completion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ impl Completion {
227227
}
228228
};
229229
});
230-
let popup = Popup::new(Self::ID, menu);
230+
let popup = Popup::new(Self::ID, menu).with_scrollbar(false);
231231
let mut completion = Self {
232232
popup,
233233
start_offset,

helix-term/src/ui/menu.rs

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,6 @@ impl<T: Item + 'static> Component for Menu<T> {
320320
(a + b - 1) / b
321321
}
322322

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-
328323
let rows = options.iter().map(|option| option.row(&self.editor_data));
329324
let table = Table::new(rows)
330325
.style(style)
@@ -357,20 +352,24 @@ impl<T: Item + 'static> Component for Menu<T> {
357352
let fits = len <= win_height;
358353

359354
let scroll_style = theme.get("ui.menu.scroll");
360-
for (i, _) in (scroll..(scroll + win_height).min(len)).enumerate() {
361-
let cell = &mut surface[(area.x + area.width - 1, area.y + i as u16)];
355+
if !fits {
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));
362359

363-
if !fits {
364-
// Draw scroll track
365-
cell.set_symbol("▐"); // right half block
366-
cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
367-
}
360+
let mut cell;
361+
for i in 0..win_height {
362+
cell = &mut surface[(area.right() - 1, area.top() + i as u16)];
368363

369-
let is_marked = i >= scroll_line && i < scroll_line + scroll_height;
364+
cell.set_symbol("▐"); // right half block
370365

371-
if !fits && is_marked {
372-
// Draw scroll thumb
373-
cell.set_fg(scroll_style.fg.unwrap_or(helix_view::theme::Color::Reset));
366+
if scroll_line <= i && i < scroll_line + scroll_height {
367+
// Draw scroll thumb
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+
}
374373
}
375374
}
376375
}

helix-term/src/ui/popup.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub struct Popup<T: Component> {
2222
auto_close: bool,
2323
ignore_escape_key: bool,
2424
id: &'static str,
25+
has_scrollbar: bool,
2526
}
2627

2728
impl<T: Component> Popup<T> {
@@ -37,6 +38,7 @@ impl<T: Component> Popup<T> {
3738
auto_close: false,
3839
ignore_escape_key: false,
3940
id,
41+
has_scrollbar: true,
4042
}
4143
}
4244

@@ -128,6 +130,14 @@ impl<T: Component> Popup<T> {
128130
}
129131
}
130132

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+
131141
pub fn contents(&self) -> &T {
132142
&self.contents
133143
}
@@ -228,6 +238,40 @@ impl<T: Component> Component for Popup<T> {
228238

229239
let inner = area.inner(&self.margin);
230240
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+
}
231275
}
232276

233277
fn id(&self) -> Option<&'static str> {

0 commit comments

Comments
 (0)