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 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()?;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
len_cnt
|
||||
(&self.0)
|
||||
.into_iter()
|
||||
.map(|comp| {
|
||||
if let Component::String(s) = comp {
|
||||
s.len() as u16
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
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());
|
||||
pub fn frame_str(&self, body_theme: ColorSet) -> String {
|
||||
self.compose(body_theme).concat_for(self)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue