3 layout::{Corner, Rect},
6 widgets::{Block, StatefulWidget, Widget},
8use std::iter::{self, Iterator};
9use unicode_width::UnicodeWidthStr;
11#[derive(Debug, Clone)]
14 selected: Option<usize>,
17impl Default for ListState {
18 fn default() -> ListState {
27 pub fn selected(&self) -> Option<usize> {
31 pub fn select(&mut self, index: Option<usize>) {
32 self.selected = index;
39#[derive(Debug, Clone, PartialEq)]
40pub struct ListItem<'a> {
45impl<'a> ListItem<'a> {
46 pub fn new<T>(content: T) -> ListItem<'a>
51 content: content.into(),
52 style: Style::default(),
56 pub fn style(mut self, style: Style) -> ListItem<'a> {
61 pub fn height(&self) -> usize {
66/// A widget to display several items among which one can be selected (optional)
71/// # use helix_tui::widgets::{Block, Borders, List, ListItem};
72/// # use helix_tui::style::{Style, Color, Modifier};
73/// let items = [ListItem::new("Item 1"), ListItem::new("Item 2"), ListItem::new("Item 3")];
75/// .block(Block::default().title("List").borders(Borders::ALL))
76/// .style(Style::default().fg(Color::White))
77/// .highlight_style(Style::default().add_modifier(Modifier::ITALIC))
78/// .highlight_symbol(">>");
80#[derive(Debug, Clone)]
82 block: Option<Block<'a>>,
83 items: Vec<ListItem<'a>>,
84 /// Style used as a base style for the widget
87 /// Style used to render selected item
88 highlight_style: Style,
89 /// Symbol in front of the selected item (Shift all items to the right)
90 highlight_symbol: Option<&'a str>,
94 pub fn new<T>(items: T) -> List<'a>
96 T: Into<Vec<ListItem<'a>>>,
100 style: Style::default(),
102 start_corner: Corner::TopLeft,
103 highlight_style: Style::default(),
104 highlight_symbol: None,
108 pub fn block(mut self, block: Block<'a>) -> List<'a> {
109 self.block = Some(block);
113 pub fn style(mut self, style: Style) -> List<'a> {
118 pub fn highlight_symbol(mut self, highlight_symbol: &'a str) -> List<'a> {
119 self.highlight_symbol = Some(highlight_symbol);
123 pub fn highlight_style(mut self, style: Style) -> List<'a> {
124 self.highlight_style = style;
128 pub fn start_corner(mut self, corner: Corner) -> List<'a> {
129 self.start_corner = corner;
134impl<'a> StatefulWidget for List<'a> {
135 type State = ListState;
137 fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
138 buf.set_style(area, self.style);
139 let list_area = match self.block.take() {
141 let inner_area = b.inner(area);
148 if list_area.width < 1 || list_area.height < 1 {
152 if self.items.is_empty() {
155 let list_height = list_area.height as usize;
157 let mut start = state.offset;
158 let mut end = state.offset;
160 for item in self.items.iter().skip(state.offset) {
161 if height + item.height() > list_height {
164 height += item.height();
168 let selected = state.selected.unwrap_or(0).min(self.items.len() - 1);
169 while selected >= end {
170 height = height.saturating_add(self.items[end].height());
172 while height > list_height {
173 height = height.saturating_sub(self.items[start].height());
177 while selected < start {
179 height = height.saturating_add(self.items[start].height());
180 while height > list_height {
182 height = height.saturating_sub(self.items[end].height());
185 state.offset = start;
187 let highlight_symbol = self.highlight_symbol.unwrap_or("");
188 let blank_symbol = iter::repeat(" ")
189 .take(highlight_symbol.width())
190 .collect::<String>();
192 let mut current_height = 0;
193 let has_selection = state.selected.is_some();
194 for (i, item) in self
201 let (x, y) = match self.start_corner {
202 Corner::BottomLeft => {
203 current_height += item.height() as u16;
204 (list_area.left(), list_area.bottom() - current_height)
207 let pos = (list_area.left(), list_area.top() + current_height);
208 current_height += item.height() as u16;
215 width: list_area.width,
216 height: item.height() as u16,
218 let item_style = self.style.patch(item.style);
219 buf.set_style(area, item_style);
221 let is_selected = state.selected.map(|s| s == i).unwrap_or(false);
222 let elem_x = if has_selection {
223 let symbol = if is_selected {
228 let (x, _) = buf.set_stringn(x, y, symbol, list_area.width as usize, item_style);
233 let max_element_width = (list_area.width - (elem_x - x)) as usize;
234 for (j, line) in item.content.lines.iter().enumerate() {
235 buf.set_spans(elem_x, y + j as u16, line, max_element_width as u16);
238 buf.set_style(area, self.highlight_style);
244impl<'a> Widget for List<'a> {
245 fn render(self, area: Rect, buf: &mut Buffer) {
246 let mut state = ListState::default();
247 StatefulWidget::render(self, area, buf, &mut state);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list