Guard ctrl+c against an accidental quit

QuitGuard sees every key first and says what to do with it. It never reads
input or the clock itself: the app passes the key, the time and whether work
is running.
use std::time::Instant;use pito_footer::{Guard, Mode, QuitGuard, Wording};
let mut guard = QuitGuard::new( Wording::new("Press ctrl+c again to quit") .ask("The import is still running. Quit anyway?"),).mode(Mode::Ask);
match guard.key(key, Instant::now(), importing) { Guard::Quit => return Ok(()), Guard::Held => {} _ => app.key(key),}Guard |
Means |
|---|---|
Pass |
the key is the app’s |
Held |
the guard used it: it armed, or it is asking |
Quit |
quit now |
Mode::Twice, the default: the first ctrl+c arms and shows the app’s “again” notice, a second inside the window quits, and any other key disarms.Mode::Ask: the same, but when the app says work is running the second press opens a confirm with theaskquestion first. A further ctrl+c while it asks answers No.Mode::Once: the first ctrl+c quits.
The window is 2 seconds unless the app sets window(Duration), and the keys
are ctrl+c unless it sets triggers(&[..]). A screen that passes every key
through, such as an embedded terminal, simply doesn’t hand its keys to the
guard.
Draw it
Section titled “Draw it”The guard gives the footer its notice while armed and its confirm while asking:
use pito_footer::{Footer, Notice, Words};
let words = Words::new("Yes", "No").hint("y/n choose · enter accept · esc cancel");let footer = Footer::new(&HINTS) .notice(guard.notice().map(Notice::accent)) .confirm(guard.bar(words));Clear the notice on time
Section titled “Clear the notice on time”deadline() is when the armed notice ends. Wait for input until then, and call
tick(now), which returns true when it disarmed, so the app redraws:
use std::time::{Duration, Instant};use ratatui::crossterm::event;
let wait = guard .deadline() .map_or(Duration::from_secs(60), |at| at.saturating_duration_since(Instant::now()));if !event::poll(wait)? { guard.tick(Instant::now()); continue;}armed() and asking() tell the app the guard’s state, and cancel() drops
both.