new window manager

pull/1/head
plazmoid 3 years ago
parent c8dc747bcc
commit 638ae4da6e
  1. 1
      bin/u_panel/Cargo.toml
  2. 4
      bin/u_panel/src/argparse.rs
  3. 30
      bin/u_panel/src/tui/impls.rs
  4. 65
      bin/u_panel/src/tui/mod.rs
  5. 43
      bin/u_panel/src/tui/ui.rs
  6. 85
      bin/u_panel/src/tui/windows/main_wnd.rs
  7. 94
      bin/u_panel/src/tui/windows/mod.rs
  8. 0
      bin/u_panel/src/tui/windows/processing.rs

@ -7,6 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
backtrace = "0.3.61"
structopt = "0.3.21" structopt = "0.3.21"
log = "^0.4" log = "^0.4"
env_logger = "0.7.1" env_logger = "0.7.1"

@ -19,7 +19,7 @@ pub struct Args {
enum Cmd { enum Cmd {
Agents(LD), Agents(LD),
Jobs(JobALD), Jobs(JobALD),
Jobmap(JobMapALD), Map(JobMapALD),
TUI, TUI,
} }
@ -127,7 +127,7 @@ pub async fn process_cmd(args: Args) -> UResult<()> {
JobALD::LD(LD::List { uid }) => printer.print(CLIENT.get_jobs(uid).await), JobALD::LD(LD::List { uid }) => printer.print(CLIENT.get_jobs(uid).await),
JobALD::LD(LD::Delete { uid }) => printer.print(CLIENT.del(Some(uid)).await), JobALD::LD(LD::Delete { uid }) => printer.print(CLIENT.del(Some(uid)).await),
}, },
Cmd::Jobmap(action) => match action { Cmd::Map(action) => match action {
JobMapALD::Add { JobMapALD::Add {
agent_uid, agent_uid,
job_idents, job_idents,

@ -7,23 +7,19 @@ pub trait Id {
fn id(&self) -> Uuid; fn id(&self) -> Uuid;
} }
impl Id for Agent { #[macro_export]
fn id(&self) -> Uuid { macro_rules! impl_id {
self.id ($($type:ty),+) => {
} $(
} impl Id for $type {
fn id(&self) -> Uuid {
impl Id for JobMeta { self.id
fn id(&self) -> Uuid { }
self.id })+
} };
} }
impl Id for AssignedJob { impl_id!(Agent, JobMeta, AssignedJob);
fn id(&self) -> Uuid {
self.id
}
}
#[async_trait] #[async_trait]
pub trait CRUD: Id pub trait CRUD: Id

@ -1,14 +1,13 @@
mod impls; mod impls;
mod state; mod windows;
mod ui;
use anyhow::Result; use anyhow::Result as AResult;
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}; use backtrace::Backtrace;
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event};
use crossterm::execute; use crossterm::execute;
use crossterm::terminal::{ use crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
}; };
use state::State;
use std::panic::set_hook; use std::panic::set_hook;
use std::process::exit; use std::process::exit;
use std::{ use std::{
@ -18,36 +17,42 @@ use std::{
time::Duration, time::Duration,
}; };
use tui::{backend::CrosstermBackend, Terminal}; use tui::{backend::CrosstermBackend, Terminal};
use windows::{MainWnd, WindowsHandler};
type Frame<'f> = tui::Frame<'f, CrosstermBackend<Stdout>>; pub type Backend = CrosstermBackend<Stdout>;
pub type Frame<'f> = tui::Frame<'f, Backend>;
enum InputEvent<I> { enum InputEvent<I> {
Key(I), Key(I),
Tick, Tick,
} }
pub async fn init_tui() -> Result<()> { fn get_terminal() -> AResult<Terminal<Backend>> {
let backend = CrosstermBackend::new(stdout());
Ok(Terminal::new(backend)?)
}
pub async fn init_tui() -> AResult<()> {
//TODO: fix this //TODO: fix this
set_hook(Box::new(|p| { set_hook(Box::new(|p| {
teardown().unwrap(); teardown().unwrap();
eprintln!("{}", p); get_terminal().unwrap().show_cursor().unwrap();
eprintln!("{}\n{:?}", p, Backtrace::new());
exit(254); exit(254);
})); }));
let mut state = State::default(); if let Err(e) = init().await {
if let Err(e) = init(&mut state).await {
teardown()?; teardown()?;
return Err(e); return Err(e);
} }
Ok(()) Ok(())
} }
async fn init(state: &mut State) -> Result<()> { async fn init() -> AResult<()> {
let mut stdout = stdout();
enable_raw_mode()?; enable_raw_mode()?;
execute!(&mut stdout, EnterAlternateScreen, EnableMouseCapture)?; execute!(stdout(), EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout); let mut wh = WindowsHandler::new(get_terminal()?);
let mut terminal = Terminal::new(backend)?; wh.push::<MainWnd>();
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
thread::spawn(move || loop { thread::spawn(move || loop {
@ -61,39 +66,23 @@ async fn init(state: &mut State) -> Result<()> {
} }
}); });
terminal.clear()?; wh.clear()?;
loop { loop {
state.check_updates().await; wh.draw()?;
terminal.draw(|f| ui::draw(f, state))?; wh.update().await?;
match rx.recv()? { match rx.recv()? {
InputEvent::Key(Event::Key(key)) => match key.code { InputEvent::Key(Event::Key(key)) => {
KeyCode::Esc => { wh.handle_kbd(key.code).await?;
teardown()?; }
terminal.show_cursor()?;
break;
}
KeyCode::Left => state.prev_tab(),
KeyCode::Right => state.next_tab(),
KeyCode::Up => state.on_up(),
KeyCode::Down => state.on_down(),
KeyCode::Delete => {
state.delete().await;
state.update_tab();
}
KeyCode::F(5) => state.update_tab(),
_ => (),
},
InputEvent::Tick => (), InputEvent::Tick => (),
_ => unreachable!(), _ => unreachable!(),
} }
} }
Ok(())
} }
fn teardown() -> Result<()> { fn teardown() -> AResult<()> {
disable_raw_mode()?; disable_raw_mode()?;
execute!(stdout(), LeaveAlternateScreen, DisableMouseCapture)?; execute!(stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
eprintln!("teardown");
Ok(()) Ok(())
} }

@ -1,43 +0,0 @@
use super::{
state::{State, UiTabs},
Frame,
};
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Modifier, Style};
use tui::text::Spans;
use tui::widgets::{Block, Borders, List, ListItem, Tabs};
pub fn draw(f: &mut Frame, s: &mut State) {
let size = f.size();
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
.split(size);
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, chunks[0]);
let tab_data = s
.tab_data()
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(tab_data)
.block(Block::default().borders(Borders::ALL))
.highlight_style(Style::default().add_modifier(Modifier::BOLD));
f.render_stateful_widget(list, chunks[1], s.tab_list_state());
}

@ -1,5 +1,7 @@
use super::impls::CRUD; use super::Window;
use crate::tui::{impls::CRUD, Frame};
use anyhow::Result as AResult; use anyhow::Result as AResult;
use crossterm::event::KeyCode;
use std::{fmt::Display, str::FromStr}; use std::{fmt::Display, str::FromStr};
use strum::VariantNames; use strum::VariantNames;
use tokio::join; use tokio::join;
@ -7,6 +9,11 @@ use tui::widgets::ListState;
use u_lib::models::{Agent, AssignedJob, JobMeta}; use u_lib::models::{Agent, AssignedJob, JobMeta};
use uuid::Uuid; use uuid::Uuid;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Modifier, Style};
use tui::text::Spans;
use tui::widgets::{Block, Borders, List, ListItem, Tabs};
#[derive(strum::Display, strum::EnumVariantNames, strum::EnumString)] #[derive(strum::Display, strum::EnumVariantNames, strum::EnumString)]
pub enum UiTabs { pub enum UiTabs {
Agents, Agents,
@ -82,7 +89,7 @@ impl<T: CRUD> Default for StatefulList<T> {
} }
} }
pub struct State { pub struct MainWnd {
pub active_tab: UiTabs, pub active_tab: UiTabs,
pub last_error: Option<String>, pub last_error: Option<String>,
pub agents: StatefulList<Agent>, pub agents: StatefulList<Agent>,
@ -90,9 +97,9 @@ pub struct State {
pub map: StatefulList<AssignedJob>, pub map: StatefulList<AssignedJob>,
} }
impl Default for State { impl Default for MainWnd {
fn default() -> Self { fn default() -> Self {
State { MainWnd {
active_tab: UiTabs::Agents, active_tab: UiTabs::Agents,
last_error: None, last_error: None,
agents: Default::default(), agents: Default::default(),
@ -102,7 +109,7 @@ impl Default for State {
} }
} }
impl State { impl MainWnd {
pub fn next_tab(&mut self) { pub fn next_tab(&mut self) {
self.active_tab = self.active_tab.next() self.active_tab = self.active_tab.next()
} }
@ -233,6 +240,74 @@ impl State {
} }
} }
#[async_trait]
impl Window for MainWnd {
async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()> {
match k {
KeyCode::Esc => {
/*teardown()?;
wh.show_cursor()?;
break;*/
}
KeyCode::Left => self.prev_tab(),
KeyCode::Right => self.next_tab(),
KeyCode::Up => self.on_up(),
KeyCode::Down => self.on_down(),
KeyCode::Delete => {
self.delete().await;
self.update_tab();
}
KeyCode::F(5) => self.update_tab(),
_ => (),
};
Ok(())
}
async fn update(&mut self) -> AResult<()> {
self.check_updates().await;
Ok(())
}
async fn close(&mut self) {
//self.show_cursor()
}
fn draw(&mut self, f: &mut Frame) {
let size = f.size();
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
.split(size);
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(self.active_tab.index());
f.render_widget(tabs, chunks[0]);
let tab_data = self
.tab_data()
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(tab_data)
.block(Block::default().borders(Borders::ALL))
.highlight_style(Style::default().add_modifier(Modifier::BOLD));
f.render_stateful_widget(list, chunks[1], self.tab_list_state());
}
}
fn crop<T: Display>(data: T, retain: usize) -> String { fn crop<T: Display>(data: T, retain: usize) -> String {
data.to_string()[..retain].to_string() data.to_string()[..retain].to_string()
} }

@ -0,0 +1,94 @@
mod main_wnd;
mod processing;
pub use main_wnd::MainWnd;
use crate::tui::{Backend, Frame};
use anyhow::Result as AResult;
use crossterm::event::KeyCode;
use std::cell::RefCell;
use std::process::exit;
use std::rc::Rc;
use tui::Terminal;
type Wnd = Rc<RefCell<dyn Window>>;
#[async_trait]
pub trait Window {
async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()>;
async fn update(&mut self) -> AResult<()>;
async fn close(&mut self);
fn draw(&mut self, f: &mut Frame);
}
pub struct WindowsHandler {
queue: Vec<Wnd>,
term: Terminal<Backend>,
redraw_all: bool,
}
impl WindowsHandler {
fn get_last_wnd(&self) -> Wnd {
self.queue.last().expect("No windows found").clone()
}
pub fn new(term: Terminal<Backend>) -> Self {
Self {
term,
queue: vec![],
redraw_all: true,
}
}
pub fn push<W: Window + Default + 'static>(&mut self) {
let window = Rc::new(RefCell::new(W::default()));
self.queue.push(window);
}
pub fn clear(&mut self) -> AResult<()> {
self.term.clear()?;
Ok(())
}
pub fn show_cursor(&mut self) -> AResult<()> {
self.term.show_cursor()?;
Ok(())
}
pub fn draw(&mut self) -> AResult<()> {
if self.redraw_all {
for wnd in self.queue.iter() {
self.term.draw(|f| wnd.borrow_mut().draw(f))?;
}
} else {
let wnd = self.get_last_wnd();
self.term.draw(|f| wnd.borrow_mut().draw(f))?;
}
Ok(())
}
pub async fn close(&mut self) {
let wnd = self.queue.pop().unwrap();
wnd.borrow_mut().close().await;
self.redraw_all = true;
if self.queue.is_empty() {
self.show_cursor().unwrap();
exit(0);
}
}
pub async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()> {
if let KeyCode::Esc = k {
self.close().await;
} else {
let current_wnd = self.get_last_wnd();
current_wnd.borrow_mut().handle_kbd(k).await?;
}
Ok(())
}
pub async fn update(&mut self) -> AResult<()> {
let current_wnd = self.get_last_wnd();
current_wnd.borrow_mut().update().await?;
Ok(())
}
}
Loading…
Cancel
Save