diff options
Diffstat (limited to 'vendor/dialoguer/src/paging.rs')
-rw-r--r-- | vendor/dialoguer/src/paging.rs | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/vendor/dialoguer/src/paging.rs b/vendor/dialoguer/src/paging.rs new file mode 100644 index 0000000..f85d9b3 --- /dev/null +++ b/vendor/dialoguer/src/paging.rs @@ -0,0 +1,118 @@ +use std::io; + +use console::Term; + +/// Creates a paging module +/// +/// The paging module serves as tracking structure to allow paged views +/// and automatically (de-)activates paging depending on the current terminal size. +pub struct Paging<'a> { + pub pages: usize, + pub current_page: usize, + pub capacity: usize, + pub active: bool, + pub max_capacity: Option<usize>, + term: &'a Term, + current_term_size: (u16, u16), + items_len: usize, + activity_transition: bool, +} + +impl<'a> Paging<'a> { + pub fn new(term: &'a Term, items_len: usize, max_capacity: Option<usize>) -> Paging<'a> { + let term_size = term.size(); + // Subtract -2 because we need space to render the prompt, if paging is active + let capacity = max_capacity + .unwrap_or(std::usize::MAX) + .clamp(3, term_size.0 as usize) + - 2; + let pages = (items_len as f64 / capacity as f64).ceil() as usize; + + Paging { + pages, + current_page: 0, + capacity, + active: pages > 1, + term, + current_term_size: term_size, + items_len, + max_capacity, + // Set transition initially to true to trigger prompt rendering for inactive paging on start + activity_transition: true, + } + } + + /// Updates all internal based on the current terminal size and cursor position + pub fn update(&mut self, cursor_pos: usize) -> io::Result<()> { + let new_term_size = self.term.size(); + + if self.current_term_size != new_term_size { + self.current_term_size = new_term_size; + self.capacity = self + .max_capacity + .unwrap_or(std::usize::MAX) + .clamp(3, self.current_term_size.0 as usize) + - 2; + self.pages = (self.items_len as f64 / self.capacity as f64).ceil() as usize; + } + + if self.active == (self.pages > 1) { + self.activity_transition = false; + } else { + self.active = self.pages > 1; + self.activity_transition = true; + // Clear everything to prevent "ghost" lines in terminal when a resize happened + self.term.clear_last_lines(self.capacity)?; + } + + if cursor_pos != !0 + && (cursor_pos < self.current_page * self.capacity + || cursor_pos >= (self.current_page + 1) * self.capacity) + { + self.current_page = cursor_pos / self.capacity; + } + + Ok(()) + } + + /// Renders a prompt when the following conditions are met: + /// * Paging is active + /// * Transition of the paging activity happened (active -> inactive / inactive -> active) + pub fn render_prompt<F>(&mut self, mut render_prompt: F) -> io::Result<()> + where + F: FnMut(Option<(usize, usize)>) -> io::Result<()>, + { + if self.active { + let paging_info = Some((self.current_page + 1, self.pages)); + render_prompt(paging_info)?; + } else if self.activity_transition { + render_prompt(None)?; + } + + self.term.flush()?; + + Ok(()) + } + + /// Navigates to the next page + pub fn next_page(&mut self) -> usize { + if self.current_page == self.pages - 1 { + self.current_page = 0; + } else { + self.current_page += 1; + } + + self.current_page * self.capacity + } + + /// Navigates to the previous page + pub fn previous_page(&mut self) -> usize { + if self.current_page == 0 { + self.current_page = self.pages - 1; + } else { + self.current_page -= 1; + } + + self.current_page * self.capacity + } +} |