ongoing frame work, still experimenting

This commit is contained in:
emilis 2023-01-14 11:58:53 +00:00
parent 0b9f43b9d2
commit b66b5e5123
6 changed files with 140 additions and 110 deletions

31
Cargo.lock generated
View File

@ -357,17 +357,6 @@ dependencies = [
"instant", "instant",
] ]
[[package]]
name = "fed"
version = "0.1.0"
dependencies = [
"anyhow",
"misskey",
"signal-hook",
"termion",
"tokio 1.24.1",
]
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.9.2" version = "0.9.2"
@ -669,6 +658,16 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "kk"
version = "0.1.0"
dependencies = [
"anyhow",
"misskey",
"termion",
"tokio 1.24.1",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -1417,16 +1416,6 @@ dependencies = [
"opaque-debug", "opaque-debug",
] ]
[[package]]
name = "signal-hook"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.0" version = "1.4.0"

View File

@ -2,11 +2,7 @@ use std::io::{Stdout, Write};
use termion::{clear, cursor, raw::RawTerminal}; use termion::{clear, cursor, raw::RawTerminal};
use super::{ use super::{Environment, Event, Page};
frame::{self, FrameDef},
theme::Theme,
Event, Page,
};
type Result<T> = std::result::Result<T, anyhow::Error>; type Result<T> = std::result::Result<T, anyhow::Error>;
@ -17,15 +13,15 @@ pub enum Body {
} }
impl Body { impl Body {
fn echo(theme: Theme, 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,
"{theme}{clear}{start}Event: {}", "{theme}{clear}{start}{}",
event, env.frame.write(&format!("Event: {}", event), 1, 1),
theme = theme.display_string(), theme = env.theme.display_string(),
clear = clear::All, clear = clear::All,
start = cursor::Goto(1, 1) start = env.frame.goto(1, 1)
)?; )?;
screen.flush()?; screen.flush()?;
Ok(()) Ok(())
@ -35,24 +31,24 @@ impl Body {
impl Page for Body { impl Page for Body {
fn receive_event( fn receive_event(
&mut self, &mut self,
theme: Theme, env: Environment,
screen: &mut RawTerminal<Stdout>, screen: &mut RawTerminal<Stdout>,
event: Event, event: Event,
) -> Result<()> { ) -> Result<()> {
match self { match self {
Body::Signin(b) => b.receive_event(theme, screen, event)?, Body::Signin(b) => b.receive_event(env, screen, event)?,
Body::Echo => Body::echo(theme, screen, event)?, Body::Echo => Body::echo(env, screen, event)?,
}; };
Ok(()) Ok(())
} }
fn init(&self, theme: Theme, screen: &mut RawTerminal<Stdout>) -> Result<()> { fn init(&self, env: Environment, screen: &mut RawTerminal<Stdout>) -> Result<()> {
write!( write!(
screen, screen,
"{theme}{cursor}{clear}{fr}", "{theme}{clear}{cursor}{fr}",
fr = frame::draw_frame(theme, FrameDef::ByPercent(90, 90)), theme = env.theme.display_string(),
theme = theme.display_string(), fr = env.frame.frame_str(),
cursor = cursor::Goto(1, 1), cursor = env.frame.goto(0, 0),
clear = clear::All clear = clear::All
)?; )?;
screen.flush()?; screen.flush()?;
@ -89,20 +85,19 @@ impl SigninPage {
impl Page for SigninPage { impl Page for SigninPage {
fn receive_event( fn receive_event(
&mut self, &mut self,
theme: Theme, env: Environment,
screen: &mut RawTerminal<Stdout>, screen: &mut RawTerminal<Stdout>,
event: Event, event: Event,
) -> Result<()> { ) -> Result<()> {
Ok(()) Ok(())
} }
fn init(&self, theme: Theme, screen: &mut RawTerminal<Stdout>) -> Result<()> { fn init(&self, env: Environment, screen: &mut RawTerminal<Stdout>) -> Result<()> {
let fr = frame::draw_frame(theme, FrameDef::ByPercent(40, 40));
write!( write!(
screen, screen,
"{theme}{clear}{hide_cursor}{frame}", "{theme}{clear}{hide_cursor}{frame}",
frame = fr, frame = env.frame.frame_str(),
theme = theme.display_string(), theme = env.theme.display_string(),
clear = clear::All, clear = clear::All,
hide_cursor = cursor::Hide, hide_cursor = cursor::Hide,
)?; )?;

View File

@ -1,34 +1,85 @@
use std::{io, process}; use termion::cursor;
use termion::{color, cursor, screen::IntoAlternateScreen};
use super::theme::Theme;
const ESTIMATED_FRAME_BIT_SIZE: usize = 11; const ESTIMATED_FRAME_BIT_SIZE: usize = 11;
pub enum FrameDef { const FRAME_CHAR: char = ' ';
pub enum FrameSize {
ByAbsolute(u16, u16), ByAbsolute(u16, u16),
ByPercent(u16, u16), ByPercent(u16, u16),
} }
struct Frame { #[derive(Debug, Clone, Copy)]
start: (u16, u16), pub struct Frame {
end: (u16, u16), start: (u16, u16), // (w, h)
end: (u16, u16), // (w, h)
} }
impl Frame { impl Frame {
fn from_bottom_right(pos: (u16, u16), term: (u16, u16)) -> Self { #[inline]
pub fn goto(&self, x: u16, y: u16) -> String {
let cg = cursor::Goto(
(self.start.0 + x).min(self.end.0 - 1),
(self.start.1 + y).min(self.end.1 - 1),
);
// eprintln!(
// "goto x: {x} y: {y} -> x: {nx} y: {ny}",
// x = x,
// y = y,
// nx = cg.0,
// ny = cg.1
// );
// eprintln!("self start: {:#?} self end: {:#?}", self.start, self.end);
cg.into()
}
pub fn write(&self, s: &str, x: u16, y: u16) -> String {
let words = s.split('\n');
let (width, height) = self.size();
let mut lines_down = 0;
let mut curr_len = width;
let mut write_parts = Vec::with_capacity(s.len() / 5);
write_parts.push(self.goto(x, y));
for w in words {
// if curr_len + (w.len() as u16) > width {
// if y + 1 >= height {
// panic!("out of vertical space")
// }
// lines_down += 1;
// curr_len = 1;
// }
write_parts.push(self.goto(1, lines_down + y));
lines_down += 1;
write_parts.push(w.to_string());
}
write_parts.concat()
}
// (x, y)
#[inline]
pub fn size(&self) -> (u16, u16) {
(self.end.0 - self.start.0, self.end.1 - self.start.1)
}
pub fn from_terminal_size() -> Result<Self, anyhow::Error> {
let term = termion::terminal_size()?;
Ok(Self::from_bottom_right(term, term))
}
fn from_bottom_right((pos_h, pos_w): (u16, u16), (term_h, term_w): (u16, u16)) -> Self {
Self { Self {
start: (term.0 - pos.0, term.1 - pos.1), start: (1.max(term_w - pos_w), 1.max(term_h - pos_h)),
end: pos, end: (pos_w, pos_h),
} }
} }
fn frame_str(&self, frame_char: char) -> String { pub fn frame_str(&self) -> String {
let (w_len, h_len) = ( let (w_len, h_len) = (
(self.end.0 - self.start.0) as usize, (self.end.0 - self.start.0) as usize,
(self.end.1 - self.start.1 - 1) as usize, (self.end.1 - self.start.1 - 1) as usize,
); );
let width_str = frame_char.to_string().repeat(w_len + 1); let width_str = FRAME_CHAR.to_string().repeat(w_len + 1);
let mut frame = String::with_capacity((h_len * 2) + (w_len * 2) * ESTIMATED_FRAME_BIT_SIZE); let mut frame = String::with_capacity((h_len * 2) + (w_len * 2) * ESTIMATED_FRAME_BIT_SIZE);
let make_line = let make_line =
|y: u16| format!("{left}{}", width_str, left = cursor::Goto(self.start.0, y)); |y: u16| format!("{left}{}", width_str, left = cursor::Goto(self.start.0, y));
@ -36,67 +87,33 @@ impl Frame {
for y in self.start.1 + 1..self.end.1 { for y in self.start.1 + 1..self.end.1 {
frame.push_str(&format!( frame.push_str(&format!(
"{left}{char}{right}{char}", "{left}{char}{right}{char}",
left = cursor::Goto(self.start.0, y), left = self.goto(self.start.0, y),
right = cursor::Goto(self.end.0, y), right = self.goto(self.end.0, y),
char = frame_char, char = FRAME_CHAR,
)); ));
} }
frame.push_str(&make_line(self.end.1)); frame.push_str(&make_line(self.end.1));
frame.push_str(&self.goto(1, 1));
frame frame
} }
} }
#[cfg(test)] impl FrameSize {
mod test {
use super::Frame;
#[test]
fn test_idk() {
let x = Frame::from_bottom_right((16, 16), (32, 32)).frame_str('=');
println!("starting");
println!("{}", x);
println!("ending");
assert!(false)
}
}
impl FrameDef {
fn abs_size(&self) -> Frame { fn abs_size(&self) -> Frame {
let (term_height, term_width) = let (term_height, term_width) =
termion::terminal_size().expect("could not get terminal size"); termion::terminal_size().expect("could not get terminal size");
let pos = match self { let pos = match self {
FrameDef::ByAbsolute(h, w) => { FrameSize::ByAbsolute(h, w) => ((*h).min(term_height), (*w).min(term_width)),
let (mut h, mut w) = (*h, *w); FrameSize::ByPercent(h, w) => {
if h > term_height {
h = term_height;
}
if w > term_width {
w = term_width;
}
(h, w)
}
FrameDef::ByPercent(h, w) => {
// term_height = 100% // term_height = 100%
// x = h% // x = h%
// x = term_height * h / 100 // x = term_height * h / 100
let (h, w) = ( (
if *h > 100 { 100 } else { *h }, term_height * (*h).min(100) / 100,
if *w > 100 { 100 } else { *w }, term_width * (*w).min(100) / 100,
); )
// (h * 100 / term_height, w * 100 / term_width)
(term_height * h / 100, term_width * w / 100)
} }
}; };
Frame::from_bottom_right(pos, (term_height, term_width)) Frame::from_bottom_right(pos, (term_height, term_width))
} }
} }
pub fn draw_frame(theme: Theme, frame_size: FrameDef) -> String {
let frame_specs = frame_size.abs_size();
format!(
"{fg}{bg}{frame}",
fg = theme.colors.frame_fg.fg_string(),
bg = theme.colors.frame_bg.bg_string(),
frame = frame_specs.frame_str('='),
)
}

View File

@ -9,6 +9,7 @@ use tokio::sync::Mutex;
use tokio::time::Instant; use tokio::time::Instant;
use self::body::Body; use self::body::Body;
use self::frame::Frame;
use self::theme::Theme; use self::theme::Theme;
pub mod body; pub mod body;
@ -46,15 +47,40 @@ impl From<Key> for Event {
} }
pub trait Page { pub trait Page {
fn init(&self, theme: Theme, screen: &mut RawTerminal<Stdout>) -> Result<()>; fn init(&self, env: Environment, screen: &mut RawTerminal<Stdout>) -> Result<()>;
fn receive_event( fn receive_event(
&mut self, &mut self,
theme: Theme, env: Environment,
screen: &mut RawTerminal<Stdout>, screen: &mut RawTerminal<Stdout>,
event: Event, event: Event,
) -> Result<()>; ) -> Result<()>;
} }
#[derive(Debug, Clone, Copy)]
pub struct Environment {
pub theme: Theme,
pub frame: Frame,
}
impl Into<String> for Environment {
fn into(self) -> String {
format!(
"{frame}{theme}",
frame = self.frame.frame_str(),
theme = self.theme.display_string(),
)
}
}
impl Environment {
fn initial_must(theme: Theme) -> Self {
Self {
theme,
frame: Frame::from_terminal_size().expect("could not get terminal size"),
}
}
}
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()?);
@ -72,21 +98,24 @@ impl Screen {
} }
pub async fn start(mut self) -> Result<()> { pub async fn start(mut self) -> Result<()> {
let env = Environment::initial_must(self.theme);
{ {
let mut scr = self.screen.lock().await; let mut scr = self.screen.lock().await;
self.body.init(self.theme, &mut scr)?; self.body.init(env, &mut scr)?;
} }
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 { if let Event::Interrupt = ev {
if let Some(last) = self.last_interrupt && last.elapsed().as_millis() < 500 { if let Some(last) = self.last_interrupt {
if last.elapsed().as_millis() < 500 {
self.events_ch.close(); self.events_ch.close();
break; break;
} }
}
self.last_interrupt = Some(Instant::now()); self.last_interrupt = Some(Instant::now());
} }
self.body.receive_event(self.theme, &mut scr, ev)?; self.body.receive_event(env, &mut scr, ev)?;
} }
// Cleanup // Cleanup

View File

@ -66,6 +66,7 @@ impl Default for Theme {
} }
impl Theme { impl Theme {
#[inline]
pub fn display_string(&self) -> String { pub fn display_string(&self) -> String {
format!( format!(
"{primary_bg}{text}", "{primary_bg}{text}",

View File

@ -1,4 +1,3 @@
#![feature(let_chains)]
use std::process; use std::process;
use display::theme::Theme; use display::theme::Theme;