doc(examples): Add comments to "list" example and fix list direction (#425)

* Add docs to list example and fix list direction

* List example: review adjustments and typo fixes
This commit is contained in:
Arne Beer 2020-12-06 16:33:31 +01:00 committed by GitHub
parent efdd6bfb19
commit 77c6e106e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -16,6 +16,12 @@ use tui::{
Terminal, Terminal,
}; };
/// This struct holds the current state of the app. In particular, it has the `items` field which is a wrapper
/// around `ListState`. Keeping track of the items state let us render the associated widget with its state
/// and have access to features such as natural scrolling.
///
/// Check the event handling at the bottom to see how to change the state on incoming events.
/// Check the drawing logic for items on how to specify the highlighting style for selected items.
struct App<'a> { struct App<'a> {
items: StatefulList<(&'a str, usize)>, items: StatefulList<(&'a str, usize)>,
events: Vec<(&'a str, &'a str)>, events: Vec<(&'a str, &'a str)>,
@ -82,9 +88,11 @@ impl<'a> App<'a> {
} }
} }
/// Rotate through the event list.
/// This only exists to simulate some kind of "progress"
fn advance(&mut self) { fn advance(&mut self) {
let event = self.events.pop().unwrap(); let event = self.events.remove(0);
self.events.insert(0, event); self.events.push(event);
} }
} }
@ -98,16 +106,18 @@ fn main() -> Result<(), Box<dyn Error>> {
let events = Events::new(); let events = Events::new();
// App // Create a new app with some exapmle state
let mut app = App::new(); let mut app = App::new();
loop { loop {
terminal.draw(|f| { terminal.draw(|f| {
// Create two chunks with equal horizontal screen space
let chunks = Layout::default() let chunks = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
.split(f.size()); .split(f.size());
// Iterate through all elements in the `items` app and append some debug text to it.
let items: Vec<ListItem> = app let items: Vec<ListItem> = app
.items .items
.items .items
@ -123,6 +133,8 @@ fn main() -> Result<(), Box<dyn Error>> {
ListItem::new(lines).style(Style::default().fg(Color::Black).bg(Color::White)) ListItem::new(lines).style(Style::default().fg(Color::Black).bg(Color::White))
}) })
.collect(); .collect();
// Create a List from all list items and highlight the currently selected one
let items = List::new(items) let items = List::new(items)
.block(Block::default().borders(Borders::ALL).title("List")) .block(Block::default().borders(Borders::ALL).title("List"))
.highlight_style( .highlight_style(
@ -131,12 +143,18 @@ fn main() -> Result<(), Box<dyn Error>> {
.add_modifier(Modifier::BOLD), .add_modifier(Modifier::BOLD),
) )
.highlight_symbol(">> "); .highlight_symbol(">> ");
// We can now render the item list
f.render_stateful_widget(items, chunks[0], &mut app.items.state); f.render_stateful_widget(items, chunks[0], &mut app.items.state);
// Let's do the same for the events.
// The event list doesn't have any state and only displays the current state of the list.
let events: Vec<ListItem> = app let events: Vec<ListItem> = app
.events .events
.iter() .iter()
.map(|&(evt, level)| { .rev()
.map(|&(event, level)| {
// Colorcode the level depending on its type
let s = match level { let s = match level {
"CRITICAL" => Style::default().fg(Color::Red), "CRITICAL" => Style::default().fg(Color::Red),
"ERROR" => Style::default().fg(Color::Magenta), "ERROR" => Style::default().fg(Color::Magenta),
@ -144,6 +162,7 @@ fn main() -> Result<(), Box<dyn Error>> {
"INFO" => Style::default().fg(Color::Blue), "INFO" => Style::default().fg(Color::Blue),
_ => Style::default(), _ => Style::default(),
}; };
// Add a example datetime and apply proper spacing between them
let header = Spans::from(vec![ let header = Spans::from(vec![
Span::styled(format!("{:<9}", level), s), Span::styled(format!("{:<9}", level), s),
Span::raw(" "), Span::raw(" "),
@ -152,7 +171,14 @@ fn main() -> Result<(), Box<dyn Error>> {
Style::default().add_modifier(Modifier::ITALIC), Style::default().add_modifier(Modifier::ITALIC),
), ),
]); ]);
let log = Spans::from(vec![Span::raw(evt)]); // The event gets it's own line
let log = Spans::from(vec![Span::raw(event)]);
// Here several things happen:
// 1. Add a `---` spacing line above the final list entry
// 2. Add the Level + datetime
// 3. Add a spacer line
// 4. Add the actual event
ListItem::new(vec![ ListItem::new(vec![
Spans::from("-".repeat(chunks[1].width as usize)), Spans::from("-".repeat(chunks[1].width as usize)),
header, header,
@ -167,6 +193,10 @@ fn main() -> Result<(), Box<dyn Error>> {
f.render_widget(events_list, chunks[1]); f.render_widget(events_list, chunks[1]);
})?; })?;
// This is a simple example on how to handle events
// 1. This breaks the loop and exits the program on `q` button press.
// 2. The `up`/`down` keys change the currently selected item in the App's `items` list.
// 3. `left` unselects the current item.
match events.next()? { match events.next()? {
Event::Input(input) => match input { Event::Input(input) => match input {
Key::Char('q') => { Key::Char('q') => {