From 8d6446d886e4d2523fba633e7207cd92ac869478 Mon Sep 17 00:00:00 2001 From: Austen Adler Date: Fri, 14 May 2021 19:25:01 -0400 Subject: [PATCH] Separate out event logic again --- src/main.rs | 45 +++++--------- src/util/event.rs | 147 +++++++++++++++++----------------------------- 2 files changed, 67 insertions(+), 125 deletions(-) diff --git a/src/main.rs b/src/main.rs index 502ca6e..3d6f896 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,8 +2,9 @@ #![allow(dead_code)] mod calc; -mod util; mod format; +mod util; +use util::event::{Event, Events}; use calc::constants::{CalculatorDisplayMode, CalculatorState, RegisterState}; use calc::errors::CalculatorResult; @@ -19,9 +20,7 @@ use std::convert::TryFrom; use std::io::Write; use std::sync::mpsc; use std::thread; -use std::time::{Duration, Instant}; use std::{error::Error, io}; -// use termion::{event::Key, raw::IntoRawMode, screen::AlternateScreen}; use tui::{ backend::CrosstermBackend, layout::{Constraint, Direction, Layout}, @@ -31,9 +30,6 @@ use tui::{ widgets::{Block, Borders, Clear, List, ListItem, Paragraph}, Terminal, }; -use util::event::Event; - -const TICK_RATE: Duration = Duration::from_millis(250); struct Dimensions { width: u16, @@ -78,29 +74,9 @@ fn main() -> Result<(), Box> { let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; - let (tx, rx) = mpsc::channel(); + let events = Events::new(250); let mut app = App::default(); - thread::spawn(move || { - let mut last_tick = Instant::now(); - loop { - // poll for tick rate duration, if no events, sent tick event. - let timeout = TICK_RATE - .checked_sub(last_tick.elapsed()) - .unwrap_or_else(|| Duration::from_secs(0)); - if event::poll(timeout).unwrap() { - if let CEvent::Key(key) = event::read().unwrap() { - tx.send(Event::Input(key)).unwrap(); - } - } - - if last_tick.elapsed() >= TICK_RATE { - tx.send(Event::Tick).unwrap(); - last_tick = Instant::now(); - } - } - }); - 'outer: loop { terminal.draw(|f| { let chunks = Layout::default() @@ -304,7 +280,7 @@ fn main() -> Result<(), Box> { } })?; - if let Event::Input(key) = rx.recv()? { + if let Event::Input(key) = events.next()? { app.error_msg = match handle_key(&mut app, key) { // Exit the program Ok(CalculatorResponse::Quit) => break 'outer, @@ -314,7 +290,7 @@ fn main() -> Result<(), Box> { } // Slurp events without a redraw - for e in rx.try_iter() { + for e in events.try_iter() { match e { Event::Input(key) => { app.error_msg = match handle_key(&mut app, key) { @@ -366,7 +342,7 @@ fn handle_key(app: &mut App, key: KeyEvent) -> CalculatorResult CalculatorResult { app.calculator.take_input(c)?; } + KeyEvent { + code: KeyCode::Char(c), + modifiers: KeyModifiers::SHIFT, + } => { + for c in (c.to_uppercase()) { + app.calculator.take_input(c)?; + } + } _ => {} }, (AppState::Help, _) => match key { @@ -486,4 +470,3 @@ fn draw_clippy_rect(c: ClippyRectangle, f: &mut Frame { Input(I), Tick, } -// /// A small event handler that wrap termion input and tick events. Each event -// /// type is handled in its own thread and returned to a common `Receiver` -// #[allow(dead_code)] -// pub struct Events { -// rx: mpsc::Receiver>, -// tx: mpsc::Sender>, -// input_handle: thread::JoinHandle<()>, -// ignore_exit_key: Arc, -// tick_handle: thread::JoinHandle<()>, -// } +pub struct Events { + rx: mpsc::Receiver>, + tx: mpsc::Sender>, + tick_handle: thread::JoinHandle<()>, +} -// #[derive(Debug, Clone, Copy)] -// pub struct Config { -// pub exit_key: KeyEvent, -// pub tick_rate: Duration, -// } +impl Events { + pub fn new(tick_rate: u64) -> Events { + let (tx, rx) = mpsc::channel(); + let tick_handle = { + let tx = tx.clone(); + thread::spawn(move || { + let mut last_tick = Instant::now(); + let tick_rate = Duration::from_millis(tick_rate); + loop { + // poll for tick rate duration, if no events, sent tick event. + let timeout = tick_rate + .checked_sub(last_tick.elapsed()) + .unwrap_or_else(|| Duration::from_secs(0)); + if event::poll(timeout).unwrap() { + if let CEvent::Key(key) = event::read().unwrap() { + tx.send(Event::Input(key)).unwrap(); + } + } -// impl Default for Config { -// fn default() -> Self { -// Config { -// exit_key: KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE), -// tick_rate: Duration::from_millis(250), -// } -// } -// } + if last_tick.elapsed() >= tick_rate { + tx.send(Event::Tick).unwrap(); + last_tick = Instant::now(); + } + } + }) + }; + Events { + tx: tx, + rx: rx, + tick_handle, + } + } -// impl Events { -// pub fn new() -> Events { -// Events::with_config(Config::default()) -// } + pub fn next(&self) -> Result, mpsc::RecvError> { + self.rx.recv() + } -// pub fn with_config(config: Config) -> Events { -// let (tx, rx) = mpsc::channel(); -// let mac_tx = tx.clone(); -// let ignore_exit_key = Arc::new(AtomicBool::new(true)); -// let input_handle = { -// let tx = tx.clone(); -// let ignore_exit_key = ignore_exit_key.clone(); -// thread::spawn(move || { -// let stdin = io::stdin(); -// for evt in stdin.keys() { -// if let Ok(key) = evt { -// if let Err(err) = tx.send(Event::Input(key)) { -// eprintln!("{}", err); -// return; -// } -// if !ignore_exit_key.load(Ordering::Relaxed) && key == config.exit_key { -// return; -// } -// } -// } -// }) -// }; -// let tick_handle = { -// thread::spawn(move || loop { -// if tx.send(Event::Tick).is_err() { -// break; -// } -// thread::sleep(config.tick_rate); -// }) -// }; -// Events { -// rx, -// tx: mac_tx, -// ignore_exit_key, -// input_handle, -// tick_handle, -// } -// } - -// pub fn next(&self) -> Result, mpsc::RecvError> { -// self.rx.recv() -// } - -// pub fn try_iter(&self) -> TryIter> { -// self.rx.try_iter() -// } - -// #[allow(dead_code)] -// pub fn disable_exit_key(&mut self) { -// self.ignore_exit_key.store(true, Ordering::Relaxed); -// } - -// #[allow(dead_code)] -// pub fn enable_exit_key(&mut self) { -// self.ignore_exit_key.store(false, Ordering::Relaxed); -// } -// } + pub fn try_iter(&self) -> TryIter> { + self.rx.try_iter() + } +}