kk/src/display/mod.rs

113 lines
3.0 KiB
Rust

use std::fmt::Display;
use std::io::{self, Stdout, Write};
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal};
use termion::{clear, cursor};
use tokio::sync::mpsc::{self, Receiver, Sender};
use tokio::sync::Mutex;
use tokio::time::Instant;
use self::body::Body;
use self::theme::Theme;
pub mod body;
pub mod frame;
pub mod theme;
type Result<T> = std::result::Result<T, anyhow::Error>;
pub struct Screen {
screen: Mutex<RawTerminal<Stdout>>,
events_ch: Receiver<Event>,
body: Body,
last_interrupt: Option<Instant>,
theme: Theme,
}
#[derive(Debug)]
pub enum Event {
Key(Key),
Interrupt,
}
impl Display for Event {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Event::Key(key) => write!(f, "{}", format!("{:#?}", key).replace('\n', "\r\n")),
Event::Interrupt => write!(f, "Interrupt"),
}
}
}
impl From<Key> for Event {
fn from(value: Key) -> Self {
Event::Key(value)
}
}
pub trait Page {
fn init(&self, theme: Theme, screen: &mut RawTerminal<Stdout>) -> Result<()>;
fn receive_event(
&mut self,
theme: Theme,
screen: &mut RawTerminal<Stdout>,
event: Event,
) -> Result<()>;
}
impl Screen {
pub fn new(theme: Theme) -> Result<Self> {
let screen = Mutex::new(io::stdout().into_raw_mode()?);
let (send, recv) = mpsc::channel::<Event>(16);
tokio::spawn(async { Screen::input_loop(send).await });
// let body = Body::Signin(SigninPage::default());
let body = Body::Echo;
Ok(Self {
screen,
body,
theme,
events_ch: recv,
last_interrupt: None,
})
}
pub async fn start(mut self) -> Result<()> {
{
let mut scr = self.screen.lock().await;
self.body.init(self.theme, &mut scr)?;
}
while let Some(ev) = self.events_ch.recv().await {
let mut scr = self.screen.lock().await;
if let Event::Interrupt = ev {
if let Some(last) = self.last_interrupt && last.elapsed().as_millis() < 500 {
self.events_ch.close();
break;
}
self.last_interrupt = Some(Instant::now());
}
self.body.receive_event(self.theme, &mut scr, ev)?;
}
// Cleanup
let mut scr = self.screen.lock().await;
write!(scr, "{}{}", cursor::Goto(1, 1), clear::All)?;
Ok(())
}
async fn input_loop(snd: Sender<Event>) {
let input = io::stdin();
for key in input.keys() {
let event = match key.unwrap() {
Key::Ctrl(sub) => match sub {
'c' => Event::Interrupt,
key => Event::Key(Key::Ctrl(key)),
},
key => key.into(),
};
snd.send(event).await.unwrap();
}
}
}