Refactored framing and fixed the issues w/ composition
This commit is contained in:
parent
e6323d4009
commit
99808f3d90
|
@ -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()?;
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
len_cnt
|
0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!(
|
.into(),
|
||||||
"{left}{line}{right}",
|
|
||||||
left = " ".repeat(base_size + ((width - len) % 2)),
|
|
||||||
line = s,
|
|
||||||
right = " ".repeat(base_size)
|
|
||||||
)
|
)
|
||||||
|
.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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue