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
[dependencies]
backtrace = "0.3.61"
structopt = "0.3.21"
log = "^0.4"
env_logger = "0.7.1"

@ -19,7 +19,7 @@ pub struct Args {
enum Cmd {
Agents(LD),
Jobs(JobALD),
Jobmap(JobMapALD),
Map(JobMapALD),
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::Delete { uid }) => printer.print(CLIENT.del(Some(uid)).await),
},
Cmd::Jobmap(action) => match action {
Cmd::Map(action) => match action {
JobMapALD::Add {
agent_uid,
job_idents,

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

@ -1,14 +1,13 @@
mod impls;
mod state;
mod ui;
mod windows;
use anyhow::Result;
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode};
use anyhow::Result as AResult;
use backtrace::Backtrace;
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event};
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::{
@ -18,36 +17,42 @@ use std::{
time::Duration,
};
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> {
Key(I),
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
set_hook(Box::new(|p| {
teardown().unwrap();
eprintln!("{}", p);
get_terminal().unwrap().show_cursor().unwrap();
eprintln!("{}\n{:?}", p, Backtrace::new());
exit(254);
}));
let mut state = State::default();
if let Err(e) = init(&mut state).await {
if let Err(e) = init().await {
teardown()?;
return Err(e);
}
Ok(())
}
async fn init(state: &mut State) -> Result<()> {
let mut stdout = stdout();
async fn init() -> AResult<()> {
enable_raw_mode()?;
execute!(&mut stdout, EnterAlternateScreen, EnableMouseCapture)?;
execute!(stdout(), EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let mut wh = WindowsHandler::new(get_terminal()?);
wh.push::<MainWnd>();
let (tx, rx) = mpsc::channel();
thread::spawn(move || loop {
@ -61,39 +66,23 @@ async fn init(state: &mut State) -> Result<()> {
}
});
terminal.clear()?;
wh.clear()?;
loop {
state.check_updates().await;
terminal.draw(|f| ui::draw(f, state))?;
wh.draw()?;
wh.update().await?;
match rx.recv()? {
InputEvent::Key(Event::Key(key)) => match key.code {
KeyCode::Esc => {
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::Key(Event::Key(key)) => {
wh.handle_kbd(key.code).await?;
}
InputEvent::Tick => (),
_ => unreachable!(),
}
}
Ok(())
}
fn teardown() -> Result<()> {
fn teardown() -> AResult<()> {
disable_raw_mode()?;
execute!(stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
eprintln!("teardown");
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 crossterm::event::KeyCode;
use std::{fmt::Display, str::FromStr};
use strum::VariantNames;
use tokio::join;
@ -7,6 +9,11 @@ use tui::widgets::ListState;
use u_lib::models::{Agent, AssignedJob, JobMeta};
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)]
pub enum UiTabs {
Agents,
@ -82,7 +89,7 @@ impl<T: CRUD> Default for StatefulList<T> {
}
}
pub struct State {
pub struct MainWnd {
pub active_tab: UiTabs,
pub last_error: Option<String>,
pub agents: StatefulList<Agent>,
@ -90,9 +97,9 @@ pub struct State {
pub map: StatefulList<AssignedJob>,
}
impl Default for State {
impl Default for MainWnd {
fn default() -> Self {
State {
MainWnd {
active_tab: UiTabs::Agents,
last_error: None,
agents: Default::default(),
@ -102,7 +109,7 @@ impl Default for State {
}
}
impl State {
impl MainWnd {
pub fn next_tab(&mut self) {
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 {
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