diff --git a/candid_tui/Cargo.toml b/candid_tui/Cargo.toml index bd035af..4728e42 100644 --- a/candid_tui/Cargo.toml +++ b/candid_tui/Cargo.toml @@ -14,3 +14,5 @@ publish = false [dependencies] candid_client = "^0.1" clap = "^2.32" +tui = "^0.5" +termion = "^1.5" diff --git a/candid_tui/src/app.rs b/candid_tui/src/app.rs new file mode 100644 index 0000000..8f5304d --- /dev/null +++ b/candid_tui/src/app.rs @@ -0,0 +1,21 @@ +use crate::util::ListState; + +use candid_client::*; + +pub struct AppState<'a> { + pub title: &'a str, + pub should_quit: bool, + pub server: CandidConnection, + pub frame_history: ListState<&'a str>, +} + +impl<'a> AppState<'a> { + pub fn new(title: &'a str, server: CandidConnection) -> AppState<'a> { + AppState { + title, + should_quit: false, + server, + frame_history: ListState::new(Vec::new()), + } + } +} diff --git a/candid_tui/src/main.rs b/candid_tui/src/main.rs index 33909b1..99ea1dc 100644 --- a/candid_tui/src/main.rs +++ b/candid_tui/src/main.rs @@ -1,7 +1,21 @@ +mod app; +mod ui; +mod util; + use candid_client::*; +use crate::app::AppState; + +use std::io; + use clap::{App, Arg}; +use termion::raw::IntoRawMode; +use termion::screen::AlternateScreen; + +use tui::backend::TermionBackend; +use tui::Terminal; + fn main() { let matches = App::new("CANdid Client") .version("0.1.0") @@ -18,10 +32,22 @@ fn main() { let addr = matches.value_of("addr").unwrap(); - let client = CandidConnection::new(addr).expect("Couldn't connect to server"); + let server = CandidConnection::new(addr).expect("Couldn't connect to server"); + + // Initialize tui + let stdout = io::stdout().into_raw_mode().unwrap(); + let stdout = AlternateScreen::from(stdout); + let backend = TermionBackend::new(stdout); + let mut terminal = Terminal::new(backend).unwrap(); + terminal.hide_cursor().unwrap(); - for line in client.lines() { - let line = line.unwrap(); - println!("Received: \"{}\"", line); - } + // Initialize the app state + let mut app = AppState::new("CANdid TUI", server); + app.frame_history.push("frame 1"); + app.frame_history.push("frame 2"); + app.frame_history.push("frame 3"); + app.frame_history.push("frame 4"); + app.frame_history.push("frame 5"); + ui::draw(&mut terminal, &app).unwrap(); + std::thread::sleep(std::time::Duration::from_millis(1000)); } diff --git a/candid_tui/src/ui.rs b/candid_tui/src/ui.rs new file mode 100644 index 0000000..b5e1e17 --- /dev/null +++ b/candid_tui/src/ui.rs @@ -0,0 +1,22 @@ +use crate::app::AppState; + +use std::io; + +use tui::backend::Backend; +use tui::style::{Color, Modifier, Style}; +use tui::widgets::{Block, Borders, SelectableList, Widget}; +use tui::Terminal; + +pub fn draw(terminal: &mut Terminal, app: &AppState) -> Result<(), io::Error> { + terminal.draw(|mut f| { + let size = f.size(); + // Box, size of the terminal, all borders, title from app state + SelectableList::default() + .block(Block::default().borders(Borders::ALL).title(app.title)) + .items(&app.frame_history.items) + .select(Some(app.frame_history.selected)) + //.highlight_style(Style::default().fg(Color::Yellow).modifier(Modifier::BOLD)) + .highlight_symbol(">") + .render(&mut f, size) + }) +} diff --git a/candid_tui/src/util.rs b/candid_tui/src/util.rs new file mode 100644 index 0000000..dfb1db4 --- /dev/null +++ b/candid_tui/src/util.rs @@ -0,0 +1,26 @@ +pub struct ListState { + pub items: Vec, + pub selected: usize, +} + +impl ListState { + pub fn new(items: Vec) -> ListState { + ListState { items, selected: 0 } + } + + pub fn push(&mut self, item: I) { + self.items.push(item); + } + + pub fn previous(&mut self) { + if self.selected > 0 { + self.selected -= 1; + } + } + + pub fn next(&mut self) { + if self.selected < self.items.len() - 1 { + self.selected += 1 + } + } +}