mirror of
https://github.com/fdehau/tui-rs.git
synced 2024-10-30 21:20:22 +00:00
8cdfc883b9
It basically never makes sense to render without syncing the size. Without resizing, if shrinking, we get artefacts. If growing, we may get panics (before this change the Rustbox sample (the only one which didn't handle resizing on its own) panicked because the widget would get an updated size, while the terminal would not).
173 lines
5.5 KiB
Rust
173 lines
5.5 KiB
Rust
extern crate failure;
|
|
extern crate termion;
|
|
extern crate tui;
|
|
|
|
#[allow(dead_code)]
|
|
mod util;
|
|
|
|
use std::io;
|
|
|
|
use termion::event::Key;
|
|
use termion::input::MouseTerminal;
|
|
use termion::raw::IntoRawMode;
|
|
use termion::screen::AlternateScreen;
|
|
use tui::backend::TermionBackend;
|
|
use tui::layout::{Constraint, Corner, Direction, Layout};
|
|
use tui::style::{Color, Modifier, Style};
|
|
use tui::widgets::{Block, Borders, List, SelectableList, Text, Widget};
|
|
use tui::Terminal;
|
|
|
|
use util::event::{Event, Events};
|
|
|
|
struct App<'a> {
|
|
items: Vec<&'a str>,
|
|
selected: Option<usize>,
|
|
events: Vec<(&'a str, &'a str)>,
|
|
info_style: Style,
|
|
warning_style: Style,
|
|
error_style: Style,
|
|
critical_style: Style,
|
|
}
|
|
|
|
impl<'a> App<'a> {
|
|
fn new() -> App<'a> {
|
|
App {
|
|
items: vec![
|
|
"Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9",
|
|
"Item10", "Item11", "Item12", "Item13", "Item14", "Item15", "Item16", "Item17",
|
|
"Item18", "Item19", "Item20", "Item21", "Item22", "Item23", "Item24",
|
|
],
|
|
selected: None,
|
|
events: vec![
|
|
("Event1", "INFO"),
|
|
("Event2", "INFO"),
|
|
("Event3", "CRITICAL"),
|
|
("Event4", "ERROR"),
|
|
("Event5", "INFO"),
|
|
("Event6", "INFO"),
|
|
("Event7", "WARNING"),
|
|
("Event8", "INFO"),
|
|
("Event9", "INFO"),
|
|
("Event10", "INFO"),
|
|
("Event11", "CRITICAL"),
|
|
("Event12", "INFO"),
|
|
("Event13", "INFO"),
|
|
("Event14", "INFO"),
|
|
("Event15", "INFO"),
|
|
("Event16", "INFO"),
|
|
("Event17", "ERROR"),
|
|
("Event18", "ERROR"),
|
|
("Event19", "INFO"),
|
|
("Event20", "INFO"),
|
|
("Event21", "WARNING"),
|
|
("Event22", "INFO"),
|
|
("Event23", "INFO"),
|
|
("Event24", "WARNING"),
|
|
("Event25", "INFO"),
|
|
("Event26", "INFO"),
|
|
],
|
|
info_style: Style::default().fg(Color::White),
|
|
warning_style: Style::default().fg(Color::Yellow),
|
|
error_style: Style::default().fg(Color::Magenta),
|
|
critical_style: Style::default().fg(Color::Red),
|
|
}
|
|
}
|
|
|
|
fn advance(&mut self) {
|
|
let event = self.events.pop().unwrap();
|
|
self.events.insert(0, event);
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<(), failure::Error> {
|
|
// Terminal initialization
|
|
let stdout = io::stdout().into_raw_mode()?;
|
|
let stdout = MouseTerminal::from(stdout);
|
|
let stdout = AlternateScreen::from(stdout);
|
|
let backend = TermionBackend::new(stdout);
|
|
let mut terminal = Terminal::new(backend)?;
|
|
terminal.hide_cursor()?;
|
|
|
|
let events = Events::new();
|
|
|
|
// App
|
|
let mut app = App::new();
|
|
|
|
loop {
|
|
let size = terminal.size()?;
|
|
|
|
terminal.draw(|mut f| {
|
|
let chunks = Layout::default()
|
|
.direction(Direction::Horizontal)
|
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
|
.split(size);
|
|
|
|
let style = Style::default().fg(Color::Black).bg(Color::White);
|
|
SelectableList::default()
|
|
.block(Block::default().borders(Borders::ALL).title("List"))
|
|
.items(&app.items)
|
|
.select(app.selected)
|
|
.style(style)
|
|
.highlight_style(style.fg(Color::LightGreen).modifier(Modifier::Bold))
|
|
.highlight_symbol(">")
|
|
.render(&mut f, chunks[0]);
|
|
{
|
|
let events = app.events.iter().map(|&(evt, level)| {
|
|
Text::styled(
|
|
format!("{}: {}", level, evt),
|
|
match level {
|
|
"ERROR" => app.error_style,
|
|
"CRITICAL" => app.critical_style,
|
|
"WARNING" => app.warning_style,
|
|
_ => app.info_style,
|
|
},
|
|
)
|
|
});
|
|
List::new(events)
|
|
.block(Block::default().borders(Borders::ALL).title("List"))
|
|
.start_corner(Corner::BottomLeft)
|
|
.render(&mut f, chunks[1]);
|
|
}
|
|
})?;
|
|
|
|
match events.next()? {
|
|
Event::Input(input) => match input {
|
|
Key::Char('q') => {
|
|
break;
|
|
}
|
|
Key::Left => {
|
|
app.selected = None;
|
|
}
|
|
Key::Down => {
|
|
app.selected = if let Some(selected) = app.selected {
|
|
if selected >= app.items.len() - 1 {
|
|
Some(0)
|
|
} else {
|
|
Some(selected + 1)
|
|
}
|
|
} else {
|
|
Some(0)
|
|
}
|
|
}
|
|
Key::Up => {
|
|
app.selected = if let Some(selected) = app.selected {
|
|
if selected > 0 {
|
|
Some(selected - 1)
|
|
} else {
|
|
Some(app.items.len() - 1)
|
|
}
|
|
} else {
|
|
Some(0)
|
|
}
|
|
}
|
|
_ => {}
|
|
},
|
|
Event::Tick => {
|
|
app.advance();
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|