Skip to content

Commit

Permalink
Popup scrollbar (helix-editor#4449)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Manosmer authored and Frederik Vestre committed Feb 6, 2023
1 parent 19809d7 commit 14bf510
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 18 deletions.
2 changes: 1 addition & 1 deletion helix-term/src/commands/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ pub fn code_action(cx: &mut Context) {
});
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);
},
)
Expand Down
2 changes: 1 addition & 1 deletion helix-term/src/ui/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ impl Completion {
}
};
});
let popup = Popup::new(Self::ID, menu);
let popup = Popup::new(Self::ID, menu).with_scrollbar(false);
let mut completion = Self {
popup,
start_offset,
Expand Down
31 changes: 15 additions & 16 deletions helix-term/src/ui/menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,11 +320,6 @@ impl<T: Item + 'static> Component for Menu<T> {
(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)
Expand Down Expand Up @@ -357,20 +352,24 @@ impl<T: Item + 'static> Component for Menu<T> {
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
cell.set_symbol("▐"); // right half block
cell.set_fg(scroll_style.bg.unwrap_or(helix_view::theme::Color::Reset));
}
let mut cell;
for i in 0..win_height {
cell = &mut surface[(area.right() - 1, area.top() + i as u16)];

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

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));
}
}
}
}
Expand Down
44 changes: 44 additions & 0 deletions helix-term/src/ui/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct Popup<T: Component> {
auto_close: bool,
ignore_escape_key: bool,
id: &'static str,
has_scrollbar: bool,
}

impl<T: Component> Popup<T> {
Expand All @@ -37,6 +38,7 @@ impl<T: Component> Popup<T> {
auto_close: false,
ignore_escape_key: false,
id,
has_scrollbar: true,
}
}

Expand Down Expand Up @@ -128,6 +130,14 @@ impl<T: Component> Popup<T> {
}
}

/// 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
}
Expand Down Expand Up @@ -228,6 +238,40 @@ impl<T: Component> Component for Popup<T> {

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> {
Expand Down

0 comments on commit 14bf510

Please sign in to comment.