Create an example UI and event handler
This commit is contained in:
parent
20b02fbd90
commit
fd444a620d
File diff suppressed because it is too large
Load Diff
|
@ -8,6 +8,7 @@ members = [
|
||||||
"coordinates",
|
"coordinates",
|
||||||
"cyberpunk-splash",
|
"cyberpunk-splash",
|
||||||
"dashboard",
|
"dashboard",
|
||||||
|
"editor-challenge",
|
||||||
"emseries",
|
"emseries",
|
||||||
"file-service",
|
"file-service",
|
||||||
"fitnesstrax/core",
|
"fitnesstrax/core",
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "editor-challenge"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = { version = "1" }
|
||||||
|
crossterm = { version = "0.19", features = [ "serde" ] }
|
||||||
|
serde = { version = "1", features = [ "derive" ] }
|
||||||
|
serde_yml = { version = "*" }
|
||||||
|
thiserror = { version = "1" }
|
||||||
|
tui = { version = "0.19", default-features = false, features = [ "crossterm", "serde" ] }
|
|
@ -0,0 +1,110 @@
|
||||||
|
use crossterm::{event::{self, KeyCode, KeyEvent}, terminal::{disable_raw_mode, enable_raw_mode}};
|
||||||
|
use std::{io::{self, Read}, sync::mpsc, thread, time::{Duration, Instant}};
|
||||||
|
use tui::{
|
||||||
|
backend::CrosstermBackend, layout::{Alignment, Constraint, Direction, Layout}, style::{Color, Style}, widgets::{Block, BorderType, Borders, Paragraph}, Terminal
|
||||||
|
};
|
||||||
|
|
||||||
|
const TITLE: &str = "Editor Challenge";
|
||||||
|
const COPYRIGHT: &str = "Editor Challenge, (c) Savanni D'Gerinel - all rights reserved";
|
||||||
|
const TICK_RATE_MS: u64 = 200;
|
||||||
|
|
||||||
|
fn render<T>(terminal: &mut Terminal<T>) -> Result<(), anyhow::Error>
|
||||||
|
where
|
||||||
|
T: tui::backend::Backend,
|
||||||
|
{
|
||||||
|
terminal.draw(|rect| {
|
||||||
|
let size = rect.size();
|
||||||
|
let chunks = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.margin(2)
|
||||||
|
.constraints(
|
||||||
|
[
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Min(2),
|
||||||
|
Constraint::Length(3),
|
||||||
|
]
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.split(size);
|
||||||
|
|
||||||
|
let title = Paragraph::new(TITLE)
|
||||||
|
.style(Style::default().fg(Color::LightCyan))
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.style(Style::default().fg(Color::White))
|
||||||
|
.title("Copyright")
|
||||||
|
.border_type(BorderType::Plain),
|
||||||
|
);
|
||||||
|
rect.render_widget(title, chunks[0]);
|
||||||
|
|
||||||
|
let cp = Paragraph::new(COPYRIGHT)
|
||||||
|
.style(Style::default().fg(Color::LightCyan))
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.block(
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.style(Style::default().fg(Color::White))
|
||||||
|
.title("Copyright")
|
||||||
|
.border_type(BorderType::Plain),
|
||||||
|
);
|
||||||
|
rect.render_widget(cp, chunks[2]);
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Event<I> {
|
||||||
|
Input(I),
|
||||||
|
Tick,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_input(tx: mpsc::Sender<Event<KeyEvent>>, tick_rate: Duration) {
|
||||||
|
let mut last_tick = Instant::now();
|
||||||
|
loop {
|
||||||
|
let timeout = tick_rate.checked_sub(last_tick.elapsed())
|
||||||
|
.unwrap_or_else(|| Duration::from_secs(0));
|
||||||
|
|
||||||
|
if event::poll(timeout).expect("poll works") {
|
||||||
|
if let event::Event::Key(key) = event::read().expect("can read events") {
|
||||||
|
tx.send(Event::Input(key)).expect("can send events");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if last_tick.elapsed() >= tick_rate {
|
||||||
|
if let Ok(_) = tx.send(Event::Tick) {
|
||||||
|
last_tick = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let tick_rate = Duration::from_millis(TICK_RATE_MS);
|
||||||
|
thread::spawn(move || {
|
||||||
|
handle_input(tx, tick_rate);
|
||||||
|
});
|
||||||
|
|
||||||
|
enable_raw_mode()?;
|
||||||
|
|
||||||
|
let stdout = io::stdout();
|
||||||
|
let backend = CrosstermBackend::new(stdout);
|
||||||
|
let mut terminal = Terminal::new(backend)?;
|
||||||
|
let _ = terminal.clear()?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
render(&mut terminal)?;
|
||||||
|
|
||||||
|
let event = rx.recv()?;
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::Input(KeyEvent{ code, .. }) if code == KeyCode::Char('q') => {
|
||||||
|
disable_raw_mode()?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue