finished body/frame refactoring
This commit is contained in:
parent
99808f3d90
commit
ac9e337e73
|
@ -3,7 +3,7 @@ use std::io::{Stdout, Write};
|
|||
use termion::{clear, cursor, raw::RawTerminal};
|
||||
|
||||
use super::{
|
||||
compose::{Component, Components},
|
||||
compose::{Component, Components, HorizontalAlignment, Text},
|
||||
frame::Frame,
|
||||
theme::ColorSet,
|
||||
Environment, Event, Page,
|
||||
|
@ -23,16 +23,14 @@ impl Body {
|
|||
screen: &mut RawTerminal<Stdout>,
|
||||
event: Event,
|
||||
) -> Result<()> {
|
||||
let event = format!("{}", event);
|
||||
write!(
|
||||
screen,
|
||||
"{theme}{content}",
|
||||
theme = env.theme.primary(),
|
||||
content = env
|
||||
.frame
|
||||
.writeln(&format!("Event: {}", event), 0, 0)
|
||||
.concat_for(&env.frame),
|
||||
)?;
|
||||
let echo_comps =
|
||||
Components::break_into(format!("Event: {}", event))
|
||||
.nextline_after_text(
|
||||
0,
|
||||
HorizontalAlignment::Left,
|
||||
&env.frame,
|
||||
);
|
||||
write!(screen, "{}", env.frame.make(echo_comps))?;
|
||||
screen.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -112,43 +110,76 @@ impl SigninPage {
|
|||
const HOSTNAME_Y: u16 = 3;
|
||||
const OK_Y: u16 = 5;
|
||||
let frame = self.frame.unwrap();
|
||||
let fr_width = frame.size().0;
|
||||
let total_width = fr_width / 3;
|
||||
let (w, _) = frame.inner_size();
|
||||
let exp_w = w / 3;
|
||||
|
||||
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()
|
||||
vec![
|
||||
frame
|
||||
.prefix_centered_goto(
|
||||
vec![Component::String(Text::Normal(
|
||||
"login kk".into(),
|
||||
))]
|
||||
.into(),
|
||||
HEADER_Y,
|
||||
)
|
||||
.0,
|
||||
frame
|
||||
.prefix_centered_goto(
|
||||
self.field(
|
||||
highlight,
|
||||
normal,
|
||||
exp_w,
|
||||
"hostname",
|
||||
&self.hostname,
|
||||
),
|
||||
HOSTNAME_Y,
|
||||
)
|
||||
.0,
|
||||
frame
|
||||
.prefix_centered_goto(
|
||||
self.field(
|
||||
highlight,
|
||||
normal,
|
||||
exp_w,
|
||||
"username",
|
||||
&self.username,
|
||||
),
|
||||
OK_Y,
|
||||
)
|
||||
.0,
|
||||
]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<Component>>()
|
||||
.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn field_str(
|
||||
fn field<C>(
|
||||
&self,
|
||||
color: ColorSet,
|
||||
width: u16,
|
||||
field: &str,
|
||||
field_content: &str,
|
||||
) -> String {
|
||||
let unpadded = format!("{field}: {field_content}",);
|
||||
let len = field.len() + field_content.len() + 2;
|
||||
unpadded + &"_".repeat(width as usize - len)
|
||||
normal: ColorSet,
|
||||
exp_width: u16,
|
||||
field: C,
|
||||
field_content: C,
|
||||
) -> Components
|
||||
where
|
||||
C: Into<String>,
|
||||
{
|
||||
let (field, field_content) =
|
||||
(field.into(), field_content.into());
|
||||
let budget_for_content = exp_width - field.len() as u16 - 3;
|
||||
vec![
|
||||
Component::String((field + &": ").into()),
|
||||
Component::Theme(color),
|
||||
Component::String(Text::LimitedOrPadded(
|
||||
field_content,
|
||||
'_',
|
||||
budget_for_content,
|
||||
)),
|
||||
Component::Theme(normal),
|
||||
]
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,11 +228,10 @@ impl Page for SigninPage {
|
|||
.unwrap()
|
||||
.frame_str(env.theme.colors.subwin),
|
||||
hide_cursor = cursor::Hide,
|
||||
content = "",
|
||||
// content = self.draw(
|
||||
// env.theme.colors.highlight,
|
||||
// env.theme.colors.subwin
|
||||
// ),
|
||||
content = self.frame.unwrap().make(self.draw(
|
||||
env.theme.colors.highlight,
|
||||
env.theme.colors.subwin
|
||||
))
|
||||
)?;
|
||||
screen.flush()?;
|
||||
|
||||
|
|
|
@ -1,14 +1,121 @@
|
|||
use std::{
|
||||
iter::Map,
|
||||
ops::{Deref, Range},
|
||||
vec::IntoIter,
|
||||
};
|
||||
|
||||
use super::{frame::Frame, theme::ColorSet};
|
||||
use std::ops::Deref;
|
||||
|
||||
const ELLIPSES: char = '…';
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Text {
|
||||
// Unrestricted
|
||||
Normal(String),
|
||||
// To length
|
||||
Limited(String, u16),
|
||||
// Limit or pad to length
|
||||
LimitedOrPadded(String, char, u16),
|
||||
PaddedBothSides(u16, String, u16),
|
||||
}
|
||||
|
||||
impl From<String> for Text {
|
||||
fn from(value: String) -> Self {
|
||||
Self::Normal(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Text {
|
||||
#[inline]
|
||||
pub fn limit(self, limit: u16) -> Text {
|
||||
match self {
|
||||
Self::Normal(s) => Self::Limited(s, limit),
|
||||
Self::Limited(s, _) => Self::Limited(s, limit),
|
||||
Self::LimitedOrPadded(s, c, _) => {
|
||||
Self::LimitedOrPadded(s, c, limit)
|
||||
}
|
||||
Self::PaddedBothSides(left, s, right) => todo!(),
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn len(&self) -> u16 {
|
||||
match self {
|
||||
Text::Normal(s) => s.len() as u16,
|
||||
Text::Limited(s, max) => (*max).min(s.len() as u16),
|
||||
Text::LimitedOrPadded(_, _, len) => *len,
|
||||
Text::PaddedBothSides(left, s, right) => {
|
||||
left + s.len() as u16 + right
|
||||
}
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn conditional_truncate(s: String, len: u16) -> String {
|
||||
let mut s = s;
|
||||
if s.len() > len as usize {
|
||||
s.truncate(len as usize - 1);
|
||||
s.push(ELLIPSES);
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn make(self, frame: &Frame) -> String {
|
||||
match self {
|
||||
Text::Normal(s) => s,
|
||||
Text::Limited(s, len) => {
|
||||
Self::conditional_truncate(s, len)
|
||||
}
|
||||
Text::LimitedOrPadded(s, pad, len) => {
|
||||
let s = Self::conditional_truncate(s, len);
|
||||
let sln = s.len();
|
||||
s + &pad.to_string().repeat(len as usize - sln)
|
||||
}
|
||||
Text::PaddedBothSides(left, s, right) => {
|
||||
format!(
|
||||
"{}{}{}",
|
||||
" ".repeat(left as usize),
|
||||
s,
|
||||
" ".repeat(right as usize)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum HorizontalAlignment {
|
||||
Left,
|
||||
Right,
|
||||
Center,
|
||||
}
|
||||
|
||||
impl HorizontalAlignment {
|
||||
pub fn align(
|
||||
&self,
|
||||
s: Text,
|
||||
frame: &Frame,
|
||||
y: u16,
|
||||
) -> Components {
|
||||
let (width, _) = frame.size();
|
||||
match self {
|
||||
Self::Left => {
|
||||
vec![Component::Goto(0, y), Component::String(s)]
|
||||
}
|
||||
Self::Right => {
|
||||
vec![
|
||||
Component::Goto(s.len() as u16 - width, y),
|
||||
Component::String(s),
|
||||
]
|
||||
}
|
||||
Self::Center => {
|
||||
vec![
|
||||
Component::Goto((width - s.len()) / 2, y),
|
||||
Component::String(s),
|
||||
]
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Component {
|
||||
String(String),
|
||||
String(Text),
|
||||
Goto(u16, u16),
|
||||
Theme(ColorSet),
|
||||
Padding(u16),
|
||||
|
@ -19,9 +126,9 @@ pub enum Component {
|
|||
}
|
||||
|
||||
impl Component {
|
||||
pub fn prepare_for(self, frame: &Frame) -> String {
|
||||
pub fn make(self, frame: &Frame) -> String {
|
||||
match self {
|
||||
Component::String(s) => s,
|
||||
Component::String(s) => s.make(frame),
|
||||
Component::Goto(x, y) => frame.goto(x, y),
|
||||
Component::Theme(c) => c.to_string(),
|
||||
Component::Clear => termion::clear::All.to_string(),
|
||||
|
@ -58,27 +165,48 @@ impl From<Component> for Components {
|
|||
}
|
||||
|
||||
impl Components {
|
||||
// Consumes itself, returns a set of new components
|
||||
// with gotos for the following line, starting at y (not y+1)
|
||||
// if there's a goto, the goto is used as the last y.
|
||||
// also applies alignment, relative to the given frame
|
||||
pub fn nextline_after_text(
|
||||
self,
|
||||
y: u16,
|
||||
align: HorizontalAlignment,
|
||||
frame: &Frame,
|
||||
) -> Self {
|
||||
let mut last_y = y;
|
||||
let xxx = self.0.into_iter().map(|comp| match comp {
|
||||
Component::String(s) => {
|
||||
last_y += 1;
|
||||
align.align(s, frame, last_y).0
|
||||
}
|
||||
Component::Goto(_, y) => {
|
||||
last_y = y;
|
||||
vec![comp]
|
||||
}
|
||||
_ => vec![comp],
|
||||
});
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn break_into(s: String) -> Components {
|
||||
s.split('\n')
|
||||
.map(|pt| Component::String(Text::Normal(pt.into())))
|
||||
.collect::<Vec<Component>>()
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn str_len(&self) -> u16 {
|
||||
(&self.0)
|
||||
.into_iter()
|
||||
.map(|comp| {
|
||||
if let Component::String(s) = comp {
|
||||
s.len() as u16
|
||||
s.len()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
pub fn concat_for(self, frame: &Frame) -> String {
|
||||
self.0
|
||||
.into_iter()
|
||||
.map(|component| component.prepare_for(frame))
|
||||
.collect::<String>()
|
||||
}
|
||||
|
||||
pub fn push(&mut self, comp: Component) {
|
||||
self.0.push(comp)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
use termion::{clear, cursor, raw::RawTerminal};
|
||||
use termion::cursor;
|
||||
|
||||
const ESTIMATED_FRAME_BIT_SIZE: usize = 11;
|
||||
use std::{
|
||||
io::{Stdout, Write},
|
||||
slice::Iter,
|
||||
vec::IntoIter,
|
||||
};
|
||||
|
||||
use crate::display::compose::Text;
|
||||
|
||||
use super::{
|
||||
compose::{Component, Components},
|
||||
theme::{ColorSet, Theme},
|
||||
theme::ColorSet,
|
||||
};
|
||||
|
||||
const FRAME_CHAR_HORIZONTAL: char = ' ';
|
||||
|
@ -60,6 +57,20 @@ impl Frame {
|
|||
.into()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn prefix_centered_goto(
|
||||
&self,
|
||||
comp: Components,
|
||||
line_num: u16,
|
||||
) -> Components {
|
||||
let x = (self.inner_size().0 - comp.str_len()) / 2;
|
||||
vec![Component::Goto(x, line_num)]
|
||||
.into_iter()
|
||||
.chain(comp.0.into_iter())
|
||||
.collect::<Vec<Component>>()
|
||||
.into()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn goto_internal(&self, x: u16, y: u16) -> Component {
|
||||
Component::Internal(
|
||||
|
@ -73,7 +84,7 @@ impl Frame {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn write_centered_clear(&self, s: &str) -> Components {
|
||||
fn write_centered_clear(&self, s: &str) -> Component {
|
||||
let width = self.size().0;
|
||||
let limit = width - self.border * 2;
|
||||
let s = if s.len() > limit as usize {
|
||||
|
@ -83,12 +94,11 @@ impl Frame {
|
|||
};
|
||||
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()
|
||||
Component::String(super::compose::Text::PaddedBothSides(
|
||||
base_size + ((width - len) % 2),
|
||||
s.into(),
|
||||
base_size,
|
||||
))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -100,58 +110,39 @@ impl Frame {
|
|||
.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 };
|
||||
#[inline]
|
||||
pub fn to_end(&self, s: &str, x: u16, y: u16) -> Components {
|
||||
// FIXME: handle the fucking newlines grrrr i hate this
|
||||
let width = self.inner_size().0;
|
||||
vec![
|
||||
Component::String(s.into()),
|
||||
Component::Padding(
|
||||
self.size().0 - s.len() as u16 - (self.border * 2),
|
||||
),
|
||||
Component::Goto(x, y),
|
||||
Component::String(Text::LimitedOrPadded(
|
||||
s.into(),
|
||||
' ',
|
||||
width - x,
|
||||
)),
|
||||
]
|
||||
.into()
|
||||
}
|
||||
|
||||
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) -> 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()
|
||||
pub fn make(&self, comp: Components) -> String {
|
||||
comp.0.into_iter().map(|c| c.make(self)).collect()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn size(&self) -> (u16, u16) {
|
||||
eprintln!(
|
||||
"end.0: {}, start.0: {}, end.1: {}, start.1: {}",
|
||||
self.end.0, self.start.0, self.end.1, self.start.1
|
||||
);
|
||||
(self.end.0 - self.start.0, self.end.1 - self.start.1)
|
||||
}
|
||||
|
||||
// Size within the borders
|
||||
#[inline(always)]
|
||||
pub fn inner_size(&self) -> (u16, u16) {
|
||||
(
|
||||
self.end.0 - self.start.0 - self.border * 2,
|
||||
self.end.1 - self.start.1 - self.border * 2,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_terminal_size(
|
||||
theme: ColorSet,
|
||||
|
@ -225,8 +216,9 @@ impl Frame {
|
|||
frame
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn frame_str(&self, body_theme: ColorSet) -> String {
|
||||
self.compose(body_theme).concat_for(self)
|
||||
self.make(self.compose(body_theme))
|
||||
}
|
||||
|
||||
// compose generates a collection of components for drawing
|
||||
|
@ -240,13 +232,10 @@ impl Frame {
|
|||
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 body_clear =
|
||||
Component::Internal(self.make(Into::<Components>::into(
|
||||
vec![body_theme.clone(), Component::Padding(w_len)],
|
||||
)));
|
||||
|
||||
let border =
|
||||
Component::Repeated(FRAME_CHAR_VERTICAL, self.border);
|
||||
|
|
Loading…
Reference in New Issue