Files
pomodoro/src/ui.rs
T

85 lines
2.3 KiB
Rust

use std::io::stdout;
use std::io::Write;
use anyhow::{bail, Result};
use crossterm::{
cursor::{self, MoveTo},
event::{Event, EventStream, KeyCode, KeyModifiers},
terminal::{self, disable_raw_mode, enable_raw_mode},
ExecutableCommand, QueueableCommand,
};
use futures::StreamExt;
use tokio::sync::broadcast::Sender;
use crate::message::Message;
pub(crate) struct Ui {
sender: Sender<Message>,
}
impl Ui {
pub(crate) fn new(sender: Sender<Message>) -> Self {
Self { sender }
}
pub(crate) async fn main_loop(self) -> Result<()> {
let mut reader = EventStream::new();
loop {
enable_raw_mode()?;
stdout().execute(cursor::Hide)?;
if let Some(Ok(Event::Key(key_event))) = reader.next().await {
let should_exit = self.handle_key_event(key_event)?;
if should_exit {
return Ok(());
};
}
}
}
fn handle_key_event(&self, key_event: crossterm::event::KeyEvent) -> Result<bool> {
if Self::is_char_q(key_event) {
self.prepare_for_program_quit()?;
return Ok(true);
};
if Self::is_ctrl_c(key_event) {
self.prepare_for_program_quit()?;
self.sender.send(Message::Quit)?;
bail!("<Ctrl-C>");
}
if Self::is_space(key_event) {
self.sender.send(Message::TogglePause)?;
}
Ok(false)
}
fn prepare_for_program_quit(&self) -> Result<()> {
self.sender.send(Message::Quit)?;
disable_raw_mode()?;
stdout().execute(cursor::Show)?;
Ok(())
}
fn is_char_q(key_event: crossterm::event::KeyEvent) -> bool {
key_event.code == KeyCode::Char('q')
}
fn is_space(key_event: crossterm::event::KeyEvent) -> bool {
key_event.code == KeyCode::Char(' ')
}
fn is_ctrl_c(key_event: crossterm::event::KeyEvent) -> bool {
key_event.code == KeyCode::Char('c') && key_event.modifiers.contains(KeyModifiers::CONTROL)
}
}
pub(crate) fn clear_terminal() -> Result<()> {
let mut stdout = stdout();
stdout.queue(terminal::Clear(terminal::ClearType::All))?;
stdout.queue(terminal::Clear(terminal::ClearType::Purge))?;
stdout.queue(MoveTo(0, 0))?;
stdout.flush()?;
Ok(())
}