|
|
@ -33,6 +33,8 @@ pub struct Menu<T: Item> {
|
|
|
|
|
|
|
|
|
|
|
|
scroll: usize,
|
|
|
|
scroll: usize,
|
|
|
|
size: (u16, u16),
|
|
|
|
size: (u16, u16),
|
|
|
|
|
|
|
|
viewport: (u16, u16),
|
|
|
|
|
|
|
|
recalculate: bool,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<T: Item> Menu<T> {
|
|
|
|
impl<T: Item> Menu<T> {
|
|
|
@ -51,6 +53,8 @@ impl<T: Item> Menu<T> {
|
|
|
|
callback_fn: Box::new(callback_fn),
|
|
|
|
callback_fn: Box::new(callback_fn),
|
|
|
|
scroll: 0,
|
|
|
|
scroll: 0,
|
|
|
|
size: (0, 0),
|
|
|
|
size: (0, 0),
|
|
|
|
|
|
|
|
viewport: (0, 0),
|
|
|
|
|
|
|
|
recalculate: true,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: scoring on empty input should just use a fastpath
|
|
|
|
// TODO: scoring on empty input should just use a fastpath
|
|
|
@ -83,6 +87,7 @@ impl<T: Item> Menu<T> {
|
|
|
|
// reset cursor position
|
|
|
|
// reset cursor position
|
|
|
|
self.cursor = None;
|
|
|
|
self.cursor = None;
|
|
|
|
self.scroll = 0;
|
|
|
|
self.scroll = 0;
|
|
|
|
|
|
|
|
self.recalculate = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn move_up(&mut self) {
|
|
|
|
pub fn move_up(&mut self) {
|
|
|
@ -99,6 +104,41 @@ impl<T: Item> Menu<T> {
|
|
|
|
self.adjust_scroll();
|
|
|
|
self.adjust_scroll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn recalculate_size(&mut self, viewport: (u16, u16)) {
|
|
|
|
|
|
|
|
let n = self
|
|
|
|
|
|
|
|
.options
|
|
|
|
|
|
|
|
.first()
|
|
|
|
|
|
|
|
.map(|option| option.row().cells.len())
|
|
|
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
let max_lens = self.options.iter().fold(vec![0; n], |mut acc, option| {
|
|
|
|
|
|
|
|
let row = option.row();
|
|
|
|
|
|
|
|
// maintain max for each column
|
|
|
|
|
|
|
|
for (acc, cell) in acc.iter_mut().zip(row.cells.iter()) {
|
|
|
|
|
|
|
|
let width = cell.content.width();
|
|
|
|
|
|
|
|
if width > *acc {
|
|
|
|
|
|
|
|
*acc = width;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
acc
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
let len = max_lens.iter().sum::<usize>() + n + 1; // +1: reserve some space for scrollbar
|
|
|
|
|
|
|
|
let width = len.min(viewport.0 as usize);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.widths = max_lens
|
|
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
|
|
.map(|len| Constraint::Length(len as u16))
|
|
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let height = self.matches.len().min(10).min(viewport.1 as usize);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.size = (width as u16, height as u16);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// adjust scroll offsets if size changed
|
|
|
|
|
|
|
|
self.adjust_scroll();
|
|
|
|
|
|
|
|
self.recalculate = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn adjust_scroll(&mut self) {
|
|
|
|
fn adjust_scroll(&mut self) {
|
|
|
|
let win_height = self.size.1 as usize;
|
|
|
|
let win_height = self.size.1 as usize;
|
|
|
|
if let Some(cursor) = self.cursor {
|
|
|
|
if let Some(cursor) = self.cursor {
|
|
|
@ -221,43 +261,13 @@ impl<T: Item + 'static> Component for Menu<T> {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
|
|
|
|
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
|
|
|
|
let n = self
|
|
|
|
if viewport != self.viewport || self.recalculate {
|
|
|
|
.options
|
|
|
|
self.recalculate_size(viewport);
|
|
|
|
.first()
|
|
|
|
}
|
|
|
|
.map(|option| option.row().cells.len())
|
|
|
|
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
let max_lens = self.options.iter().fold(vec![0; n], |mut acc, option| {
|
|
|
|
|
|
|
|
let row = option.row();
|
|
|
|
|
|
|
|
// maintain max for each column
|
|
|
|
|
|
|
|
for (acc, cell) in acc.iter_mut().zip(row.cells.iter()) {
|
|
|
|
|
|
|
|
let width = cell.content.width();
|
|
|
|
|
|
|
|
if width > *acc {
|
|
|
|
|
|
|
|
*acc = width;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
acc
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
let len = max_lens.iter().sum::<usize>() + n + 1; // +1: reserve some space for scrollbar
|
|
|
|
|
|
|
|
let width = len.min(viewport.0 as usize);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.widths = max_lens
|
|
|
|
|
|
|
|
.into_iter()
|
|
|
|
|
|
|
|
.map(|len| Constraint::Length(len as u16))
|
|
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let height = self.options.len().min(10).min(viewport.1 as usize);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.size = (width as u16, height as u16);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// adjust scroll offsets if size changed
|
|
|
|
|
|
|
|
self.adjust_scroll();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Some(self.size)
|
|
|
|
Some(self.size)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: required size should re-trigger when we filter items so we can draw a smaller menu
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
|
|
|
fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
|
|
|
let theme = &cx.editor.theme;
|
|
|
|
let theme = &cx.editor.theme;
|
|
|
|
let style = theme
|
|
|
|
let style = theme
|
|
|
|