Skip to content
Merged
32 changes: 21 additions & 11 deletions src/menu/columnar_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ pub struct ColumnarMenu {
col_pos: u16,
/// row position in the menu. Starts from 0
row_pos: u16,
/// Number of values that are skipped when printing,
/// depending on selected value and terminal height
skip_values: u16,
/// Event sent to the menu
event: Option<MenuEvent>,
/// Longest suggestion found in the values
Expand All @@ -82,6 +85,7 @@ impl Default for ColumnarMenu {
values: Vec::new(),
col_pos: 0,
row_pos: 0,
skip_values: 0,
event: None,
longest_suggestion: 0,
input: None,
Expand Down Expand Up @@ -619,6 +623,21 @@ impl Menu for ColumnarMenu {
self.working_details.columns = possible_cols;
}
}

let available_lines = painter.remaining_lines();

let first_visible_row = self.skip_values / self.get_cols();

self.skip_values = if self.row_pos <= first_visible_row {
// Selection is above the visible area, scroll up
self.row_pos * self.get_cols()
} else if self.row_pos >= first_visible_row + available_lines {
// Selection is below the visible area, scroll down
(self.row_pos.saturating_sub(available_lines) + 1) * self.get_cols()
} else {
// Selection is within the visible area
self.skip_values
};
}
}

Expand All @@ -645,27 +664,18 @@ impl Menu for ColumnarMenu {
if self.get_values().is_empty() {
self.no_records_msg(use_ansi_coloring)
} else {
// The skip values represent the number of lines that should be skipped
// while printing the menu
let skip_values = if self.row_pos >= available_lines {
let skip_lines = self.row_pos.saturating_sub(available_lines) + 1;
(skip_lines * self.get_cols()) as usize
} else {
0
};

// It seems that crossterm prefers to have a complete string ready to be printed
// rather than looping through the values and printing multiple things
// This reduces the flickering when printing the menu
let available_values = (available_lines * self.get_cols()) as usize;
self.get_values()
.iter()
.skip(skip_values)
.skip(self.skip_values as usize)
.take(available_values)
.enumerate()
.map(|(index, suggestion)| {
// Correcting the enumerate index based on the number of skipped values
let index = index + skip_values;
let index = index + self.skip_values as usize;
let column = index as u16 % self.get_cols();
let empty_space = self.get_width().saturating_sub(suggestion.value.width());

Expand Down
33 changes: 22 additions & 11 deletions src/menu/ide_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ pub struct IdeMenu {
values: Vec<Suggestion>,
/// Selected value. Starts at 0
selected: u16,
/// Number of values that are skipped when printing,
/// depending on selected value and terminal height
skip_values: u16,
/// Event sent to the menu
event: Option<MenuEvent>,
/// Longest suggestion found in the values
Expand All @@ -161,6 +164,7 @@ impl Default for IdeMenu {
working_details: IdeMenuDetails::default(),
values: Vec::new(),
selected: 0,
skip_values: 0,
event: None,
longest_suggestion: 0,
input: None,
Expand Down Expand Up @@ -807,6 +811,23 @@ impl Menu for IdeMenu {

self.working_details.space_left = space_left;
self.working_details.space_right = space_right;

let available_lines = painter
.remaining_lines()
.min(self.default_details.max_completion_height);

let visible_items = available_lines.saturating_sub(border_width);

self.skip_values = if self.selected <= self.skip_values {
// Selection is above the visible area
self.selected
} else if self.selected >= self.skip_values + visible_items {
// Selection is below the visible area
self.selected.saturating_sub(visible_items) + 1
} else {
// Selection is within the visible area
self.skip_values
}
}
}

Expand Down Expand Up @@ -840,17 +861,7 @@ impl Menu for IdeMenu {
};

let available_lines = available_lines.min(self.default_details.max_completion_height);
// The skip values represent the number of lines that should be skipped
// while printing the menu
let skip_values = if self.selected >= available_lines.saturating_sub(border_width) {
let skip_lines = self
.selected
.saturating_sub(available_lines.saturating_sub(border_width))
+ 1;
skip_lines as usize
} else {
0
};
let skip_values = self.skip_values as usize;

let available_values = available_lines.saturating_sub(border_width) as usize;

Expand Down
12 changes: 10 additions & 2 deletions src/painting/painter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ pub struct Painter {
// Stdout
stdout: W,
prompt_start_row: u16,
// The number of lines that the prompt takes up
prompt_height: u16,
terminal_size: (u16, u16),
last_required_lines: u16,
large_buffer: bool,
Expand All @@ -103,6 +105,7 @@ impl Painter {
Painter {
stdout,
prompt_start_row: 0,
prompt_height: 0,
terminal_size: (0, 0),
last_required_lines: 0,
large_buffer: false,
Expand All @@ -123,7 +126,9 @@ impl Painter {

/// Returns the available lines from the prompt down
pub fn remaining_lines(&self) -> u16 {
self.screen_height().saturating_sub(self.prompt_start_row)
self.screen_height()
.saturating_sub(self.prompt_start_row)
.saturating_sub(self.prompt_height)
}

/// Returns the state necessary before suspending the painter (to run a host command event).
Expand Down Expand Up @@ -199,6 +204,9 @@ impl Painter {
let screen_width = self.screen_width();
let screen_height = self.screen_height();

// We add one here as [`PromptLines::prompt_lines_with_wrap`] intentionally subtracts 1 from the real value.
self.prompt_height = lines.prompt_lines_with_wrap(screen_width) + 1;

// Handle resize for multi line prompt
if self.just_resized {
self.prompt_start_row = self.prompt_start_row.saturating_sub(
Expand All @@ -209,7 +217,7 @@ impl Painter {
}

// Lines and distance parameters
let remaining_lines = self.remaining_lines();
let remaining_lines = self.remaining_lines() + self.prompt_height;
let required_lines = lines.required_lines(screen_width, menu);

// Marking the painter state as larger buffer to avoid animations
Expand Down