aboutsummaryrefslogtreecommitdiffstats
path: root/src/ratatui/widgets/list.rs
diff options
context:
space:
mode:
authorVladislav Stepanov <8uk.8ak@gmail.com>2023-04-14 23:18:58 +0400
committerGitHub <noreply@github.com>2023-04-14 20:18:58 +0100
commitc05d2850420a2c163b8f62c33a6cef7c0ae1ad8d (patch)
tree2c44a44eda7e76fa74e78ac1fd02f55c1ed4d804 /src/ratatui/widgets/list.rs
parentSwitch to uuidv7 (#864) (diff)
downloadatuin-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.rs268
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);
- }
-}