Refactored framing and fixed the issues w/ composition

This commit is contained in:
emilis 2023-01-15 15:13:04 +00:00
parent e6323d4009
commit 99808f3d90
4 changed files with 218 additions and 129 deletions

View File

@ -3,7 +3,10 @@ use std::io::{Stdout, Write};
use termion::{clear, cursor, raw::RawTerminal}; use termion::{clear, cursor, raw::RawTerminal};
use super::{ use super::{
frame::Frame, theme::ColorSet, Environment, Event, Page, compose::{Component, Components},
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>;
@ -25,8 +28,10 @@ impl Body {
screen, screen,
"{theme}{content}", "{theme}{content}",
theme = env.theme.primary(), theme = env.theme.primary(),
content = content = env
env.frame.writeln(&format!("Event: {}", event), 0, 0), .frame
.writeln(&format!("Event: {}", event), 0, 0)
.concat_for(&env.frame),
)?; )?;
screen.flush()?; screen.flush()?;
Ok(()) Ok(())
@ -42,7 +47,7 @@ impl Body {
"{hide}{clear}{fr}{cursor}", "{hide}{clear}{fr}{cursor}",
clear = clear::All, clear = clear::All,
hide = cursor::Hide, hide = cursor::Hide,
fr = env.frame.frame_str(env.theme.primary()), fr = env.frame.frame_str(env.theme.colors.primary),
cursor = env.frame.goto(0, 0), cursor = env.frame.goto(0, 0),
)?; )?;
screen.flush()?; screen.flush()?;
@ -98,7 +103,11 @@ impl Default for SigninCursorLocation {
impl SigninPage { impl SigninPage {
#[inline] #[inline]
fn draw(&self, highlight: ColorSet, normal: ColorSet) -> String { fn draw(
&self,
highlight: ColorSet,
normal: ColorSet,
) -> Components {
const HEADER_Y: u16 = 1; const HEADER_Y: u16 = 1;
const HOSTNAME_Y: u16 = 3; const HOSTNAME_Y: u16 = 3;
const OK_Y: u16 = 5; const OK_Y: u16 = 5;
@ -106,21 +115,27 @@ impl SigninPage {
let fr_width = frame.size().0; let fr_width = frame.size().0;
let total_width = fr_width / 3; let total_width = fr_width / 3;
format!( todo!()
"{header}{hostname}{retheme}{ok}", // frame
header = frame.write_centered("login kk", HEADER_Y), // .write_centered("login kk", HEADER_Y)
hostname = frame.write_centered( // .into_iter()
&self.field_str( // .chain(
highlight, // frame
total_width, // .write_centered(
"hostname", // &self.field_str(
"", // highlight,
), // total_width,
HOSTNAME_Y, // "hostname",
), // "",
retheme = normal.to_string(), // ),
ok = frame.write_centered("[ok]", OK_Y), // HOSTNAME_Y,
) // )
// .into_iter(),
// )
// .chain(vec![Component::Theme(normal)].into_iter())
// .chain(frame.write_centered("[ok]", OK_Y).into_iter())
// .collect::<Vec<Component>>()
// .into()
} }
#[inline] #[inline]
@ -176,16 +191,17 @@ impl Page for SigninPage {
"{theme}{clear}{frame}{subframe}{content}{hide_cursor}", "{theme}{clear}{frame}{subframe}{content}{hide_cursor}",
theme = env.theme.frame(), theme = env.theme.frame(),
clear = clear::All, clear = clear::All,
frame = env.frame.frame_str(env.theme.primary()), frame = env.frame.frame_str(env.theme.colors.primary),
subframe = self subframe = self
.frame .frame
.unwrap() .unwrap()
.frame_str(env.theme.colors.subwin.to_string()), .frame_str(env.theme.colors.subwin),
hide_cursor = cursor::Hide, hide_cursor = cursor::Hide,
content = self.draw( content = "",
env.theme.colors.highlight, // content = self.draw(
env.theme.colors.subwin // env.theme.colors.highlight,
), // env.theme.colors.subwin
// ),
)?; )?;
screen.flush()?; screen.flush()?;

View File

@ -1,4 +1,8 @@
use std::ops::Deref; use std::{
iter::Map,
ops::{Deref, Range},
vec::IntoIter,
};
use super::{frame::Frame, theme::ColorSet}; use super::{frame::Frame, theme::ColorSet};
@ -7,22 +11,31 @@ pub enum Component {
String(String), String(String),
Goto(u16, u16), Goto(u16, u16),
Theme(ColorSet), Theme(ColorSet),
Padding(u16),
// Pre-formatted
Internal(String),
Repeated(char, u16),
Clear, Clear,
} }
impl Component { impl Component {
pub fn prepare_for(&self, frame: &Frame) -> String { pub fn prepare_for(self, frame: &Frame) -> String {
match self { match self {
Component::String(s) => s.to_owned(), Component::String(s) => s,
Component::Goto(x, y) => frame.goto(*x, *y), Component::Goto(x, y) => frame.goto(x, y),
Component::Theme(c) => c.to_string(), Component::Theme(c) => c.to_string(),
Component::Clear => termion::clear::All.to_string(), Component::Clear => termion::clear::All.to_string(),
Component::Padding(len) => " ".repeat(len as usize),
Component::Internal(i) => i,
Component::Repeated(ch, len) => {
ch.to_string().repeat(len as usize)
}
} }
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Components(Vec<Component>); pub struct Components(pub Vec<Component>);
impl Deref for Components { impl Deref for Components {
type Target = Vec<Component>; type Target = Vec<Component>;
@ -46,13 +59,16 @@ impl From<Component> for Components {
impl Components { impl Components {
pub fn str_len(&self) -> u16 { pub fn str_len(&self) -> u16 {
let mut len_cnt: u16 = 0; (&self.0)
for elem in &self.0 { .into_iter()
if let Component::String(s) = elem { .map(|comp| {
len_cnt += s.len() as u16; if let Component::String(s) = comp {
s.len() as u16
} else {
0
} }
} })
len_cnt .sum()
} }
pub fn concat_for(self, frame: &Frame) -> String { pub fn concat_for(self, frame: &Frame) -> String {
@ -61,4 +77,8 @@ impl Components {
.map(|component| component.prepare_for(frame)) .map(|component| component.prepare_for(frame))
.collect::<String>() .collect::<String>()
} }
pub fn push(&mut self, comp: Component) {
self.0.push(comp)
}
} }

View File

@ -1,9 +1,16 @@
use termion::{clear, cursor, raw::RawTerminal}; use termion::{clear, cursor, raw::RawTerminal};
const ESTIMATED_FRAME_BIT_SIZE: usize = 11; const ESTIMATED_FRAME_BIT_SIZE: usize = 11;
use std::io::{Stdout, Write}; use std::{
io::{Stdout, Write},
slice::Iter,
vec::IntoIter,
};
use super::theme::{ColorSet, Theme}; use super::{
compose::{Component, Components},
theme::{ColorSet, Theme},
};
const FRAME_CHAR_HORIZONTAL: char = ' '; const FRAME_CHAR_HORIZONTAL: char = ' ';
const FRAME_CHAR_VERTICAL: char = ' '; const FRAME_CHAR_VERTICAL: char = ' ';
@ -40,7 +47,7 @@ impl Frame {
#[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( cursor::Goto(
self.border.max( self.border.max(
(self.border + self.start.0 + x) (self.border + self.start.0 + x)
.min(self.end.0 - self.border), .min(self.end.0 - self.border),
@ -49,76 +56,91 @@ impl Frame {
(self.border + self.start.1 + y) (self.border + self.start.1 + y)
.min(self.end.1 - self.border), .min(self.end.1 - self.border),
), ),
);
cg.into()
}
#[inline(always)]
fn goto_internal(&self, x: u16, y: u16) -> String {
cursor::Goto(
(self.start.0 + x).min(self.end.0),
(self.start.1 + y).min(self.end.1),
) )
.into() .into()
} }
#[inline(always)] #[inline(always)]
fn write_centered_clear(&self, s: &str) -> String { fn goto_internal(&self, x: u16, y: u16) -> Component {
let width = self.size().0 as usize; Component::Internal(
let limit = width - self.border as usize * 2; cursor::Goto(
let s = if s.len() > limit { &s[..limit] } else { s }; (self.start.0 + x).min(self.end.0),
let len = s.len(); (self.start.1 + y).min(self.end.1),
let base_size = ((width - len) / 2) - self.border as usize;
format!(
"{left}{line}{right}",
left = " ".repeat(base_size + ((width - len) % 2)),
line = s,
right = " ".repeat(base_size)
) )
.into(),
)
.into()
} }
#[inline(always)] #[inline(always)]
fn write_frame_line(&self, y: u16, length: u16) -> String { fn write_centered_clear(&self, s: &str) -> Components {
self.goto_internal(0, y) let width = self.size().0;
+ &FRAME_CHAR_HORIZONTAL let limit = width - self.border * 2;
.to_string() let s = if s.len() > limit as usize {
.repeat(length as usize) &s[..limit as usize]
} else {
s
};
let len = s.len() as u16;
let base_size = ((width - len) / 2) - self.border;
vec![
Component::Padding(base_size + ((width - len) % 2)),
Component::String(s.into()),
Component::Padding(base_size),
]
.into()
} }
#[inline(always)] #[inline(always)]
fn write_clear_to_end(&self, s: &str) -> String { fn write_frame_line(&self, y: u16, length: u16) -> Components {
vec![
self.goto_internal(0, y),
Component::Repeated(FRAME_CHAR_HORIZONTAL, length),
]
.into()
}
#[inline(always)]
fn write_clear_to_end(&self, s: &str) -> Components {
let limit = (self.size().0 - self.border * 2) as usize; let limit = (self.size().0 - self.border * 2) as usize;
let s = if s.len() > limit { &s[..limit] } else { s }; let s = if s.len() > limit { &s[..limit] } else { s };
let clear_length = self.size().0 as usize vec![
- s.len() Component::String(s.into()),
- (self.border * 2) as usize; Component::Padding(
format!("{}{}", s, " ".repeat(clear_length)) self.size().0 - s.len() as u16 - (self.border * 2),
),
]
.into()
} }
pub fn writeln(&self, s: &str, x: u16, y: u16) -> String { pub fn writeln(&self, s: &str, x: u16, y: u16) -> Components {
let words = s.split('\n').collect::<Vec<&str>>(); let mut index: u16 = 0;
let mut out_words = Vec::with_capacity(words.len()); s.split('\n')
for i in 0..words.len() { .flat_map(|wrd| {
out_words.push(format!( index += 1;
"{ret}{str}", vec![
str = self.write_clear_to_end(words[i]), vec![Component::Goto(x, y + index - 1)],
ret = self.goto(x, y + i as u16), self.write_clear_to_end(wrd).0,
)); ]
} })
out_words.concat() .flatten()
.collect::<Vec<Component>>()
.into()
} }
pub fn write_centered(&self, s: &str, y: u16) -> String { pub fn write_centered(&self, s: &str, y: u16) -> Components {
let words = s.split('\n').collect::<Vec<&str>>(); let mut index: u16 = 0;
let mut out_words = Vec::with_capacity(words.len()); s.split('\n')
for i in 0..words.len() { .flat_map(|wrd| {
out_words.push(format!( index += 1;
"{ret}{str}", vec![
ret = self.goto(0, y + i as u16), vec![Component::Goto(0, y + index - 1)],
str = self.write_centered_clear(words[i]), self.write_centered_clear(wrd).0,
)); ]
} })
out_words.concat() .flatten()
.collect::<Vec<Component>>()
.into()
} }
#[inline(always)] #[inline(always)]
@ -203,42 +225,73 @@ impl Frame {
frame frame
} }
pub fn frame_str(&self, body_theme: String) -> String { pub fn frame_str(&self, body_theme: ColorSet) -> String {
if self.border == 0 { self.compose(body_theme).concat_for(self)
return body_theme + &clear::All.to_string();
} }
self.goto_internal(0, 0);
let (w_len, h_len) = self.size();
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 frame_theme = self.theme.to_string();
let body_clear =
body_theme.clone() + &" ".repeat(w_len as usize);
frame.push(self.theme.to_string());
for y in 0..self.border { // compose generates a collection of components for drawing
frame.push(self.write_frame_line(y, w_len)); // the frame within a screen
pub fn compose(&self, body_theme: ColorSet) -> Components {
let body_theme = Component::Internal(body_theme.to_string());
if self.border == 0 {
// FIXME: this will fuck up subframes
return vec![body_theme, Component::Clear].into();
} }
for y in self.border..h_len - self.border { let (w_len, h_len) = self.size();
frame.push(format!( let frame_theme = Component::Internal(self.theme.to_string());
"{left}{body_clear}{frame_theme}{left}{char}{right}{char}",
body_clear = &body_clear, let body_clear = Component::Internal(
frame_theme = &frame_theme, Into::<Components>::into(vec![
left = self.goto_internal(0, y), body_theme.clone(),
right = self.goto_internal(w_len - self.border, y), Component::Padding(w_len),
char = FRAME_CHAR_VERTICAL ])
.to_string() .concat_for(self),
.repeat(self.border as usize), );
));
} let border =
for y in h_len - self.border..h_len { Component::Repeated(FRAME_CHAR_VERTICAL, self.border);
frame.push(self.write_frame_line(y, w_len)); // This seems cursed but, I assure you, it's fine
} vec![vec![frame_theme.clone()]]
frame.push(self.goto(0, 0)); .into_iter()
frame.push(body_theme); // Top horizontal borders
frame.concat() .chain(
(0..self.border)
.into_iter()
.map(|y: u16| self.write_frame_line(y, w_len).0),
)
// Frame body + vertical frame borders
.chain(
(self.border..h_len - self.border).into_iter().map(
|y: u16| {
let left = self.goto_internal(0, y);
let right = self
.goto_internal(w_len - self.border, y);
vec![
left.clone(),
body_clear.clone(),
frame_theme.clone(),
left,
border.clone(),
right,
border.clone(),
]
},
),
)
// Bottom horizontal border
.chain(
(h_len - self.border..h_len)
.into_iter()
.map(|y: u16| self.write_frame_line(y, w_len).0),
)
// Theme reset + move to beginning of inside frame
.chain(
vec![vec![Component::Goto(0, 0), body_theme]]
.into_iter(),
)
.flatten()
.collect::<Vec<Component>>()
.into()
} }
} }

View File

@ -89,7 +89,7 @@ pub struct Environment {
impl Into<String> for Environment { impl Into<String> for Environment {
fn into(self) -> String { fn into(self) -> String {
self.frame.frame_str(self.theme.primary()) self.frame.frame_str(self.theme.colors.primary)
} }
} }
@ -107,7 +107,7 @@ impl Environment {
#[inline(always)] #[inline(always)]
pub fn primary_frame(&self) -> String { pub fn primary_frame(&self) -> String {
self.frame.frame_str(self.theme.primary()) self.frame.frame_str(self.theme.colors.primary)
} }
} }