Handle keys and clicks
The list never reads input itself. Your app reads its events, hands the list
the keys it should handle as the crate’s own Key, and asks it which row a
click landed on.
Keys in, steps out
Section titled “Keys in, steps out”List::key(key) moves the selection and answers with a Step:
| Step | When |
|---|---|
Moved |
the selection moved |
Held |
a list key that moved nothing, such as up on the first row |
Open(index) |
the open key, with the selected row’s index |
Pass |
a key that isn’t the list’s, left for your app |
The keys are a Keys set. Keys::new(), the default, is the arrows, page up
and page down, home, end and enter. Keys::VIM adds j and k, g and G
for the ends and ctrl+u and ctrl+d to page. Any part of a set can be replaced:
use pito_list::{Column, Key, Keys, List, ListView, Row, Step};use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
const OPEN: &[Key] = &[Key::Enter, Key::Char('o')];
let mut list = List::new() .keys(Keys::VIM.open(OPEN)) .with_rows((1..=40).map(|n| Row::new([format!("item {n}")])));let columns = [Column::new("Item", 8, 0)];let area = Rect::new(0, 0, 20, 10);ListView::new(&mut list, &columns).render(area, &mut Buffer::empty(area));
assert_eq!(list.key(Key::Char('k')), Step::Held);assert_eq!(list.key(Key::Char('j')), Step::Moved);assert_eq!(list.key(Key::Ctrl('d')), Step::Moved);assert_eq!(list.selected(), Some(10));assert_eq!(list.key(Key::Char('G')), Step::Moved);assert_eq!(list.key(Key::Char('o')), Step::Open(39));assert_eq!(list.key(Key::Char('x')), Step::Pass);Paging moves by the rows on screen less one, so the list needs one draw
before the page keys know how far to go; here the 10-line area pages by 9
rows. up, down, page_up, page_down, first and last do the same as
their keys, for an app that maps its own.
With the crossterm feature, Key::from(event) converts a crossterm
KeyEvent. Alt, Super, Meta or Hyper on a key that isn’t a character gives
Key::Other; Alt with a character stays apart as Key::Alt(c); Ctrl+Alt
with a character passes on as that character, which is how AltGr arrives on
some platforms, so diacritics can still be typed. A key release is
Key::Other.
Paging that keeps the view still
Section titled “Paging that keeps the view still”By default paging moves the selection and the view follows it. With
List::new().paging(Paging::View) the view moves by the same rows as the
selection, so the selected row stays on its line of the screen until the
list’s start or end stops the view. scroll_to(top) moves the view itself;
the next draw still keeps the selection in view.
Clicks
Section titled “Clicks”List::hit(area, header, column, row) names the row under a terminal cell,
following the scroll: give it the area the list was drawn in, whether the
header was drawn, and the mouse event’s column and row. It answers None
for the header, a section heading, a blank line, the end line, a cell below
the rows, the scrollbar’s column and the count’s line.
use pito_list::{Column, List, ListView, Row};use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
let mut list = List::new().with_rows((1..=40).map(|n| Row::new([format!("item {n}")])));let columns = [Column::new("Item", 8, 0)];let area = Rect::new(0, 2, 20, 10);list.select(25);ListView::new(&mut list, &columns).header(true).render(area, &mut Buffer::empty(Rect::new(0, 0, 20, 12)));
assert_eq!(list.top(), 17);assert_eq!(list.hit(area, true, 5, 2), None);assert_eq!(list.hit(area, true, 5, 3), Some(17));if let Some(index) = list.hit(area, true, 5, 6) { list.select(index);}assert_eq!(list.selected(), Some(20));The list was drawn from line 2, its header on that line, scrolled so that
row 25 is on screen: a click on line 3 is row 17, the first one shown, and a
click on line 6 selects row 20. hit answers for the last draw, so call it
with the area that draw used.