# Description Duration can not be negative, and an underflow causes a panic. This should fix #12539 as from what I can tell that bug was caused in `nu-explore:📟:events` from subtracting durations, but I figured this might be more widespread, and saturating to zero generally makes sense. I also added the relevant clippy lint to try to prevent this from happening in the future. I can't think of a reason we would ever want to subtract durations without checking first. cc @fdncred # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib`
70 lines
1.8 KiB
Rust
70 lines
1.8 KiB
Rust
use std::{
|
|
io::Result,
|
|
time::{Duration, Instant},
|
|
};
|
|
|
|
use crossterm::event::{poll, read, Event, KeyEvent, KeyEventKind};
|
|
|
|
pub struct UIEvents {
|
|
tick_rate: Duration,
|
|
}
|
|
|
|
pub struct Cfg {
|
|
pub tick_rate: Duration,
|
|
}
|
|
|
|
impl Default for Cfg {
|
|
fn default() -> Cfg {
|
|
Cfg {
|
|
tick_rate: Duration::from_millis(250),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl UIEvents {
|
|
pub fn new() -> UIEvents {
|
|
UIEvents::with_config(Cfg::default())
|
|
}
|
|
|
|
pub fn with_config(config: Cfg) -> UIEvents {
|
|
UIEvents {
|
|
tick_rate: config.tick_rate,
|
|
}
|
|
}
|
|
|
|
pub fn next(&self) -> Result<Option<KeyEvent>> {
|
|
let now = Instant::now();
|
|
match poll(self.tick_rate) {
|
|
Ok(true) => {
|
|
if let Event::Key(event) = read()? {
|
|
// We are only interested in Pressed events;
|
|
// It's crucial because there are cases where terminal MIGHT produce false events;
|
|
// 2 events 1 for release 1 for press.
|
|
// Want to react only on 1 of them so we do.
|
|
if event.kind == KeyEventKind::Press {
|
|
return Ok(Some(event));
|
|
}
|
|
}
|
|
|
|
let time_spent = now.elapsed();
|
|
let rest = self.tick_rate.saturating_sub(time_spent);
|
|
|
|
Self { tick_rate: rest }.next()
|
|
}
|
|
Ok(false) => Ok(None),
|
|
Err(err) => Err(err),
|
|
}
|
|
}
|
|
|
|
pub fn try_next(&self) -> Result<Option<KeyEvent>> {
|
|
match poll(Duration::default()) {
|
|
Ok(true) => match read()? {
|
|
Event::Key(event) if event.kind == KeyEventKind::Press => Ok(Some(event)),
|
|
_ => Ok(None),
|
|
},
|
|
Ok(false) => Ok(None),
|
|
Err(err) => Err(err),
|
|
}
|
|
}
|
|
}
|