113 lines
3.0 KiB
Rust
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();
|
|
}
|
|
}
|
|
}
|