2023-06-12 05:07:15 +00:00
|
|
|
use std::{
|
2023-10-19 11:29:53 +00:00
|
|
|
io::{self, stdout, Stdout},
|
|
|
|
rc::Rc,
|
|
|
|
time::Duration,
|
2023-06-12 05:07:15 +00:00
|
|
|
};
|
|
|
|
|
2023-10-19 11:29:53 +00:00
|
|
|
use anyhow::Result;
|
2021-11-01 21:27:46 +00:00
|
|
|
use crossterm::{
|
2023-10-19 11:29:53 +00:00
|
|
|
event::{self, Event, KeyCode, KeyEventKind},
|
2021-11-01 21:27:46 +00:00
|
|
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
2023-10-19 11:29:53 +00:00
|
|
|
ExecutableCommand,
|
2021-11-01 21:27:46 +00:00
|
|
|
};
|
2023-10-19 11:29:53 +00:00
|
|
|
use ratatui::{
|
|
|
|
prelude::*,
|
|
|
|
widgets::{block::Title, *},
|
|
|
|
};
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
App::run()
|
|
|
|
}
|
2018-09-23 18:59:51 +00:00
|
|
|
|
2016-11-07 23:35:46 +00:00
|
|
|
struct App {
|
2023-10-19 11:29:53 +00:00
|
|
|
term: Term,
|
|
|
|
should_quit: bool,
|
|
|
|
state: AppState,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Clone, Copy)]
|
|
|
|
struct AppState {
|
2016-11-07 23:35:46 +00:00
|
|
|
progress1: u16,
|
|
|
|
progress2: u16,
|
2018-11-27 01:02:37 +00:00
|
|
|
progress3: f64,
|
2016-11-07 23:35:46 +00:00
|
|
|
progress4: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl App {
|
2023-10-19 11:29:53 +00:00
|
|
|
fn run() -> Result<()> {
|
|
|
|
// run at ~10 fps minus the time it takes to draw
|
|
|
|
let timeout = Duration::from_secs_f32(1.0 / 10.0);
|
|
|
|
let mut app = Self::start()?;
|
|
|
|
while !app.should_quit {
|
|
|
|
app.update();
|
|
|
|
app.draw()?;
|
|
|
|
app.handle_events(timeout)?;
|
2016-11-07 23:35:46 +00:00
|
|
|
}
|
2023-10-19 11:29:53 +00:00
|
|
|
app.stop()?;
|
|
|
|
Ok(())
|
2016-11-07 23:35:46 +00:00
|
|
|
}
|
|
|
|
|
2023-10-19 11:29:53 +00:00
|
|
|
fn start() -> Result<Self> {
|
|
|
|
Ok(App {
|
|
|
|
term: Term::start()?,
|
|
|
|
should_quit: false,
|
|
|
|
state: AppState {
|
|
|
|
progress1: 0,
|
|
|
|
progress2: 0,
|
|
|
|
progress3: 0.0,
|
|
|
|
progress4: 0,
|
|
|
|
},
|
|
|
|
})
|
2016-11-07 23:35:46 +00:00
|
|
|
}
|
|
|
|
|
2023-10-19 11:29:53 +00:00
|
|
|
fn stop(&mut self) -> Result<()> {
|
|
|
|
Term::stop()?;
|
|
|
|
Ok(())
|
2021-11-01 21:27:46 +00:00
|
|
|
}
|
2019-12-15 20:38:18 +00:00
|
|
|
|
2023-10-19 11:29:53 +00:00
|
|
|
fn update(&mut self) {
|
|
|
|
self.state.progress1 = (self.state.progress1 + 4).min(100);
|
|
|
|
self.state.progress2 = (self.state.progress2 + 3).min(100);
|
|
|
|
self.state.progress3 = (self.state.progress3 + 0.02).min(1.0);
|
|
|
|
self.state.progress4 = (self.state.progress4 + 1).min(100);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn draw(&mut self) -> Result<()> {
|
|
|
|
self.term.draw(|frame| {
|
|
|
|
let state = self.state;
|
|
|
|
let layout = Self::equal_layout(frame);
|
|
|
|
Self::render_gauge1(state.progress1, frame, layout[0]);
|
|
|
|
Self::render_gauge2(state.progress2, frame, layout[1]);
|
|
|
|
Self::render_gauge3(state.progress3, frame, layout[2]);
|
|
|
|
Self::render_gauge4(state.progress4, frame, layout[3]);
|
|
|
|
})?;
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-12-15 20:38:18 +00:00
|
|
|
|
2023-10-19 11:29:53 +00:00
|
|
|
fn handle_events(&mut self, timeout: Duration) -> io::Result<()> {
|
|
|
|
if event::poll(timeout)? {
|
2021-11-01 21:27:46 +00:00
|
|
|
if let Event::Key(key) = event::read()? {
|
2023-10-19 11:29:53 +00:00
|
|
|
if key.kind == KeyEventKind::Press {
|
|
|
|
if let KeyCode::Char('q') = key.code {
|
|
|
|
self.should_quit = true;
|
|
|
|
}
|
2018-12-07 15:17:33 +00:00
|
|
|
}
|
|
|
|
}
|
2021-11-01 21:27:46 +00:00
|
|
|
}
|
2023-10-19 11:29:53 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn equal_layout(frame: &Frame) -> Rc<[Rect]> {
|
|
|
|
Layout::default()
|
|
|
|
.direction(Direction::Vertical)
|
|
|
|
.constraints([
|
|
|
|
Constraint::Percentage(25),
|
|
|
|
Constraint::Percentage(25),
|
|
|
|
Constraint::Percentage(25),
|
|
|
|
Constraint::Percentage(25),
|
|
|
|
])
|
|
|
|
.split(frame.size())
|
2016-11-07 23:35:46 +00:00
|
|
|
}
|
2023-10-19 11:29:53 +00:00
|
|
|
|
|
|
|
fn render_gauge1(progress: u16, frame: &mut Frame, area: Rect) {
|
|
|
|
let title = Self::title_block("Gauge with percentage progress");
|
|
|
|
let gauge = Gauge::default()
|
|
|
|
.block(title)
|
|
|
|
.gauge_style(Style::new().light_red())
|
|
|
|
.percent(progress);
|
|
|
|
frame.render_widget(gauge, area);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_gauge2(progress: u16, frame: &mut Frame, area: Rect) {
|
|
|
|
let title = Self::title_block("Gauge with percentage progress and custom label");
|
|
|
|
let label = format!("{}/100", progress);
|
|
|
|
let gauge = Gauge::default()
|
|
|
|
.block(title)
|
|
|
|
.gauge_style(Style::new().blue().on_light_blue())
|
|
|
|
.percent(progress)
|
|
|
|
.label(label);
|
|
|
|
frame.render_widget(gauge, area);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_gauge3(progress: f64, frame: &mut Frame, area: Rect) {
|
|
|
|
let title =
|
|
|
|
Self::title_block("Gauge with ratio progress, custom label with style, and unicode");
|
|
|
|
let label = Span::styled(
|
|
|
|
format!("{:.2}%", progress * 100.0),
|
|
|
|
Style::new().red().italic().bold(),
|
|
|
|
);
|
|
|
|
let gauge = Gauge::default()
|
|
|
|
.block(title)
|
|
|
|
.gauge_style(Style::default().fg(Color::Yellow))
|
|
|
|
.ratio(progress)
|
|
|
|
.label(label)
|
|
|
|
.use_unicode(true);
|
|
|
|
frame.render_widget(gauge, area);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn render_gauge4(progress: u16, frame: &mut Frame, area: Rect) {
|
|
|
|
let title = Self::title_block("Gauge with percentage progress and label");
|
|
|
|
let label = format!("{}/100", progress);
|
|
|
|
let gauge = Gauge::default()
|
|
|
|
.block(title)
|
|
|
|
.gauge_style(Style::new().green().italic())
|
|
|
|
.percent(progress)
|
|
|
|
.label(label);
|
|
|
|
frame.render_widget(gauge, area);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn title_block(title: &str) -> Block {
|
|
|
|
let title = Title::from(title).alignment(Alignment::Center);
|
|
|
|
Block::default().title(title).borders(Borders::TOP)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct Term {
|
|
|
|
terminal: Terminal<CrosstermBackend<Stdout>>,
|
2021-11-01 21:27:46 +00:00
|
|
|
}
|
2016-11-07 23:35:46 +00:00
|
|
|
|
2023-10-19 11:29:53 +00:00
|
|
|
impl Term {
|
|
|
|
pub fn start() -> io::Result<Term> {
|
|
|
|
stdout().execute(EnterAlternateScreen)?;
|
|
|
|
enable_raw_mode()?;
|
|
|
|
let terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
|
|
|
Ok(Self { terminal })
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn stop() -> io::Result<()> {
|
|
|
|
disable_raw_mode()?;
|
|
|
|
stdout().execute(LeaveAlternateScreen)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn draw(&mut self, frame: impl FnOnce(&mut Frame)) -> Result<()> {
|
|
|
|
self.terminal.draw(frame)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
2016-11-07 23:35:46 +00:00
|
|
|
}
|