aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.rs138
-rw-r--r--src/event.rs2
-rw-r--r--src/main.rs1
-rw-r--r--src/state.rs20
4 files changed, 149 insertions, 12 deletions
diff --git a/src/app.rs b/src/app.rs
index e5d0f25..1b46409 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,10 +1,16 @@
use std::{
error::Error,
io::{stdout, Stdout},
+ time::Duration,
};
use crossterm::{
- terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
+ cursor::{Hide, MoveTo, Show},
+ style::{Color, SetForegroundColor},
+ terminal::{
+ disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen,
+ LeaveAlternateScreen,
+ },
ExecutableCommand,
};
use tokio::{
@@ -12,7 +18,10 @@ use tokio::{
sync::mpsc::{channel, Receiver, Sender},
};
-use crate::event::{handle_input, Event};
+use crate::{
+ event::{handle_input, Event},
+ state::State,
+};
pub const TICK_RATE: u64 = 1000 / 20;
@@ -22,7 +31,8 @@ pub struct App {
event_rx: Receiver<Event>,
running: bool,
quote: Vec<String>,
- current: (usize, usize),
+ state: State,
+ should_render: bool,
}
impl App {
@@ -38,41 +48,145 @@ impl App {
event_rx,
event_tx,
running: false,
- current: (0, 0),
+ state: State::default(),
+ should_render: true,
}
}
pub async fn run(&mut self) -> Result<(), Box<dyn Error>> {
- self.stdout.execute(EnterAlternateScreen)?;
+ self.stdout.execute(EnterAlternateScreen)?.execute(Hide)?;
enable_raw_mode()?;
- let (ks_tx, ks_rx): (Sender<()>, Receiver<()>) = channel(1);
+ let (input_ks_tx, input_ks_rx): (Sender<()>, Receiver<()>) = channel(1);
+ let ev = self.event_tx.clone();
+ spawn(async {
+ start_input_handler(ev, input_ks_rx).await;
+ });
+
+ let (tick_ks_tx, tick_ks_rx): (Sender<()>, Receiver<()>) = channel(1);
let ev = self.event_tx.clone();
spawn(async {
- start_input_handler(ev, ks_rx).await;
+ start_tick_generator(ev, tick_ks_rx).await;
});
self.running = true;
while self.running {
self.process().await?;
}
- let _ = ks_tx.send(());
+ input_ks_tx.send(()).await?;
+ tick_ks_tx.send(()).await?;
disable_raw_mode()?;
- self.stdout.execute(LeaveAlternateScreen)?;
+ self.stdout.execute(Show)?.execute(LeaveAlternateScreen)?;
return Ok(());
}
async fn process(&mut self) -> Result<(), Box<dyn Error>> {
- match self.event_rx.recv().await.unwrap() {
+ let event = self.event_rx.recv().await.unwrap();
+ match event {
Event::Terminate => {
self.running = false;
}
- Event::KeyPress(_) => todo!(),
- Event::Backspace => todo!(),
+ Event::KeyPress(k) => self.handle_keypress(k).await,
+ Event::Backspace => self.handle_backspace().await,
+ Event::Render => self.render().await,
+ }
+
+ if event != Event::Render {
+ self.should_render = true;
}
return Ok(());
}
+
+ async fn handle_keypress(&mut self, k: char) {
+ if self.state.buffer == self.quote[self.state.current] && k == ' ' {
+ self.state.buffer.clear();
+ self.state.current += 1;
+ if self.state.current == self.quote.len() {
+ // TODO Remove running set and set state to StateEnum::Result instead
+ self.running = false;
+ }
+ } else {
+ self.state.buffer.push(k);
+ }
+ }
+
+ async fn handle_backspace(&mut self) {
+ if !self.state.buffer.is_empty() {
+ self.state.buffer.pop();
+ }
+ }
+
+ async fn render(&mut self) {
+ if !self.should_render {
+ return;
+ }
+
+ self.stdout
+ .execute(Clear(ClearType::All))
+ .unwrap()
+ .execute(SetForegroundColor(Color::Green))
+ .unwrap()
+ .execute(MoveTo(0, 0))
+ .unwrap();
+ let done = self.quote[..self.state.current].join(" ");
+ print!("{}", done);
+
+ if !done.trim().is_empty() {
+ print!(" ");
+ }
+
+ for i in 0..self.state.buffer.len() {
+ if i >= self.quote[self.state.current].len() {
+ break;
+ }
+
+ let c = self.quote[self.state.current].chars().nth(i).unwrap();
+ if self.state.buffer.chars().nth(i).unwrap() == c {
+ self.stdout
+ .execute(SetForegroundColor(Color::Green))
+ .unwrap();
+ } else {
+ self.stdout.execute(SetForegroundColor(Color::Red)).unwrap();
+ }
+ print!("{}", c);
+ }
+
+ if self.state.buffer.len() < self.quote[self.state.current].len() {
+ self.stdout
+ .execute(SetForegroundColor(Color::Reset))
+ .unwrap();
+ let v = &self.quote[self.state.current][self.state.buffer.len()..];
+ print!("{}", v);
+ } else if self.state.buffer.len() > self.quote[self.state.current].len() {
+ self.stdout
+ .execute(SetForegroundColor(Color::Yellow))
+ .unwrap();
+ let v = &self.state.buffer[self.quote[self.state.current].len()..];
+ print!("{}", v);
+ }
+
+ print!(" ");
+
+ self.stdout
+ .execute(SetForegroundColor(Color::Reset))
+ .unwrap();
+ let to_do = self.quote[self.state.current + 1..].join(" ");
+ println!("{}", to_do);
+ self.should_render = false;
+ }
+}
+
+async fn start_tick_generator(ev: Sender<Event>, mut kill_switch: Receiver<()>) {
+ loop {
+ tokio::select! {
+ _ = async {
+ tokio::time::sleep(Duration::from_millis(TICK_RATE)).await;
+ ev.send(Event::Render).await
+ } => (),
+ _ = kill_switch.recv() => return,
+ }
+ }
}
async fn start_input_handler(ev: Sender<Event>, mut kill_switch: Receiver<()>) {
diff --git a/src/event.rs b/src/event.rs
index d5cdabe..400fcb6 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -5,10 +5,12 @@ use tokio::sync::mpsc::Sender;
use crate::app::TICK_RATE;
+#[derive(PartialEq)]
pub enum Event {
Terminate,
KeyPress(char),
Backspace,
+ Render,
}
// TODO
diff --git a/src/main.rs b/src/main.rs
index d10b40c..520f0a0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,6 @@
mod app;
pub mod event;
+pub mod state;
use std::{error::Error, fs::read_to_string, path::Path};
diff --git a/src/state.rs b/src/state.rs
new file mode 100644
index 0000000..18bcb68
--- /dev/null
+++ b/src/state.rs
@@ -0,0 +1,20 @@
+pub struct State {
+ pub current: usize,
+ pub buffer: String,
+ pub state: StateEnum,
+}
+
+pub enum StateEnum {
+ Typing,
+ Results(u8),
+}
+
+impl Default for State {
+ fn default() -> Self {
+ State {
+ current: 0,
+ buffer: String::new(),
+ state: StateEnum::Typing,
+ }
+ }
+}
XMR address: 854DmXNrxULU3ZFJVs4Wc8PFhbq29RhqHhY8W6cdWrtFN3qmooKyyeYPcDzZTNRxphhJ5UzASQfAdEMwSteVqymk28aLhqj