From 48304dce338389e2f8879cc217c2ecee8ce5055a Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Fri, 17 Dec 2021 18:10:24 +0100 Subject: [PATCH] better more fun things --- src/controller.rs | 90 ++++++++++++++++++++++++++++------------------- src/main.rs | 4 +-- src/model.rs | 17 +++------ src/view.rs | 68 ++++++++++++++++++++++------------- 4 files changed, 104 insertions(+), 75 deletions(-) diff --git a/src/controller.rs b/src/controller.rs index f267cbb..58eade1 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -1,4 +1,4 @@ -use crate::model::{AppState, AppStateTable}; +use crate::model::{AppState, AppStateFullView}; use crate::{view, App}; use crossterm::event; use crossterm::event::{Event, KeyCode}; @@ -9,14 +9,18 @@ use tui::Terminal; pub fn run_app(terminal: &mut Terminal, mut app: App) -> io::Result<()> { 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()? { 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::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(terminal: &mut Terminal, mut app: App) -> io::Resu impl App { pub fn new() -> App { App { - state: AppState::Table(AppStateTable { + table: AppState { table_state: TableState::default(), items: vec![ vec!["backend".to_string(), "running".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()], ], - }), - } - } - 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)); + }, + selected: None, } } - pub fn previous(&mut self) { - if let AppState::Table(AppStateTable { table_state, items }) = &mut self.state { - let i = match table_state.selected() { - Some(i) => { - if i == 0 { - items.len() - 1 - } else { - i - 1 - } - } - None => 0, - }; - table_state.select(Some(i)); + pub fn is_table(&self) -> bool { + self.selected.is_none() + } + + fn select_service(&mut self) { + if self.is_table() { + if let Some(selected) = self.table.table_state.selected() { + self.selected = Some(AppStateFullView { + name: self.table.items[selected][0].to_string(), + }); + } } } - 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)); + } } diff --git a/src/main.rs b/src/main.rs index 64edabf..1582eb5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,13 +2,13 @@ mod controller; mod model; mod view; -use crossterm::event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode}; +use crossterm::event::{DisableMouseCapture, EnableMouseCapture}; use crossterm::execute; use crossterm::terminal::{ disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, }; use std::{error::Error, io}; -use tui::backend::{Backend, CrosstermBackend}; +use tui::backend::CrosstermBackend; use tui::Terminal; use crate::model::App; diff --git a/src/model.rs b/src/model.rs index 6b9c774..73f4848 100644 --- a/src/model.rs +++ b/src/model.rs @@ -2,24 +2,17 @@ use tui::widgets::TableState; #[derive(Debug)] pub struct App { - pub state: AppState + pub table: AppState, + pub selected: Option } #[derive(Debug)] -pub enum AppState { - Table(AppStateTable), - FullView { - name: String, - } -} - -#[derive(Debug)] -pub struct AppStateTable { +pub struct AppState { pub table_state: TableState, pub items: Vec>, } #[derive(Debug)] pub struct AppStateFullView { - name: String, -} \ No newline at end of file + pub name: String, +} diff --git a/src/view.rs b/src/view.rs index 7b92edb..d903170 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,35 +1,45 @@ use tui::backend::Backend; -use tui::layout::{Constraint, Layout}; +use tui::layout::{Constraint, Layout, Rect}; 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 crate::model::{AppState, AppStateTable}; +use crate::model::{AppState, AppStateFullView}; use crate::App; -pub fn ui(f: &mut Frame, app: &mut App) { - match &mut app.state { - AppState::Table(state) => { - table_ui(f, state); +pub fn render_ui(f: &mut Frame, app: &mut App) { + let chunks = if f.size().height < 22 { + Layout::default() + .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()), - f.size(), + chunks[0], ), } + + if let Some(footer_chunk) = chunks.get(1) { + render_help_footer(f, app, *footer_chunk); + } } -pub fn table_ui(f: &mut Frame, state: &mut AppStateTable) { - let rects = Layout::default() - .constraints(vec![Constraint::Percentage(100)]) - .margin(5) - .split(f.size()); - +fn render_table(f: &mut Frame, state: &mut AppState, area: Rect) { let selected_style = Style::default().add_modifier(Modifier::REVERSED); let normal_style = Style::default().bg(Color::Blue); - let header_cells = ["Header1", "Header2", "Header3"] + let header_cells = ["name", "status"] .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) .style(normal_style) @@ -49,14 +59,22 @@ pub fn table_ui(f: &mut Frame, state: &mut AppStateTable) { let t = Table::new(rows) .header(header) - .block(Block::default().borders(Borders::ALL).title("Table")) + .block(Block::default().borders(Borders::ALL).title("services")) .highlight_style(selected_style) - .highlight_symbol(">> ") - .widths(&[ - Constraint::Percentage(50), - Constraint::Length(30), - Constraint::Min(10), - ]); + .widths(&[Constraint::Percentage(50), Constraint::Length(30)]); - 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(f: &mut Frame, 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); }