#[allow(dead_code)] mod util; use std::io; use itui::{ backend::TermionBackend, layout::{Constraint, Corner, Direction, Layout}, style::{Color, Modifier, Style}, widgets::{Block, Borders, List, SelectableList, Text, Widget}, Terminal, }; use termion::{event::Key, input::MouseTerminal, raw::IntoRawMode, screen::AlternateScreen}; use crate::util::event::{Event, Events}; struct App<'a> { items: Vec<&'a str>, selected: Option, 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 { terminal.draw(|mut f| { let chunks = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) .split(f.size()); let style = Style::default().fg(Color::Black).bg(Color::White); SelectableList::default() .block( Block::default() .borders(Borders::ALL) .title("List") .area(chunks[0]), ) .items(&app.items) .select(app.selected) .style(style) .highlight_style(style.fg(Color::LightGreen).modifier(Modifier::BOLD)) .highlight_symbol(">") .render(&mut f); { 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") .area(chunks[1]), ) .start_corner(Corner::BottomLeft) .render(&mut f); } })?; 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(()) }