mirror of
https://github.com/Noratrieb/service-manager.git
synced 2026-01-16 09:25:05 +01:00
better more fun things
This commit is contained in:
parent
4f5b34a154
commit
48304dce33
4 changed files with 104 additions and 75 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::model::{AppState, AppStateTable};
|
use crate::model::{AppState, AppStateFullView};
|
||||||
use crate::{view, App};
|
use crate::{view, App};
|
||||||
use crossterm::event;
|
use crossterm::event;
|
||||||
use crossterm::event::{Event, KeyCode};
|
use crossterm::event::{Event, KeyCode};
|
||||||
|
|
@ -9,14 +9,18 @@ use tui::Terminal;
|
||||||
|
|
||||||
pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<()> {
|
pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
terminal.draw(|f| view::ui(f, &mut app))?;
|
terminal.draw(|f| view::render_ui(f, &mut app))?;
|
||||||
|
|
||||||
if let Event::Key(key) = event::read()? {
|
if let Event::Key(key) = event::read()? {
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Char('q') => return Ok(()),
|
KeyCode::Char('q') => match app.selected {
|
||||||
|
Some(_) => app.leave_service(),
|
||||||
|
None => return Ok(()),
|
||||||
|
},
|
||||||
KeyCode::Down => app.next(),
|
KeyCode::Down => app.next(),
|
||||||
KeyCode::Up => app.previous(),
|
KeyCode::Up => app.previous(),
|
||||||
KeyCode::Enter => app.select(),
|
KeyCode::Enter => app.select_service(),
|
||||||
|
KeyCode::Esc => app.leave_service(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -26,47 +30,61 @@ pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Resu
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new() -> App {
|
pub fn new() -> App {
|
||||||
App {
|
App {
|
||||||
state: AppState::Table(AppStateTable {
|
table: AppState {
|
||||||
table_state: TableState::default(),
|
table_state: TableState::default(),
|
||||||
items: vec![
|
items: vec![
|
||||||
vec!["backend".to_string(), "running".to_string()],
|
vec!["backend".to_string(), "running".to_string()],
|
||||||
vec!["frontend".to_string(), "exited (0)".to_string()],
|
vec!["frontend".to_string(), "exited (0)".to_string()],
|
||||||
vec!["frontend".to_string(), "failed (1)".to_string()],
|
vec!["database".to_string(), "failed (1)".to_string()],
|
||||||
],
|
],
|
||||||
}),
|
},
|
||||||
}
|
selected: None,
|
||||||
}
|
|
||||||
pub fn next(&mut self) {
|
|
||||||
if let AppState::Table(AppStateTable { table_state, items }) = &mut self.state {
|
|
||||||
let i = match table_state.selected() {
|
|
||||||
Some(i) => {
|
|
||||||
if i >= items.len() - 1 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
i + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => 0,
|
|
||||||
};
|
|
||||||
table_state.select(Some(i));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn previous(&mut self) {
|
pub fn is_table(&self) -> bool {
|
||||||
if let AppState::Table(AppStateTable { table_state, items }) = &mut self.state {
|
self.selected.is_none()
|
||||||
let i = match table_state.selected() {
|
}
|
||||||
Some(i) => {
|
|
||||||
if i == 0 {
|
fn select_service(&mut self) {
|
||||||
items.len() - 1
|
if self.is_table() {
|
||||||
} else {
|
if let Some(selected) = self.table.table_state.selected() {
|
||||||
i - 1
|
self.selected = Some(AppStateFullView {
|
||||||
}
|
name: self.table.items[selected][0].to_string(),
|
||||||
}
|
});
|
||||||
None => 0,
|
}
|
||||||
};
|
|
||||||
table_state.select(Some(i));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn select(&mut self) {}
|
fn leave_service(&mut self) {
|
||||||
|
self.selected = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) {
|
||||||
|
let i = match self.table.table_state.selected() {
|
||||||
|
Some(i) => {
|
||||||
|
if i >= self.table.items.len() - 1 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
self.table.table_state.select(Some(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn previous(&mut self) {
|
||||||
|
let i = match self.table.table_state.selected() {
|
||||||
|
Some(i) => {
|
||||||
|
if i == 0 {
|
||||||
|
self.table.items.len() - 1
|
||||||
|
} else {
|
||||||
|
i - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
self.table.table_state.select(Some(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ mod controller;
|
||||||
mod model;
|
mod model;
|
||||||
mod view;
|
mod view;
|
||||||
|
|
||||||
use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode};
|
use crossterm::event::{DisableMouseCapture, EnableMouseCapture};
|
||||||
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 std::{error::Error, io};
|
use std::{error::Error, io};
|
||||||
use tui::backend::{Backend, CrosstermBackend};
|
use tui::backend::CrosstermBackend;
|
||||||
use tui::Terminal;
|
use tui::Terminal;
|
||||||
|
|
||||||
use crate::model::App;
|
use crate::model::App;
|
||||||
|
|
|
||||||
17
src/model.rs
17
src/model.rs
|
|
@ -2,24 +2,17 @@ use tui::widgets::TableState;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub state: AppState
|
pub table: AppState,
|
||||||
|
pub selected: Option<AppStateFullView>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AppState {
|
pub struct AppState {
|
||||||
Table(AppStateTable),
|
|
||||||
FullView {
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AppStateTable {
|
|
||||||
pub table_state: TableState,
|
pub table_state: TableState,
|
||||||
pub items: Vec<Vec<String>>,
|
pub items: Vec<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AppStateFullView {
|
pub struct AppStateFullView {
|
||||||
name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
68
src/view.rs
68
src/view.rs
|
|
@ -1,35 +1,45 @@
|
||||||
use tui::backend::Backend;
|
use tui::backend::Backend;
|
||||||
use tui::layout::{Constraint, Layout};
|
use tui::layout::{Constraint, Layout, Rect};
|
||||||
use tui::style::{Color, Modifier, Style};
|
use tui::style::{Color, Modifier, Style};
|
||||||
use tui::widgets::{Block, Borders, Cell, Row, Table};
|
use tui::text::Spans;
|
||||||
|
use tui::widgets::{Block, Borders, Cell, Paragraph, Row, Table};
|
||||||
use tui::Frame;
|
use tui::Frame;
|
||||||
|
|
||||||
use crate::model::{AppState, AppStateTable};
|
use crate::model::{AppState, AppStateFullView};
|
||||||
use crate::App;
|
use crate::App;
|
||||||
|
|
||||||
pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
pub fn render_ui<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
||||||
match &mut app.state {
|
let chunks = if f.size().height < 22 {
|
||||||
AppState::Table(state) => {
|
Layout::default()
|
||||||
table_ui(f, state);
|
.constraints(vec![Constraint::Percentage(100)])
|
||||||
|
.split(f.size())
|
||||||
|
} else {
|
||||||
|
Layout::default()
|
||||||
|
.constraints(vec![Constraint::Percentage(90), Constraint::Max(3)])
|
||||||
|
.split(f.size())
|
||||||
|
};
|
||||||
|
|
||||||
|
match app.selected {
|
||||||
|
None => {
|
||||||
|
render_table(f, &mut app.table, chunks[0]);
|
||||||
}
|
}
|
||||||
AppState::FullView { name } => f.render_widget(
|
Some(AppStateFullView { ref name }) => f.render_widget(
|
||||||
Block::default().borders(Borders::ALL).title(name.as_ref()),
|
Block::default().borders(Borders::ALL).title(name.as_ref()),
|
||||||
f.size(),
|
chunks[0],
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(footer_chunk) = chunks.get(1) {
|
||||||
|
render_help_footer(f, app, *footer_chunk);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn table_ui<B: Backend>(f: &mut Frame<B>, state: &mut AppStateTable) {
|
fn render_table<B: Backend>(f: &mut Frame<B>, state: &mut AppState, area: Rect) {
|
||||||
let rects = Layout::default()
|
|
||||||
.constraints(vec![Constraint::Percentage(100)])
|
|
||||||
.margin(5)
|
|
||||||
.split(f.size());
|
|
||||||
|
|
||||||
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
|
let selected_style = Style::default().add_modifier(Modifier::REVERSED);
|
||||||
let normal_style = Style::default().bg(Color::Blue);
|
let normal_style = Style::default().bg(Color::Blue);
|
||||||
let header_cells = ["Header1", "Header2", "Header3"]
|
let header_cells = ["name", "status"]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|h| Cell::from(*h).style(Style::default().fg(Color::Red)));
|
.map(|h| Cell::from(*h).style(Style::default()));
|
||||||
|
|
||||||
let header = Row::new(header_cells)
|
let header = Row::new(header_cells)
|
||||||
.style(normal_style)
|
.style(normal_style)
|
||||||
|
|
@ -49,14 +59,22 @@ pub fn table_ui<B: Backend>(f: &mut Frame<B>, state: &mut AppStateTable) {
|
||||||
|
|
||||||
let t = Table::new(rows)
|
let t = Table::new(rows)
|
||||||
.header(header)
|
.header(header)
|
||||||
.block(Block::default().borders(Borders::ALL).title("Table"))
|
.block(Block::default().borders(Borders::ALL).title("services"))
|
||||||
.highlight_style(selected_style)
|
.highlight_style(selected_style)
|
||||||
.highlight_symbol(">> ")
|
.widths(&[Constraint::Percentage(50), Constraint::Length(30)]);
|
||||||
.widths(&[
|
|
||||||
Constraint::Percentage(50),
|
|
||||||
Constraint::Length(30),
|
|
||||||
Constraint::Min(10),
|
|
||||||
]);
|
|
||||||
|
|
||||||
f.render_stateful_widget(t, rects[0], &mut state.table_state);
|
f.render_stateful_widget(t, area, &mut state.table_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_help_footer<B: Backend>(f: &mut Frame<B>, app: &App, area: Rect) {
|
||||||
|
let block = Block::default().title("help").borders(Borders::ALL);
|
||||||
|
|
||||||
|
let paragraph = Paragraph::new(if app.is_table() {
|
||||||
|
vec![Spans::from("q-quit down-down up-up enter-select")]
|
||||||
|
} else {
|
||||||
|
vec![Spans::from("q-back esc-back")]
|
||||||
|
})
|
||||||
|
.block(block);
|
||||||
|
|
||||||
|
f.render_widget(paragraph, area);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue