removed debug!, made subframes work
This commit is contained in:
parent
9f7be00735
commit
c70ecf5fbe
|
@ -0,0 +1 @@
|
||||||
|
max_width = 70
|
|
@ -2,25 +2,48 @@ use std::io::{Stdout, Write};
|
||||||
|
|
||||||
use termion::{clear, cursor, raw::RawTerminal};
|
use termion::{clear, cursor, raw::RawTerminal};
|
||||||
|
|
||||||
use super::{Environment, Event, Page};
|
use super::{
|
||||||
|
frame::Frame, theme::ColorSet, Environment, Event, Page,
|
||||||
|
};
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, anyhow::Error>;
|
type Result<T> = std::result::Result<T, anyhow::Error>;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Body {
|
pub enum Body {
|
||||||
Echo,
|
Echo,
|
||||||
// Signin(SigninPage),
|
Signin(SigninPage),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
fn echo(env: Environment, screen: &mut RawTerminal<Stdout>, event: Event) -> Result<()> {
|
fn echo(
|
||||||
|
env: Environment,
|
||||||
|
screen: &mut RawTerminal<Stdout>,
|
||||||
|
event: Event,
|
||||||
|
) -> Result<()> {
|
||||||
let event = format!("{}", event);
|
let event = format!("{}", event);
|
||||||
write!(
|
write!(
|
||||||
screen,
|
screen,
|
||||||
"{clear}{frame}{content}",
|
"{theme}{content}",
|
||||||
|
theme = env.theme.primary(),
|
||||||
|
content =
|
||||||
|
env.frame.writeln(&format!("Event: {}", event), 0, 0),
|
||||||
|
)?;
|
||||||
|
screen.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn echo_init(
|
||||||
|
&self,
|
||||||
|
env: Environment,
|
||||||
|
screen: &mut RawTerminal<Stdout>,
|
||||||
|
) -> Result<()> {
|
||||||
|
write!(
|
||||||
|
screen,
|
||||||
|
"{hide}{clear}{fr}{cursor}",
|
||||||
clear = clear::All,
|
clear = clear::All,
|
||||||
frame = env.frame.frame_str(env.theme.primary()),
|
hide = cursor::Hide,
|
||||||
content = env.frame.writeln(&format!("Event: {}", event), 0, 0),
|
fr = env.frame.frame_str(env.theme.primary()),
|
||||||
|
cursor = env.frame.goto(0, 0),
|
||||||
)?;
|
)?;
|
||||||
screen.flush()?;
|
screen.flush()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -35,71 +58,103 @@ impl Page for Body {
|
||||||
event: Event,
|
event: Event,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
// Body::Signin(b) => b.receive_event(env, screen, event)?,
|
Body::Signin(b) => b.receive_event(env, screen, event)?,
|
||||||
Body::Echo => Body::echo(env, screen, event)?,
|
Body::Echo => Body::echo(env, screen, event)?,
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&self, env: Environment, screen: &mut RawTerminal<Stdout>) -> Result<()> {
|
fn init(
|
||||||
write!(
|
&mut self,
|
||||||
screen,
|
env: Environment,
|
||||||
"{hide}{clear}{fr}{cursor}",
|
screen: &mut RawTerminal<Stdout>,
|
||||||
hide = cursor::Hide,
|
) -> Result<()> {
|
||||||
fr = env.frame.frame_str(env.theme.primary()),
|
match self {
|
||||||
cursor = env.frame.goto(0, 0),
|
Body::Echo => self.echo_init(env, screen),
|
||||||
clear = clear::All
|
Body::Signin(signin) => signin.init(env, screen),
|
||||||
)?;
|
}
|
||||||
screen.flush()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
// struct SigninPage {
|
pub struct SigninPage {
|
||||||
// hostname: String,
|
hostname: String,
|
||||||
// username: String,
|
username: String,
|
||||||
// cursor: SigninCursorLocation,
|
cursor: SigninCursorLocation,
|
||||||
// }
|
cursor_position: u16,
|
||||||
|
frame: Option<Frame>,
|
||||||
|
}
|
||||||
|
|
||||||
// #[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
// enum SigninCursorLocation {
|
enum SigninCursorLocation {
|
||||||
// Hostname,
|
Hostname,
|
||||||
// Username,
|
Username,
|
||||||
// Next,
|
Next,
|
||||||
// }
|
}
|
||||||
|
|
||||||
// impl Default for SigninCursorLocation {
|
impl Default for SigninCursorLocation {
|
||||||
// fn default() -> Self {
|
fn default() -> Self {
|
||||||
// Self::Hostname
|
Self::Hostname
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// impl SigninPage {
|
impl SigninPage {
|
||||||
// fn frame_string() -> String {
|
#[inline]
|
||||||
// format!("")
|
fn draw(&self) -> String {
|
||||||
// }
|
let frame = self.frame.unwrap();
|
||||||
// }
|
frame.write_centered("login kk", 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// impl Page for SigninPage {
|
impl Page for SigninPage {
|
||||||
// fn receive_event(
|
fn receive_event(
|
||||||
// &mut self,
|
&mut self,
|
||||||
// env: Environment,
|
env: Environment,
|
||||||
// screen: &mut RawTerminal<Stdout>,
|
screen: &mut RawTerminal<Stdout>,
|
||||||
// event: Event,
|
event: Event,
|
||||||
// ) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Ok(())
|
match event {
|
||||||
// }
|
Event::Key(key) => match key {
|
||||||
|
termion::event::Key::Char(_) => {}
|
||||||
|
termion::event::Key::Up => {}
|
||||||
|
termion::event::Key::Down => {}
|
||||||
|
termion::event::Key::Backspace => {}
|
||||||
|
termion::event::Key::Delete => {}
|
||||||
|
termion::event::Key::Left => {}
|
||||||
|
termion::event::Key::Right => {}
|
||||||
|
//
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// fn init(&self, env: Environment, screen: &mut RawTerminal<Stdout>) -> Result<()> {
|
Ok(())
|
||||||
// write!(
|
}
|
||||||
// screen,
|
|
||||||
// "{frame}{hide_cursor}",
|
|
||||||
// frame = env.frame.frame_str(&env.theme, env.theme.primary()),
|
|
||||||
// hide_cursor = cursor::Hide,
|
|
||||||
// )?;
|
|
||||||
// screen.flush()?;
|
|
||||||
|
|
||||||
// Ok(())
|
fn init(
|
||||||
// }
|
&mut self,
|
||||||
// }
|
env: Environment,
|
||||||
|
screen: &mut RawTerminal<Stdout>,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.frame = Some(env.frame.sub(
|
||||||
|
env.theme.colors.subwin,
|
||||||
|
super::frame::FrameSize::ByPercent(80, 80),
|
||||||
|
2,
|
||||||
|
));
|
||||||
|
write!(
|
||||||
|
screen,
|
||||||
|
"{theme}{clear}{frame}{subframe}{hide_cursor}{content}",
|
||||||
|
theme = env.theme.frame(),
|
||||||
|
clear = clear::All,
|
||||||
|
frame = env.frame.frame_str(env.theme.primary()),
|
||||||
|
subframe = self
|
||||||
|
.frame
|
||||||
|
.unwrap()
|
||||||
|
.frame_str(ColorSet::default().to_string()),
|
||||||
|
hide_cursor = cursor::Hide,
|
||||||
|
content = self.draw(),
|
||||||
|
)?;
|
||||||
|
screen.flush()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
pub fn debug_append(s: String) {
|
|
||||||
std::process::Command::new("fish")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(format!("echo \"{}\" >> debug.log", s))
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
macro_rules! debug {
|
|
||||||
($fmt_string:expr, $($arg:tt)*) => {
|
|
||||||
$crate::display::debug::debug_append(
|
|
||||||
format!($fmt_string, $($arg)*)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
pub(crate) use debug;
|
|
|
@ -1,11 +1,9 @@
|
||||||
use termion::{clear, cursor};
|
use termion::{clear, cursor, raw::RawTerminal};
|
||||||
|
|
||||||
const ESTIMATED_FRAME_BIT_SIZE: usize = 11;
|
const ESTIMATED_FRAME_BIT_SIZE: usize = 11;
|
||||||
use std::io::Write;
|
use std::io::{Stdout, Write};
|
||||||
|
|
||||||
use crate::display::debug::debug;
|
use super::theme::{ColorSet, Theme};
|
||||||
|
|
||||||
use super::theme::Theme;
|
|
||||||
|
|
||||||
const FRAME_CHAR_HORIZONTAL: char = ' ';
|
const FRAME_CHAR_HORIZONTAL: char = ' ';
|
||||||
const FRAME_CHAR_VERTICAL: char = ' ';
|
const FRAME_CHAR_VERTICAL: char = ' ';
|
||||||
|
@ -21,15 +19,32 @@ pub struct Frame {
|
||||||
start: (u16, u16),
|
start: (u16, u16),
|
||||||
end: (u16, u16),
|
end: (u16, u16),
|
||||||
width: u16,
|
width: u16,
|
||||||
theme: Theme,
|
theme: ColorSet,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Frame {
|
impl Frame {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn sub(
|
||||||
|
&self,
|
||||||
|
theme: ColorSet,
|
||||||
|
size: FrameSize,
|
||||||
|
width: u16,
|
||||||
|
) -> Frame {
|
||||||
|
let (p_width, p_height) = self.size();
|
||||||
|
size.abs_size((p_width - 1, p_height - 1), theme, width)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn goto(&self, x: u16, y: u16) -> String {
|
pub fn goto(&self, x: u16, y: u16) -> String {
|
||||||
let cg = cursor::Goto(
|
let cg = cursor::Goto(
|
||||||
(self.width + self.start.0 + x).min(self.end.0 - self.width),
|
self.width.max(
|
||||||
(self.width + self.start.1 + y).min(self.end.1 - self.width),
|
(self.width + self.start.0 + x)
|
||||||
|
.min(self.end.0 - self.width),
|
||||||
|
),
|
||||||
|
self.width.max(
|
||||||
|
(self.width + self.start.1 + y)
|
||||||
|
.min(self.end.1 - self.width),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
cg.into()
|
cg.into()
|
||||||
}
|
}
|
||||||
|
@ -43,38 +58,73 @@ impl Frame {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_clear_to_end(&self, s: &str) -> String {
|
||||||
|
let clear_length = self.size().0 as usize
|
||||||
|
- s.len()
|
||||||
|
- (self.width * 2) as usize;
|
||||||
|
format!("{}{}", s, " ".repeat(clear_length))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn writeln(&self, s: &str, x: u16, y: u16) -> String {
|
pub fn writeln(&self, s: &str, x: u16, y: u16) -> String {
|
||||||
let words = s.split('\n').collect::<Vec<&str>>();
|
let words = s.split('\n').collect::<Vec<&str>>();
|
||||||
let mut out_words = Vec::with_capacity(words.len());
|
let mut out_words = Vec::with_capacity(words.len());
|
||||||
for i in 0..words.len() {
|
for i in 0..words.len() {
|
||||||
out_words.push(format!(
|
out_words.push(format!(
|
||||||
"{ret}{str}",
|
"{ret}{str}",
|
||||||
str = words[i],
|
str = self.write_clear_to_end(words[i]),
|
||||||
ret = self.goto(x, y + i as u16),
|
ret = self.goto(x, y + i as u16),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
out_words.concat()
|
out_words.concat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_frame_line(&self, y: u16, length: u16) -> String {
|
||||||
|
self.goto_internal(0, y)
|
||||||
|
+ &FRAME_CHAR_HORIZONTAL
|
||||||
|
.to_string()
|
||||||
|
.repeat(length as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_centered(&self, s: &str, y: u16) -> String {
|
||||||
|
let (width, _) = self.size();
|
||||||
|
let words = s.split('\n').collect::<Vec<&str>>();
|
||||||
|
let mut out_words = Vec::with_capacity(words.len());
|
||||||
|
for i in 0..words.len() {
|
||||||
|
let word = words[i];
|
||||||
|
let x = width - (word.len() as u16).min(width);
|
||||||
|
out_words.push(format!(
|
||||||
|
"{ret}{str}",
|
||||||
|
str = word,
|
||||||
|
ret = self.goto(x / 2, y + i as u16),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
out_words.concat()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn size(&self) -> (u16, u16) {
|
pub fn size(&self) -> (u16, u16) {
|
||||||
(self.end.0 - self.start.0, self.end.1 - self.start.1)
|
(self.end.0 - self.start.0, self.end.1 - self.start.1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_terminal_size(theme: Theme, width: u16) -> Result<Self, anyhow::Error> {
|
pub fn from_terminal_size(
|
||||||
|
theme: ColorSet,
|
||||||
|
width: u16,
|
||||||
|
) -> Result<Self, anyhow::Error> {
|
||||||
let term = termion::terminal_size()?;
|
let term = termion::terminal_size()?;
|
||||||
// Swap term dimensions to use x/y
|
// Swap term dimensions to use x/y
|
||||||
let term = (term.1, term.0);
|
// let term = (term.1, term.0);
|
||||||
Ok(Self::from_bottom_right(term, term, width, theme))
|
Ok(Self::from_bottom_right(term, term, width, theme))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_bottom_right(
|
fn from_bottom_right(
|
||||||
(pos_h, pos_w): (u16, u16),
|
(pos_w, pos_h): (u16, u16),
|
||||||
(term_h, term_w): (u16, u16),
|
(term_w, term_h): (u16, u16),
|
||||||
width: u16,
|
width: u16,
|
||||||
theme: Theme,
|
theme: ColorSet,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
start: (1.max(term_w - pos_w), 1.max(term_h - pos_h)),
|
start: (1.max(term_w - pos_w), 1.max(term_h - pos_h)),
|
||||||
|
@ -90,32 +140,34 @@ impl Frame {
|
||||||
}
|
}
|
||||||
self.goto_internal(0, 0);
|
self.goto_internal(0, 0);
|
||||||
let (w_len, h_len) = self.size();
|
let (w_len, h_len) = self.size();
|
||||||
let width_str = FRAME_CHAR_HORIZONTAL.to_string().repeat(w_len as usize + 1);
|
let width_str = FRAME_CHAR_HORIZONTAL
|
||||||
|
.to_string()
|
||||||
|
.repeat(w_len as usize + 1);
|
||||||
let mut frame = Vec::with_capacity(h_len as usize + 4);
|
let mut frame = Vec::with_capacity(h_len as usize + 4);
|
||||||
let frame_theme = self.theme.frame();
|
let frame_theme = self.theme.to_string();
|
||||||
let body_clear = body_theme.clone() + &clear::CurrentLine.to_string();
|
let body_clear =
|
||||||
frame.push(frame_theme.clone());
|
body_theme.clone() + &" ".repeat(w_len as usize);
|
||||||
|
frame.push(self.theme.to_string());
|
||||||
|
|
||||||
let make_line =
|
|
||||||
|y: u16| format!("{left}{}", width_str, left = cursor::Goto(self.start.0, y));
|
|
||||||
for y in 0..self.width {
|
for y in 0..self.width {
|
||||||
frame.push(make_line(self.start.1 + y));
|
frame.push(self.write_frame_line(y, w_len));
|
||||||
}
|
}
|
||||||
for y in self.width + self.start.1..self.end.1 - self.width {
|
for y in self.width..h_len - self.width {
|
||||||
frame.push(format!(
|
frame.push(format!(
|
||||||
"{left}{body_clear}{frame_theme}{char}{right}{char}",
|
"{left}{body_clear}{frame_theme}{left}{char}{right}{char}",
|
||||||
body_clear = &body_clear,
|
body_clear = &body_clear,
|
||||||
frame_theme = &frame_theme,
|
frame_theme = &frame_theme,
|
||||||
left = cursor::Goto(self.start.0, y),
|
left = self.goto_internal(0, y),
|
||||||
right = cursor::Goto(self.start.0 + self.end.0 - self.width, y),
|
right = self.goto_internal(w_len - self.width, y),
|
||||||
char = FRAME_CHAR_VERTICAL.to_string().repeat(self.width as usize),
|
char = FRAME_CHAR_VERTICAL
|
||||||
|
.to_string()
|
||||||
|
.repeat(self.width as usize),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
for y in self.end.1 - self.width - 1..self.end.1 {
|
for y in h_len - self.width..h_len {
|
||||||
frame.push(make_line(self.start.1 + y));
|
frame.push(self.write_frame_line(y, w_len));
|
||||||
}
|
}
|
||||||
frame.push(make_line(self.end.1));
|
frame.push(self.goto(0, 0));
|
||||||
frame.push(self.goto(1, 1));
|
|
||||||
frame.push(body_theme);
|
frame.push(body_theme);
|
||||||
frame.concat()
|
frame.concat()
|
||||||
}
|
}
|
||||||
|
@ -123,22 +175,32 @@ impl Frame {
|
||||||
|
|
||||||
impl FrameSize {
|
impl FrameSize {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn abs_size(&self, theme: Theme, width: u16) -> Frame {
|
fn abs_size(
|
||||||
let (term_height, term_width) =
|
&self,
|
||||||
termion::terminal_size().expect("could not get terminal size");
|
(term_width, term_height): (u16, u16),
|
||||||
|
theme: ColorSet,
|
||||||
|
width: u16,
|
||||||
|
) -> Frame {
|
||||||
let pos = match self {
|
let pos = match self {
|
||||||
FrameSize::ByAbsolute(h, w) => ((*h).min(term_height), (*w).min(term_width)),
|
FrameSize::ByAbsolute(h, w) => {
|
||||||
|
((*w).min(term_width), (*h).min(term_height))
|
||||||
|
}
|
||||||
FrameSize::ByPercent(h, w) => {
|
FrameSize::ByPercent(h, w) => {
|
||||||
// term_height = 100%
|
// term_height = 100%
|
||||||
// x = h%
|
// x = h%
|
||||||
//
|
//
|
||||||
// term_height * h / 100 = x
|
// term_height * h / 100 = x
|
||||||
(
|
(
|
||||||
term_height * (*h).min(100) / 100,
|
|
||||||
term_width * (*w).min(100) / 100,
|
term_width * (*w).min(100) / 100,
|
||||||
|
term_height * (*h).min(100) / 100,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Frame::from_bottom_right(pos, (term_height, term_width), width, theme)
|
Frame::from_bottom_right(
|
||||||
|
pos,
|
||||||
|
(term_width, term_height),
|
||||||
|
width,
|
||||||
|
theme,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,47 +8,74 @@ use tokio::sync::mpsc::{self, Receiver, Sender};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use tokio::time::Instant;
|
use tokio::time::Instant;
|
||||||
|
|
||||||
use self::body::Body;
|
use self::body::{Body, SigninPage};
|
||||||
use self::frame::Frame;
|
use self::frame::Frame;
|
||||||
use self::theme::{Color, Theme};
|
use self::theme::{Color, Theme};
|
||||||
|
|
||||||
pub mod body;
|
pub mod body;
|
||||||
pub mod debug;
|
|
||||||
pub mod frame;
|
pub mod frame;
|
||||||
pub mod theme;
|
pub mod theme;
|
||||||
type Result<T> = std::result::Result<T, anyhow::Error>;
|
type Result<T> = std::result::Result<T, anyhow::Error>;
|
||||||
|
|
||||||
pub struct Screen {
|
pub struct Screen {
|
||||||
screen: Mutex<RawTerminal<Stdout>>,
|
screen: Mutex<RawTerminal<Stdout>>,
|
||||||
events_ch: Receiver<Event>,
|
events_ch: Receiver<KeyEvent>,
|
||||||
body: Body,
|
body: Body,
|
||||||
last_interrupt: Option<Instant>,
|
last_interrupt: Option<Instant>,
|
||||||
theme: Theme,
|
theme: Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
enum KeyEvent {
|
||||||
Key(Key),
|
Key(Key),
|
||||||
Interrupt,
|
Interrupt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Event {
|
#[derive(Debug)]
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
pub enum Event {
|
||||||
match self {
|
Key(Key),
|
||||||
Event::Key(key) => write!(f, "{}", format!("{:#?}", key).replace('\n', "\r\n")),
|
}
|
||||||
Event::Interrupt => write!(f, "Interrupt"),
|
|
||||||
|
impl From<KeyEvent> for Event {
|
||||||
|
fn from(value: KeyEvent) -> Self {
|
||||||
|
match value {
|
||||||
|
KeyEvent::Key(key) => Event::Key(key),
|
||||||
|
_ => panic!(
|
||||||
|
"FIXME: this event should be handled outside of children"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Key> for Event {
|
impl Display for Event {
|
||||||
|
fn fmt(
|
||||||
|
&self,
|
||||||
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Event::Key(key) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
format!("{:#?}", key)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Key> for KeyEvent {
|
||||||
fn from(value: Key) -> Self {
|
fn from(value: Key) -> Self {
|
||||||
Event::Key(value)
|
KeyEvent::Key(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Page {
|
pub trait Page {
|
||||||
fn init(&self, env: Environment, screen: &mut RawTerminal<Stdout>) -> Result<()>;
|
fn init(
|
||||||
|
&mut self,
|
||||||
|
env: Environment,
|
||||||
|
screen: &mut RawTerminal<Stdout>,
|
||||||
|
) -> Result<()>;
|
||||||
fn receive_event(
|
fn receive_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: Environment,
|
env: Environment,
|
||||||
|
@ -65,11 +92,7 @@ pub struct Environment {
|
||||||
|
|
||||||
impl Into<String> for Environment {
|
impl Into<String> for Environment {
|
||||||
fn into(self) -> String {
|
fn into(self) -> String {
|
||||||
format!(
|
self.frame.frame_str(self.theme.primary())
|
||||||
"{frame}{theme}",
|
|
||||||
frame = self.frame.frame_str(self.theme.primary()),
|
|
||||||
theme = self.theme.primary(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +103,8 @@ impl Environment {
|
||||||
.unwrap_or(1);
|
.unwrap_or(1);
|
||||||
Self {
|
Self {
|
||||||
theme,
|
theme,
|
||||||
frame: Frame::from_terminal_size(theme, w).expect("could not get terminal size"),
|
frame: Frame::from_terminal_size(theme.colors.frame, w)
|
||||||
|
.expect("could not get terminal size"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,10 +117,10 @@ impl Environment {
|
||||||
impl Screen {
|
impl Screen {
|
||||||
pub fn new(theme: Theme) -> Result<Self> {
|
pub fn new(theme: Theme) -> Result<Self> {
|
||||||
let screen = Mutex::new(io::stdout().into_raw_mode()?);
|
let screen = Mutex::new(io::stdout().into_raw_mode()?);
|
||||||
let (send, recv) = mpsc::channel::<Event>(16);
|
let (send, recv) = mpsc::channel::<KeyEvent>(16);
|
||||||
tokio::spawn(async { Screen::input_loop(send).await });
|
tokio::spawn(async { Screen::input_loop(send).await });
|
||||||
// let body = Body::Signin(SigninPage::default());
|
let body = Body::Signin(SigninPage::default());
|
||||||
let body = Body::Echo;
|
// let body = Body::Echo;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
screen,
|
screen,
|
||||||
body,
|
body,
|
||||||
|
@ -115,16 +139,22 @@ impl Screen {
|
||||||
|
|
||||||
while let Some(ev) = self.events_ch.recv().await {
|
while let Some(ev) = self.events_ch.recv().await {
|
||||||
let mut scr = self.screen.lock().await;
|
let mut scr = self.screen.lock().await;
|
||||||
if let Event::Interrupt = ev {
|
match ev {
|
||||||
if let Some(last) = self.last_interrupt {
|
KeyEvent::Interrupt => {
|
||||||
if last.elapsed().as_millis() < 500 {
|
if let Some(last) = self.last_interrupt {
|
||||||
self.events_ch.close();
|
if last.elapsed().as_millis() < 500 {
|
||||||
break;
|
self.events_ch.close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
self.last_interrupt = Some(Instant::now());
|
||||||
}
|
}
|
||||||
self.last_interrupt = Some(Instant::now());
|
KeyEvent::Key(key) => self.body.receive_event(
|
||||||
|
env,
|
||||||
|
&mut scr,
|
||||||
|
Event::Key(key),
|
||||||
|
)?,
|
||||||
}
|
}
|
||||||
self.body.receive_event(env, &mut scr, ev)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
|
@ -142,13 +172,13 @@ impl Screen {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn input_loop(snd: Sender<Event>) {
|
async fn input_loop(snd: Sender<KeyEvent>) {
|
||||||
let input = io::stdin();
|
let input = io::stdin();
|
||||||
for key in input.keys() {
|
for key in input.keys() {
|
||||||
let event = match key.unwrap() {
|
let event = match key.unwrap() {
|
||||||
Key::Ctrl(sub) => match sub {
|
Key::Ctrl(sub) => match sub {
|
||||||
'c' => Event::Interrupt,
|
'c' => KeyEvent::Interrupt,
|
||||||
key => Event::Key(Key::Ctrl(key)),
|
key => KeyEvent::Key(Key::Ctrl(key)),
|
||||||
},
|
},
|
||||||
key => key.into(),
|
key => key.into(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,15 @@ pub struct ColorSet {
|
||||||
pub bg: Color,
|
pub bg: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for ColorSet {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
fg: "#ffffff".into(),
|
||||||
|
bg: "#000000".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToString for ColorSet {
|
impl ToString for ColorSet {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
|
@ -24,6 +33,7 @@ impl ToString for ColorSet {
|
||||||
pub struct Colors {
|
pub struct Colors {
|
||||||
pub primary: ColorSet,
|
pub primary: ColorSet,
|
||||||
pub frame: ColorSet,
|
pub frame: ColorSet,
|
||||||
|
pub subwin: ColorSet,
|
||||||
}
|
}
|
||||||
impl Theme {}
|
impl Theme {}
|
||||||
|
|
||||||
|
@ -54,9 +64,12 @@ impl From<&str> for Color {
|
||||||
i = 1;
|
i = 1;
|
||||||
}
|
}
|
||||||
Self(color::Rgb(
|
Self(color::Rgb(
|
||||||
u8::from_str_radix(&value[i..i + 2], 16).expect("red hex"),
|
u8::from_str_radix(&value[i..i + 2], 16)
|
||||||
u8::from_str_radix(&value[i + 2..i + 4], 16).expect("green hex"),
|
.expect("red hex"),
|
||||||
u8::from_str_radix(&value[i + 4..i + 6], 16).expect("blue hex"),
|
u8::from_str_radix(&value[i + 2..i + 4], 16)
|
||||||
|
.expect("green hex"),
|
||||||
|
u8::from_str_radix(&value[i + 4..i + 6], 16)
|
||||||
|
.expect("blue hex"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,6 +86,7 @@ impl Default for Theme {
|
||||||
fg: "#ffe6ff".into(),
|
fg: "#ffe6ff".into(),
|
||||||
bg: "#330033".into(),
|
bg: "#330033".into(),
|
||||||
},
|
},
|
||||||
|
subwin: ColorSet{ fg: "#ffe6ff".into(), bg: "#110011".into() },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue