parent
f840865597
commit
ec3f78b8cd
10 changed files with 187 additions and 11 deletions
@ -0,0 +1,82 @@ |
|||||||
|
mod state; |
||||||
|
mod ui; |
||||||
|
|
||||||
|
use anyhow::Result; |
||||||
|
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}; |
||||||
|
use crossterm::execute; |
||||||
|
use crossterm::terminal::{ |
||||||
|
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, |
||||||
|
}; |
||||||
|
use state::State; |
||||||
|
use std::panic::set_hook; |
||||||
|
use std::process::exit; |
||||||
|
use std::{ |
||||||
|
io::{stdout, Stdout}, |
||||||
|
sync::mpsc, |
||||||
|
thread, |
||||||
|
time::Duration, |
||||||
|
}; |
||||||
|
use tui::{backend::CrosstermBackend, Terminal}; |
||||||
|
|
||||||
|
type Frame<'f> = tui::Frame<'f, CrosstermBackend<Stdout>>; |
||||||
|
|
||||||
|
pub fn init_tui(token: String) -> Result<()> { |
||||||
|
//TODO: fix this
|
||||||
|
set_hook(Box::new(|p| { |
||||||
|
teardown().unwrap(); |
||||||
|
eprintln!("{}", p); |
||||||
|
exit(254); |
||||||
|
})); |
||||||
|
let mut state = State::new(token); |
||||||
|
if let Err(e) = init(&mut state) { |
||||||
|
teardown()?; |
||||||
|
return Err(e); |
||||||
|
} |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn init(state: &mut State) -> Result<()> { |
||||||
|
let mut stdout = stdout(); |
||||||
|
enable_raw_mode()?; |
||||||
|
execute!(&mut stdout, EnterAlternateScreen, EnableMouseCapture)?; |
||||||
|
|
||||||
|
let backend = CrosstermBackend::new(stdout); |
||||||
|
let mut terminal = Terminal::new(backend)?; |
||||||
|
let (tx, rx) = mpsc::channel(); |
||||||
|
|
||||||
|
thread::spawn(move || loop { |
||||||
|
if event::poll(Duration::from_millis(10)).unwrap() { |
||||||
|
match event::read().unwrap() { |
||||||
|
key @ Event::Key(_) => tx.send(key).unwrap(), |
||||||
|
_ => (), |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
terminal.clear()?; |
||||||
|
|
||||||
|
loop { |
||||||
|
terminal.draw(|f| ui::draw(f, state))?; |
||||||
|
match rx.recv()? { |
||||||
|
Event::Key(key) => match key.code { |
||||||
|
KeyCode::Esc => { |
||||||
|
teardown()?; |
||||||
|
terminal.show_cursor()?; |
||||||
|
break; |
||||||
|
} |
||||||
|
KeyCode::Left => state.prev_tab(), |
||||||
|
KeyCode::Right => state.next_tab(), |
||||||
|
_ => (), |
||||||
|
}, |
||||||
|
_ => unreachable!(), |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn teardown() -> Result<()> { |
||||||
|
disable_raw_mode()?; |
||||||
|
execute!(stdout(), LeaveAlternateScreen, DisableMouseCapture)?; |
||||||
|
eprintln!("teardown"); |
||||||
|
Ok(()) |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
use std::str::FromStr; |
||||||
|
use strum::VariantNames; |
||||||
|
|
||||||
|
#[derive(strum::Display, strum::EnumVariantNames, strum::EnumString)] |
||||||
|
pub enum UiTabs { |
||||||
|
Agents, |
||||||
|
Jobs, |
||||||
|
Map, |
||||||
|
} |
||||||
|
|
||||||
|
impl UiTabs { |
||||||
|
pub fn variants() -> &'static [&'static str] { |
||||||
|
Self::VARIANTS |
||||||
|
} |
||||||
|
|
||||||
|
pub fn index(&self) -> usize { |
||||||
|
let ss = self.to_string(); |
||||||
|
Self::VARIANTS.iter().position(|el| **el == ss).unwrap() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn next(&self) -> Self { |
||||||
|
let next_idx = (self.index() + 1) % Self::VARIANTS.len(); |
||||||
|
Self::from_str(Self::VARIANTS[next_idx]).unwrap() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn prev(&self) -> Self { |
||||||
|
let vlen = Self::VARIANTS.len(); |
||||||
|
let next_idx = (self.index() + vlen - 1) % vlen; |
||||||
|
Self::from_str(Self::VARIANTS[next_idx]).unwrap() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct State { |
||||||
|
pub token: String, |
||||||
|
pub active_tab: UiTabs, |
||||||
|
} |
||||||
|
|
||||||
|
impl State { |
||||||
|
pub fn new(token: String) -> Self { |
||||||
|
State { |
||||||
|
token, |
||||||
|
active_tab: UiTabs::Agents, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn next_tab(&mut self) { |
||||||
|
self.active_tab = self.active_tab.next() |
||||||
|
} |
||||||
|
|
||||||
|
pub fn prev_tab(&mut self) { |
||||||
|
self.active_tab = self.active_tab.prev() |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
use super::{ |
||||||
|
state::{State, UiTabs}, |
||||||
|
Frame, |
||||||
|
}; |
||||||
|
use tui::style::{Color, Style}; |
||||||
|
use tui::text::Spans; |
||||||
|
use tui::widgets::{Block, Borders, Tabs}; |
||||||
|
|
||||||
|
pub fn draw(f: &mut Frame, s: &mut State) { |
||||||
|
let titles = UiTabs::variants() |
||||||
|
.iter() |
||||||
|
.cloned() |
||||||
|
.map(Spans::from) |
||||||
|
.collect(); |
||||||
|
let tabs = Tabs::new(titles) |
||||||
|
.block( |
||||||
|
Block::default() |
||||||
|
.title("The whole that you need to know") |
||||||
|
.borders(Borders::ALL), |
||||||
|
) |
||||||
|
.style(Style::default().fg(Color::White)) |
||||||
|
.highlight_style(Style::default().fg(Color::Yellow)) |
||||||
|
.divider("-") |
||||||
|
.select(s.active_tab.index()); |
||||||
|
f.render_widget(tabs, f.size()) |
||||||
|
} |
Loading…
Reference in new issue