1use tui::{
2 buffer::Buffer,
3 layout::Rect,
4 text::{Line, Span, Text},
5 widgets::{Block, Paragraph, Widget},
6};
7
8#[derive(Default)]
9pub struct List {
10 pub offset: usize,
12}
13
14impl List {
15 fn list_offset_for(&self, entry_in_view: Option<usize>, height: usize) -> usize {
16 match entry_in_view {
17 Some(pos) => match height as usize {
18 h if (self.offset + h).saturating_sub(1) < pos => pos - h + 1,
19 _ if self.offset > pos => pos,
20 _ => self.offset,
21 },
22 None => 0,
23 }
24 }
25}
26
27#[derive(Default)]
28pub struct ListProps<'b> {
29 pub block: Option<Block<'b>>,
30 pub entry_in_view: Option<usize>,
31}
32
33impl List {
34 pub fn render<'a, 't>(
35 &mut self,
36 props: ListProps<'a>,
37 items: impl IntoIterator<Item = Vec<Span<'t>>>,
38 area: Rect,
39 buf: &mut Buffer,
40 ) {
41 let ListProps {
42 block,
43 entry_in_view,
44 } = props;
45
46 let list_area = match block {
47 Some(b) => {
48 let inner_area = b.inner(area);
49 b.render(area, buf);
50 inner_area
51 }
52 None => area,
53 };
54 self.offset = self.list_offset_for(entry_in_view, list_area.height as usize);
55
56 if list_area.width < 1 || list_area.height < 1 {
57 return;
58 }
59
60 for (i, vec_of_spans) in items
61 .into_iter()
62 .skip(self.offset)
63 .enumerate()
64 .take(list_area.height as usize)
65 {
66 let (x, y) = (list_area.left(), list_area.top() + i as u16);
67 Paragraph::new(Text::from(Line::from(vec_of_spans))).render(
68 Rect {
69 x,
70 y,
71 width: list_area.width,
72 height: 1,
73 },
74 buf,
75 );
76 }
77 }
78}