diff options
| author | Vladislav Stepanov <8uk.8ak@gmail.com> | 2023-04-14 23:18:58 +0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-14 20:18:58 +0100 |
| commit | c05d2850420a2c163b8f62c33a6cef7c0ae1ad8d (patch) | |
| tree | 2c44a44eda7e76fa74e78ac1fd02f55c1ed4d804 /src/ratatui/widgets/list.rs | |
| parent | Switch to uuidv7 (#864) (diff) | |
| download | atuin-c05d2850420a2c163b8f62c33a6cef7c0ae1ad8d.zip | |
Workspace reorder (#868)
* Try different workspace structure
Move main crate (atuin) to be on the same level with other crates in
this workspace
* extract common dependencies to the workspace definition
* fix base64 v0.21 deprecation warning
* questionable: update deps & fix chrono deprecations
possible panic sites are unchanged, they're just more visible now
* Revert "questionable: update deps & fix chrono deprecations"
This reverts commit 993e60f8dea81a1625a04285a617959ad09a0866.
Diffstat (limited to 'src/ratatui/widgets/list.rs')
| -rw-r--r-- | src/ratatui/widgets/list.rs | 268 |
1 files changed, 0 insertions, 268 deletions
diff --git a/src/ratatui/widgets/list.rs b/src/ratatui/widgets/list.rs deleted file mode 100644 index 9608d299..00000000 --- a/src/ratatui/widgets/list.rs +++ /dev/null @@ -1,268 +0,0 @@ -use crate::ratatui::{ - buffer::Buffer, - layout::{Corner, Rect}, - style::Style, - text::Text, - widgets::{Block, StatefulWidget, Widget}, -}; -use unicode_width::UnicodeWidthStr; - -#[derive(Debug, Clone, Default)] -pub struct ListState { - offset: usize, - selected: Option<usize>, -} - -impl ListState { - pub fn selected(&self) -> Option<usize> { - self.selected - } - - pub fn select(&mut self, index: Option<usize>) { - self.selected = index; - if index.is_none() { - self.offset = 0; - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ListItem<'a> { - content: Text<'a>, - style: Style, -} - -impl<'a> ListItem<'a> { - pub fn new<T>(content: T) -> ListItem<'a> - where - T: Into<Text<'a>>, - { - ListItem { - content: content.into(), - style: Style::default(), - } - } - - pub fn style(mut self, style: Style) -> ListItem<'a> { - self.style = style; - self - } - - pub fn height(&self) -> usize { - self.content.height() - } - - pub fn width(&self) -> usize { - self.content.width() - } -} - -/// A widget to display several items among which one can be selected (optional) -/// -/// # Examples -/// -/// ``` -/// # use ratatui::widgets::{Block, Borders, List, ListItem}; -/// # use ratatui::style::{Style, Color, Modifier}; -/// let items = [ListItem::new("Item 1"), ListItem::new("Item 2"), ListItem::new("Item 3")]; -/// List::new(items) -/// .block(Block::default().title("List").borders(Borders::ALL)) -/// .style(Style::default().fg(Color::White)) -/// .highlight_style(Style::default().add_modifier(Modifier::ITALIC)) -/// .highlight_symbol(">>"); -/// ``` -#[derive(Debug, Clone)] -pub struct List<'a> { - block: Option<Block<'a>>, - items: Vec<ListItem<'a>>, - /// Style used as a base style for the widget - style: Style, - start_corner: Corner, - /// Style used to render selected item - highlight_style: Style, - /// Symbol in front of the selected item (Shift all items to the right) - highlight_symbol: Option<&'a str>, - /// Whether to repeat the highlight symbol for each line of the selected item - repeat_highlight_symbol: bool, -} - -impl<'a> List<'a> { - pub fn new<T>(items: T) -> List<'a> - where - T: Into<Vec<ListItem<'a>>>, - { - List { - block: None, - style: Style::default(), - items: items.into(), - start_corner: Corner::TopLeft, - highlight_style: Style::default(), - highlight_symbol: None, - repeat_highlight_symbol: false, - } - } - - pub fn block(mut self, block: Block<'a>) -> List<'a> { - self.block = Some(block); - self - } - - pub fn style(mut self, style: Style) -> List<'a> { - self.style = style; - self - } - - pub fn highlight_symbol(mut self, highlight_symbol: &'a str) -> List<'a> { - self.highlight_symbol = Some(highlight_symbol); - self - } - - pub fn highlight_style(mut self, style: Style) -> List<'a> { - self.highlight_style = style; - self - } - - pub fn repeat_highlight_symbol(mut self, repeat: bool) -> List<'a> { - self.repeat_highlight_symbol = repeat; - self - } - - pub fn start_corner(mut self, corner: Corner) -> List<'a> { - self.start_corner = corner; - self - } - - fn get_items_bounds( - &self, - selected: Option<usize>, - offset: usize, - max_height: usize, - ) -> (usize, usize) { - let offset = offset.min(self.items.len().saturating_sub(1)); - let mut start = offset; - let mut end = offset; - let mut height = 0; - for item in self.items.iter().skip(offset) { - if height + item.height() > max_height { - break; - } - height += item.height(); - end += 1; - } - - let selected = selected.unwrap_or(0).min(self.items.len() - 1); - while selected >= end { - height = height.saturating_add(self.items[end].height()); - end += 1; - while height > max_height { - height = height.saturating_sub(self.items[start].height()); - start += 1; - } - } - while selected < start { - start -= 1; - height = height.saturating_add(self.items[start].height()); - while height > max_height { - end -= 1; - height = height.saturating_sub(self.items[end].height()); - } - } - (start, end) - } -} - -impl<'a> StatefulWidget for List<'a> { - type State = ListState; - - fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { - buf.set_style(area, self.style); - let list_area = match self.block.take() { - Some(b) => { - let inner_area = b.inner(area); - b.render(area, buf); - inner_area - } - None => area, - }; - - if list_area.width < 1 || list_area.height < 1 { - return; - } - - if self.items.is_empty() { - return; - } - let list_height = list_area.height as usize; - - let (start, end) = self.get_items_bounds(state.selected, state.offset, list_height); - state.offset = start; - - let highlight_symbol = self.highlight_symbol.unwrap_or(""); - let blank_symbol = " ".repeat(highlight_symbol.width()); - - let mut current_height = 0; - let has_selection = state.selected.is_some(); - for (i, item) in self - .items - .iter_mut() - .enumerate() - .skip(state.offset) - .take(end - start) - { - let (x, y) = match self.start_corner { - Corner::BottomLeft => { - current_height += item.height() as u16; - (list_area.left(), list_area.bottom() - current_height) - } - _ => { - let pos = (list_area.left(), list_area.top() + current_height); - current_height += item.height() as u16; - pos - } - }; - let area = Rect { - x, - y, - width: list_area.width, - height: item.height() as u16, - }; - let item_style = self.style.patch(item.style); - buf.set_style(area, item_style); - - let is_selected = state.selected.map(|s| s == i).unwrap_or(false); - for (j, line) in item.content.lines.iter().enumerate() { - // if the item is selected, we need to display the highlight symbol: - // - either for the first line of the item only, - // - or for each line of the item if the appropriate option is set - let symbol = if is_selected && (j == 0 || self.repeat_highlight_symbol) { - highlight_symbol - } else { - &blank_symbol - }; - let (elem_x, max_element_width) = if has_selection { - let (elem_x, _) = buf.set_stringn( - x, - y + j as u16, - symbol, - list_area.width as usize, - item_style, - ); - (elem_x, (list_area.width - (elem_x - x))) - } else { - (x, list_area.width) - }; - buf.set_spans(elem_x, y + j as u16, line, max_element_width); - } - if is_selected { - buf.set_style(area, self.highlight_style); - } - } - } -} - -impl<'a> Widget for List<'a> { - fn render(self, area: Rect, buf: &mut Buffer) { - let mut state = ListState::default(); - StatefulWidget::render(self, area, buf, &mut state); - } -} |
