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 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>;
@ -25,8 +28,10 @@ impl Body {
screen,
"{theme}{content}",
theme = env.theme.primary(),
content =
env.frame.writeln(&format!("Event: {}", event), 0, 0),
content = env
.frame
.writeln(&format!("Event: {}", event), 0, 0)
.concat_for(&env.frame),
)?;
screen.flush()?;
Ok(())
@ -42,7 +47,7 @@ impl Body {
"{hide}{clear}{fr}{cursor}",
clear = clear::All,
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),
)?;
screen.flush()?;
@ -98,7 +103,11 @@ impl Default for SigninCursorLocation {
impl SigninPage {
#[inline]
fn draw(&self, highlight: ColorSet, normal: ColorSet) -> String {
fn draw(
&self,
highlight: ColorSet,
normal: ColorSet,
) -> Components {
const HEADER_Y: u16 = 1;
const HOSTNAME_Y: u16 = 3;
const OK_Y: u16 = 5;
@ -106,21 +115,27 @@ impl SigninPage {
let fr_width = frame.size().0;
let total_width = fr_width / 3;
format!(
"{header}{hostname}{retheme}{ok}",
header = frame.write_centered("login kk", HEADER_Y),
hostname = frame.write_centered(
&self.field_str(
highlight,
total_width,
"hostname",
"",
),
HOSTNAME_Y,
),
retheme = normal.to_string(),
ok = frame.write_centered("[ok]", OK_Y),
)
todo!()
// frame
// .write_centered("login kk", HEADER_Y)
// .into_iter()
// .chain(
// frame
// .write_centered(
// &self.field_str(
// highlight,
// total_width,
// "hostname",
// "",
// ),
// 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]
@ -176,16 +191,17 @@ impl Page for SigninPage {
"{theme}{clear}{frame}{subframe}{content}{hide_cursor}",
theme = env.theme.frame(),
clear = clear::All,
frame = env.frame.frame_str(env.theme.primary()),
frame = env.frame.frame_str(env.theme.colors.primary),
subframe = self
.frame
.unwrap()
.frame_str(env.theme.colors.subwin.to_string()),
.frame_str(env.theme.colors.subwin),
hide_cursor = cursor::Hide,
content = self.draw(
env.theme.colors.highlight,
env.theme.colors.subwin
),
content = "",
// content = self.draw(
// env.theme.colors.highlight,
// env.theme.colors.subwin
// ),
)?;
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};
@ -7,22 +11,31 @@ pub enum Component {
String(String),
Goto(u16, u16),
Theme(ColorSet),
Padding(u16),
// Pre-formatted
Internal(String),
Repeated(char, u16),
Clear,
}
impl Component {
pub fn prepare_for(&self, frame: &Frame) -> String {
pub fn prepare_for(self, frame: &Frame) -> String {
match self {
Component::String(s) => s.to_owned(),
Component::Goto(x, y) => frame.goto(*x, *y),
Component::String(s) => s,
Component::Goto(x, y) => frame.goto(x, y),
Component::Theme(c) => c.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)]
pub struct Components(Vec<Component>);
pub struct Components(pub Vec<Component>);
impl Deref for Components {
type Target = Vec<Component>;
@ -46,13 +59,16 @@ impl From<Component> for Components {
impl Components {
pub fn str_len(&self) -> u16 {
let mut len_cnt: u16 = 0;
for elem in &self.0 {
if let Component::String(s) = elem {
len_cnt += s.len() as u16;
(&self.0)
.into_iter()
.map(|comp| {
if let Component::String(s) = comp {
s.len() as u16
} else {
0
}
}
len_cnt
})
.sum()
}
pub fn concat_for(self, frame: &Frame) -> String {
@ -61,4 +77,8 @@ impl Components {
.map(|component| component.prepare_for(frame))
.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};
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_VERTICAL: char = ' ';
@ -40,7 +47,7 @@ impl Frame {
#[inline(always)]
pub fn goto(&self, x: u16, y: u16) -> String {
let cg = cursor::Goto(
cursor::Goto(
self.border.max(
(self.border + self.start.0 + x)
.min(self.end.0 - self.border),
@ -49,76 +56,91 @@ impl Frame {
(self.border + self.start.1 + y)
.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()
}
#[inline(always)]
fn write_centered_clear(&self, s: &str) -> String {
let width = self.size().0 as usize;
let limit = width - self.border as usize * 2;
let s = if s.len() > limit { &s[..limit] } else { s };
let len = s.len();
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)
fn goto_internal(&self, x: u16, y: u16) -> Component {
Component::Internal(
cursor::Goto(
(self.start.0 + x).min(self.end.0),
(self.start.1 + y).min(self.end.1),
)
.into(),
)
.into()
}
#[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)
fn write_centered_clear(&self, s: &str) -> Components {
let width = self.size().0;
let limit = width - self.border * 2;
let s = if s.len() > limit 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)]
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 s = if s.len() > limit { &s[..limit] } else { s };
let clear_length = self.size().0 as usize
- s.len()
- (self.border * 2) as usize;
format!("{}{}", s, " ".repeat(clear_length))
vec![
Component::String(s.into()),
Component::Padding(
self.size().0 - s.len() as u16 - (self.border * 2),
),
]
.into()
}
pub fn writeln(&self, s: &str, x: u16, y: u16) -> String {
let words = s.split('\n').collect::<Vec<&str>>();
let mut out_words = Vec::with_capacity(words.len());
for i in 0..words.len() {
out_words.push(format!(
"{ret}{str}",
str = self.write_clear_to_end(words[i]),
ret = self.goto(x, y + i as u16),
));
}
out_words.concat()
pub fn writeln(&self, s: &str, x: u16, y: u16) -> Components {
let mut index: u16 = 0;
s.split('\n')
.flat_map(|wrd| {
index += 1;
vec![
vec![Component::Goto(x, y + index - 1)],
self.write_clear_to_end(wrd).0,
]
})
.flatten()
.collect::<Vec<Component>>()
.into()
}
pub fn write_centered(&self, s: &str, y: u16) -> String {
let words = s.split('\n').collect::<Vec<&str>>();
let mut out_words = Vec::with_capacity(words.len());
for i in 0..words.len() {
out_words.push(format!(
"{ret}{str}",
ret = self.goto(0, y + i as u16),
str = self.write_centered_clear(words[i]),
));
}
out_words.concat()
pub fn write_centered(&self, s: &str, y: u16) -> Components {
let mut index: u16 = 0;
s.split('\n')
.flat_map(|wrd| {
index += 1;
vec![
vec![Component::Goto(0, y + index - 1)],
self.write_centered_clear(wrd).0,
]
})
.flatten()
.collect::<Vec<Component>>()
.into()
}
#[inline(always)]
@ -203,42 +225,73 @@ impl Frame {
frame
}
pub fn frame_str(&self, body_theme: String) -> String {
if self.border == 0 {
return body_theme + &clear::All.to_string();
pub fn frame_str(&self, body_theme: ColorSet) -> String {
self.compose(body_theme).concat_for(self)
}
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 {
frame.push(self.write_frame_line(y, w_len));
// compose generates a collection of components for drawing
// 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 {
frame.push(format!(
"{left}{body_clear}{frame_theme}{left}{char}{right}{char}",
body_clear = &body_clear,
frame_theme = &frame_theme,
left = self.goto_internal(0, y),
right = self.goto_internal(w_len - self.border, y),
char = FRAME_CHAR_VERTICAL
.to_string()
.repeat(self.border as usize),
));
}
for y in h_len - self.border..h_len {
frame.push(self.write_frame_line(y, w_len));
}
frame.push(self.goto(0, 0));
frame.push(body_theme);
frame.concat()
let (w_len, h_len) = self.size();
let frame_theme = Component::Internal(self.theme.to_string());
let body_clear = Component::Internal(
Into::<Components>::into(vec![
body_theme.clone(),
Component::Padding(w_len),
])
.concat_for(self),
);
let border =
Component::Repeated(FRAME_CHAR_VERTICAL, self.border);
// This seems cursed but, I assure you, it's fine
vec![vec![frame_theme.clone()]]
.into_iter()
// Top horizontal borders
.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 {
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)]
pub fn primary_frame(&self) -> String {
self.frame.frame_str(self.theme.primary())
self.frame.frame_str(self.theme.colors.primary)
}
}