diff --git a/CHANGELOG.md b/CHANGELOG.md index cf4c07e..cd7a6df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## To be released +### Features +* Add option to `widgets::List` to repeat the hightlight symbol for each line of multi-line items. ## v0.16.0 - 2021-08-01 diff --git a/src/widgets/list.rs b/src/widgets/list.rs index 4753819..6fe06bd 100644 --- a/src/widgets/list.rs +++ b/src/widgets/list.rs @@ -87,6 +87,8 @@ pub struct List<'a> { 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> { @@ -101,6 +103,7 @@ impl<'a> List<'a> { start_corner: Corner::TopLeft, highlight_style: Style::default(), highlight_symbol: None, + repeat_highlight_symbol: false, } } @@ -123,6 +126,11 @@ impl<'a> 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; @@ -227,19 +235,21 @@ impl<'a> StatefulWidget for List<'a> { buf.set_style(area, item_style); let is_selected = state.selected.map(|s| s == i).unwrap_or(false); - let elem_x = if has_selection { - let symbol = if is_selected { + for (j, line) in item.content.lines.iter().enumerate() { + // if the item is selected, we need to display the hightlight 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 (x, _) = buf.set_stringn(x, y, symbol, list_area.width as usize, item_style); - x - } else { - x - }; - let max_element_width = (list_area.width - (elem_x - x)) as usize; - for (j, line) in item.content.lines.iter().enumerate() { + 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)) as u16) + } else { + (x, list_area.width) + }; buf.set_spans(elem_x, y + j as u16, line, max_element_width as u16); } if is_selected { diff --git a/tests/widgets_list.rs b/tests/widgets_list.rs index 1dd8fa8..945afba 100644 --- a/tests/widgets_list.rs +++ b/tests/widgets_list.rs @@ -126,3 +126,72 @@ fn widgets_list_should_clamp_offset_if_items_are_removed() { let expected = Buffer::with_lines(vec![" Item 3 ", " ", " ", " "]); terminal.backend().assert_buffer(&expected); } + +#[test] +fn widgets_list_should_display_multiline_items() { + let backend = TestBackend::new(10, 6); + let mut terminal = Terminal::new(backend).unwrap(); + let mut state = ListState::default(); + state.select(Some(1)); + terminal + .draw(|f| { + let size = f.size(); + let items = vec![ + ListItem::new(vec![Spans::from("Item 1"), Spans::from("Item 1a")]), + ListItem::new(vec![Spans::from("Item 2"), Spans::from("Item 2b")]), + ListItem::new(vec![Spans::from("Item 3"), Spans::from("Item 3c")]), + ]; + let list = List::new(items) + .highlight_style(Style::default().bg(Color::Yellow)) + .highlight_symbol(">> "); + f.render_stateful_widget(list, size, &mut state); + }) + .unwrap(); + let mut expected = Buffer::with_lines(vec![ + " Item 1 ", + " Item 1a", + ">> Item 2 ", + " Item 2b", + " Item 3 ", + " Item 3c"]); + for x in 0..10 { + expected.get_mut(x, 2).set_bg(Color::Yellow); + expected.get_mut(x, 3).set_bg(Color::Yellow); + } + terminal.backend().assert_buffer(&expected); +} + +#[test] +fn widgets_list_should_repeat_highlight_symbol() { + let backend = TestBackend::new(10, 6); + let mut terminal = Terminal::new(backend).unwrap(); + let mut state = ListState::default(); + state.select(Some(1)); + terminal + .draw(|f| { + let size = f.size(); + let items = vec![ + ListItem::new(vec![Spans::from("Item 1"), Spans::from("Item 1a")]), + ListItem::new(vec![Spans::from("Item 2"), Spans::from("Item 2b")]), + ListItem::new(vec![Spans::from("Item 3"), Spans::from("Item 3c")]), + ]; + let list = List::new(items) + .highlight_style(Style::default().bg(Color::Yellow)) + .highlight_symbol(">> ") + .repeat_highlight_symbol(true); + f.render_stateful_widget(list, size, &mut state); + }) + .unwrap(); + let mut expected = Buffer::with_lines(vec![ + " Item 1 ", + " Item 1a", + ">> Item 2 ", + ">> Item 2b", + " Item 3 ", + " Item 3c"]); + for x in 0..10 { + expected.get_mut(x, 2).set_bg(Color::Yellow); + expected.get_mut(x, 3).set_bg(Color::Yellow); + } + terminal.backend().assert_buffer(&expected); +}