feat: started work on the main ui
This commit is contained in:
parent
70d39c35ab
commit
da4fba9203
|
@ -28,8 +28,6 @@ async fn main() -> Result<()> {
|
|||
let args = Args::parse();
|
||||
let base_url = args.base_url.unwrap();
|
||||
|
||||
let stdout = ui::prepare()?;
|
||||
|
||||
let api = match if let Some(token) = args.token {
|
||||
match Api::from_token(&base_url, token).await? {
|
||||
Ok(a) => Some(a),
|
||||
|
@ -42,12 +40,14 @@ async fn main() -> Result<()> {
|
|||
} {
|
||||
Some(a) => a,
|
||||
None => {
|
||||
let mut login = ui::login::UI::new(stdout, base_url)?;
|
||||
let mut login = ui::login::UI::new(ui::prepare()?, base_url)?;
|
||||
login.run().await?
|
||||
}
|
||||
};
|
||||
|
||||
println!("{:#?}", api);
|
||||
let mut ui = ui::master::UI::new(api, ui::prepare()?)?;
|
||||
|
||||
ui.run().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
use crate::api::Api;
|
||||
use crate::{api::Api, ui, ui::TextAreaExt};
|
||||
use anyhow::Result;
|
||||
use crossterm::{
|
||||
event::{DisableMouseCapture, Event, KeyCode, KeyEvent},
|
||||
execute,
|
||||
terminal::{disable_raw_mode, LeaveAlternateScreen},
|
||||
};
|
||||
use crossterm::event::{Event, KeyCode, KeyEvent};
|
||||
use std::io::Stdout;
|
||||
use tui::{
|
||||
backend::CrosstermBackend,
|
||||
|
@ -16,33 +12,6 @@ use tui::{
|
|||
};
|
||||
use tui_textarea::TextArea;
|
||||
|
||||
trait TextAreaExt {
|
||||
fn disable(&mut self);
|
||||
fn enable(&mut self);
|
||||
}
|
||||
|
||||
impl TextAreaExt for TextArea<'_> {
|
||||
fn disable(&mut self) {
|
||||
self.set_cursor_line_style(Style::default());
|
||||
self.set_cursor_style(Style::default());
|
||||
let b = self
|
||||
.block()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Block::default().borders(Borders::ALL));
|
||||
self.set_block(b.style(Style::default().fg(Color::DarkGray)));
|
||||
}
|
||||
|
||||
fn enable(&mut self) {
|
||||
self.set_cursor_line_style(Style::default().add_modifier(Modifier::UNDERLINED));
|
||||
self.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
|
||||
let b = self
|
||||
.block()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Block::default().borders(Borders::ALL));
|
||||
self.set_block(b.style(Style::default()));
|
||||
}
|
||||
}
|
||||
|
||||
enum InputPosition {
|
||||
Username,
|
||||
Password,
|
||||
|
@ -63,15 +32,7 @@ pub struct UI<'a> {
|
|||
|
||||
impl Drop for UI<'_> {
|
||||
fn drop(&mut self) {
|
||||
disable_raw_mode().expect("While destructing UI -> Failed to disable raw mode");
|
||||
execute!(
|
||||
self.terminal.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
DisableMouseCapture
|
||||
).expect("While destructing UI -> Failed execute backend commands (LeaveAlternateScreen and DisableMouseCapture)");
|
||||
self.terminal
|
||||
.show_cursor()
|
||||
.expect("While destructing UI -> Failed to re-enable cursor");
|
||||
ui::restore(&mut self.terminal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
use crate::api::Api;
|
||||
use anyhow::Result;
|
||||
use crossterm::event::{Event, KeyCode, KeyEvent};
|
||||
use std::io::Stdout;
|
||||
use tui::widgets::{Block, Borders, List, ListItem, ListState};
|
||||
use tui::{
|
||||
backend::CrosstermBackend,
|
||||
layout::{Constraint, Direction, Layout},
|
||||
style::{Color, Modifier, Style},
|
||||
Terminal,
|
||||
};
|
||||
|
||||
enum InputPosition {
|
||||
Sidebar,
|
||||
Content,
|
||||
}
|
||||
|
||||
pub struct UI<'a> {
|
||||
terminal: Terminal<CrosstermBackend<Stdout>>,
|
||||
sidebar_items: Vec<&'a str>,
|
||||
sidebar_state: ListState,
|
||||
position: InputPosition,
|
||||
|
||||
api: Api,
|
||||
}
|
||||
|
||||
impl UI<'_> {
|
||||
pub fn new(api: Api, stdout: Stdout) -> Result<Self> {
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let terminal = Terminal::new(backend)?;
|
||||
|
||||
let sidebar_items = vec!["Overview", "Users", "Projects", "Server Status", "Backup"];
|
||||
let mut sidebar_state = ListState::default();
|
||||
sidebar_state.select(Some(0));
|
||||
|
||||
Ok(Self {
|
||||
terminal,
|
||||
sidebar_items,
|
||||
sidebar_state,
|
||||
position: InputPosition::Sidebar,
|
||||
api,
|
||||
})
|
||||
}
|
||||
pub async fn run(&mut self) -> Result<()> {
|
||||
loop {
|
||||
self.draw().await?;
|
||||
let event = crossterm::event::read()?;
|
||||
|
||||
match event {
|
||||
Event::Key(KeyEvent {
|
||||
code: KeyCode::Esc, ..
|
||||
}) => {
|
||||
return Ok(());
|
||||
}
|
||||
Event::Key(KeyEvent {
|
||||
code: KeyCode::Tab, ..
|
||||
}) => {
|
||||
self.cycle();
|
||||
}
|
||||
input => match self.position {
|
||||
InputPosition::Sidebar => match input {
|
||||
Event::Key(KeyEvent {
|
||||
code: KeyCode::Up, ..
|
||||
}) => {
|
||||
if let Some(i) = self.sidebar_state.selected() {
|
||||
if i > 0 {
|
||||
self.sidebar_state.select(Some(i - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Key(KeyEvent {
|
||||
code: KeyCode::Down,
|
||||
..
|
||||
}) => {
|
||||
if let Some(i) = self.sidebar_state.selected() {
|
||||
if i < self.sidebar_items.len() - 1 {
|
||||
self.sidebar_state.select(Some(i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
InputPosition::Content => (),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cycle(&mut self) {
|
||||
self.position = match self.position {
|
||||
InputPosition::Sidebar => InputPosition::Content,
|
||||
InputPosition::Content => InputPosition::Sidebar,
|
||||
}
|
||||
}
|
||||
|
||||
async fn draw(&mut self) -> Result<()> {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints(
|
||||
[
|
||||
Constraint::Length(32), // Sidebar
|
||||
Constraint::Min(32), // Content
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(self.terminal.size()?);
|
||||
|
||||
let sidebar = List::new(
|
||||
self.sidebar_items
|
||||
.iter()
|
||||
.map(|&x| ListItem::new(x))
|
||||
.collect::<Vec<ListItem>>(),
|
||||
)
|
||||
.block(Block::default().borders(Borders::ALL))
|
||||
.style(Style::default().fg(Color::White))
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.fg(Color::Green)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)
|
||||
.highlight_symbol("> ");
|
||||
|
||||
let content_block = Block::default().borders(Borders::ALL);
|
||||
|
||||
self.terminal.draw(|f| {
|
||||
f.render_stateful_widget(sidebar, chunks[0], &mut self.sidebar_state);
|
||||
f.render_widget(content_block, chunks[1]);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,16 +1,63 @@
|
|||
pub mod login;
|
||||
pub mod master;
|
||||
|
||||
use std::{io, io::Stdout};
|
||||
use anyhow::{Context, Result};
|
||||
use crossterm::{
|
||||
event::EnableMouseCapture,
|
||||
event::{DisableMouseCapture, EnableMouseCapture},
|
||||
execute,
|
||||
terminal::{enable_raw_mode, EnterAlternateScreen},
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use std::{io, io::Stdout};
|
||||
use tui::{
|
||||
backend::CrosstermBackend,
|
||||
style::{Color, Modifier, Style},
|
||||
widgets::{Block, Borders},
|
||||
Terminal,
|
||||
};
|
||||
use tui_textarea::TextArea;
|
||||
|
||||
pub trait TextAreaExt {
|
||||
fn disable(&mut self);
|
||||
fn enable(&mut self);
|
||||
}
|
||||
|
||||
impl TextAreaExt for TextArea<'_> {
|
||||
fn disable(&mut self) {
|
||||
self.set_cursor_line_style(Style::default());
|
||||
self.set_cursor_style(Style::default());
|
||||
let b = self
|
||||
.block()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Block::default().borders(Borders::ALL));
|
||||
self.set_block(b.style(Style::default().fg(Color::DarkGray)));
|
||||
}
|
||||
|
||||
fn enable(&mut self) {
|
||||
self.set_cursor_line_style(Style::default().add_modifier(Modifier::UNDERLINED));
|
||||
self.set_cursor_style(Style::default().add_modifier(Modifier::REVERSED));
|
||||
let b = self
|
||||
.block()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Block::default().borders(Borders::ALL));
|
||||
self.set_block(b.style(Style::default()));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare() -> Result<Stdout> {
|
||||
enable_raw_mode().context("Failed to enable raw mode")?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
||||
Ok(stdout)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn restore(terminal: &mut Terminal<CrosstermBackend<Stdout>>) {
|
||||
disable_raw_mode().expect("While destructing UI -> Failed to disable raw mode");
|
||||
execute!(
|
||||
terminal.backend_mut(),
|
||||
LeaveAlternateScreen,
|
||||
DisableMouseCapture,
|
||||
).expect("While destructing UI -> Failed execute backend commands (LeaveAlternateScreen and DisableMouseCapture)");
|
||||
terminal
|
||||
.show_cursor()
|
||||
.expect("While destructing UI -> Failed to re-enable cursor");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue