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",
]
[[package]]
name = "fed"
version = "0.1.0"
dependencies = [
"anyhow",
"misskey",
"signal-hook",
"termion",
"tokio 1.24.1",
]
[[package]]
name = "flume"
version = "0.9.2"
@ -669,6 +658,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kk"
version = "0.1.0"
dependencies = [
"anyhow",
"misskey",
"termion",
"tokio 1.24.1",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -1417,16 +1416,6 @@ dependencies = [
"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]]
name = "signal-hook-registry"
version = "1.4.0"

View File

@ -2,11 +2,7 @@ use std::io::{Stdout, Write};
use termion::{clear, cursor, raw::RawTerminal};
use super::{
frame::{self, FrameDef},
theme::Theme,
Event, Page,
};
use super::{Environment, Event, Page};
type Result<T> = std::result::Result<T, anyhow::Error>;
@ -17,15 +13,15 @@ pub enum 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);
write!(
screen,
"{theme}{clear}{start}Event: {}",
event,
theme = theme.display_string(),
"{theme}{clear}{start}{}",
env.frame.write(&format!("Event: {}", event), 1, 1),
theme = env.theme.display_string(),
clear = clear::All,
start = cursor::Goto(1, 1)
start = env.frame.goto(1, 1)
)?;
screen.flush()?;
Ok(())
@ -35,24 +31,24 @@ impl Body {
impl Page for Body {
fn receive_event(
&mut self,
theme: Theme,
env: Environment,
screen: &mut RawTerminal<Stdout>,
event: Event,
) -> Result<()> {
match self {
Body::Signin(b) => b.receive_event(theme, screen, event)?,
Body::Echo => Body::echo(theme, screen, event)?,
Body::Signin(b) => b.receive_event(env, screen, event)?,
Body::Echo => Body::echo(env, screen, event)?,
};
Ok(())
}
fn init(&self, theme: Theme, screen: &mut RawTerminal<Stdout>) -> Result<()> {
fn init(&self, env: Environment, screen: &mut RawTerminal<Stdout>) -> Result<()> {
write!(
screen,
"{theme}{cursor}{clear}{fr}",
fr = frame::draw_frame(theme, FrameDef::ByPercent(90, 90)),
theme = theme.display_string(),
cursor = cursor::Goto(1, 1),
"{theme}{clear}{cursor}{fr}",
theme = env.theme.display_string(),
fr = env.frame.frame_str(),
cursor = env.frame.goto(0, 0),
clear = clear::All
)?;
screen.flush()?;
@ -89,20 +85,19 @@ impl SigninPage {
impl Page for SigninPage {
fn receive_event(
&mut self,
theme: Theme,
env: Environment,
screen: &mut RawTerminal<Stdout>,
event: Event,
) -> Result<()> {
Ok(())
}
fn init(&self, theme: Theme, screen: &mut RawTerminal<Stdout>) -> Result<()> {
let fr = frame::draw_frame(theme, FrameDef::ByPercent(40, 40));
fn init(&self, env: Environment, screen: &mut RawTerminal<Stdout>) -> Result<()> {
write!(
screen,
"{theme}{clear}{hide_cursor}{frame}",
frame = fr,
theme = theme.display_string(),
frame = env.frame.frame_str(),
theme = env.theme.display_string(),
clear = clear::All,
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;
pub enum FrameDef {
const FRAME_CHAR: char = ' ';
pub enum FrameSize {
ByAbsolute(u16, u16),
ByPercent(u16, u16),
}
struct Frame {
start: (u16, u16),
end: (u16, u16),
#[derive(Debug, Clone, Copy)]
pub struct Frame {
start: (u16, u16), // (w, h)
end: (u16, u16), // (w, h)
}
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 {
start: (term.0 - pos.0, term.1 - pos.1),
end: pos,
start: (1.max(term_w - pos_w), 1.max(term_h - pos_h)),
end: (pos_w, pos_h),
}
}
fn frame_str(&self, frame_char: char) -> String {
pub fn frame_str(&self) -> String {
let (w_len, h_len) = (
(self.end.0 - self.start.0) 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 make_line =
|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 {
frame.push_str(&format!(
"{left}{char}{right}{char}",
left = cursor::Goto(self.start.0, y),
right = cursor::Goto(self.end.0, y),
char = frame_char,
left = self.goto(self.start.0, y),
right = self.goto(self.end.0, y),
char = FRAME_CHAR,
));
}
frame.push_str(&make_line(self.end.1));
frame.push_str(&self.goto(1, 1));
frame
}
}
#[cfg(test)]
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 {
impl FrameSize {
fn abs_size(&self) -> Frame {
let (term_height, term_width) =
termion::terminal_size().expect("could not get terminal size");
let pos = match self {
FrameDef::ByAbsolute(h, w) => {
let (mut h, mut w) = (*h, *w);
if h > term_height {
h = term_height;
}
if w > term_width {
w = term_width;
}
(h, w)
}
FrameDef::ByPercent(h, w) => {
FrameSize::ByAbsolute(h, w) => ((*h).min(term_height), (*w).min(term_width)),
FrameSize::ByPercent(h, w) => {
// term_height = 100%
// x = h%
// x = term_height * h / 100
let (h, w) = (
if *h > 100 { 100 } else { *h },
if *w > 100 { 100 } else { *w },
);
// (h * 100 / term_height, w * 100 / term_width)
(term_height * h / 100, term_width * w / 100)
(
term_height * (*h).min(100) / 100,
term_width * (*w).min(100) / 100,
)
}
};
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 self::body::Body;
use self::frame::Frame;
use self::theme::Theme;
pub mod body;
@ -46,15 +47,40 @@ impl From<Key> for Event {
}
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(
&mut self,
theme: Theme,
env: Environment,
screen: &mut RawTerminal<Stdout>,
event: Event,
) -> 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 {
pub fn new(theme: Theme) -> Result<Self> {
let screen = Mutex::new(io::stdout().into_raw_mode()?);
@ -72,21 +98,24 @@ impl Screen {
}
pub async fn start(mut self) -> Result<()> {
let env = Environment::initial_must(self.theme);
{
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 {
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;
if let Some(last) = self.last_interrupt {
if 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)?;
self.body.receive_event(env, &mut scr, ev)?;
}
// Cleanup

View File

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

View File

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