|
|
|
@ -1,17 +1,10 @@ |
|
|
|
|
use termion::cursor; |
|
|
|
|
|
|
|
|
|
const ESTIMATED_FRAME_BIT_SIZE: usize = 11; |
|
|
|
|
|
|
|
|
|
use crate::display::compose::Text; |
|
|
|
|
|
|
|
|
|
use super::{ |
|
|
|
|
compose::{Component, Components}, |
|
|
|
|
theme::ColorSet, |
|
|
|
|
theme::{self, ColorSet}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const FRAME_CHAR_HORIZONTAL: char = ' '; |
|
|
|
|
const FRAME_CHAR_VERTICAL: char = ' '; |
|
|
|
|
|
|
|
|
|
pub enum FrameSize { |
|
|
|
|
ByAbsolute(u16, u16), |
|
|
|
|
ByPercent(u16, u16), |
|
|
|
@ -22,36 +15,30 @@ pub struct Frame { |
|
|
|
|
// Both are (w, h)
|
|
|
|
|
start: (u16, u16), |
|
|
|
|
end: (u16, u16), |
|
|
|
|
border: u16, |
|
|
|
|
theme: ColorSet, |
|
|
|
|
theme: theme::Frame, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Frame { |
|
|
|
|
#[inline(always)] |
|
|
|
|
pub fn sub( |
|
|
|
|
&self, |
|
|
|
|
theme: ColorSet, |
|
|
|
|
size: FrameSize, |
|
|
|
|
border: u16, |
|
|
|
|
) -> Frame { |
|
|
|
|
pub fn sub(&self, theme: theme::Frame, size: FrameSize) -> Frame { |
|
|
|
|
let (p_width, p_height) = self.size(); |
|
|
|
|
let sub_size = size.abs_size(( |
|
|
|
|
p_width - self.border * 2, |
|
|
|
|
p_height - self.border * 2, |
|
|
|
|
p_width - self.theme.width * 2, |
|
|
|
|
p_height - self.theme.width * 2, |
|
|
|
|
)); |
|
|
|
|
self.child_centered(sub_size, border, theme) |
|
|
|
|
self.child_centered(sub_size, theme) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline(always)] |
|
|
|
|
pub fn goto(&self, x: u16, y: u16) -> String { |
|
|
|
|
cursor::Goto( |
|
|
|
|
self.border.max( |
|
|
|
|
(self.border + self.start.0 + x) |
|
|
|
|
.min(self.end.0 - self.border), |
|
|
|
|
self.theme.width.max( |
|
|
|
|
(self.theme.width + self.start.0 + x) |
|
|
|
|
.min(self.end.0 - self.theme.width), |
|
|
|
|
), |
|
|
|
|
self.border.max( |
|
|
|
|
(self.border + self.start.1 + y) |
|
|
|
|
.min(self.end.1 - self.border), |
|
|
|
|
self.theme.width.max( |
|
|
|
|
(self.theme.width + self.start.1 + y) |
|
|
|
|
.min(self.end.1 - self.theme.width), |
|
|
|
|
), |
|
|
|
|
) |
|
|
|
|
.into() |
|
|
|
@ -86,14 +73,14 @@ impl Frame { |
|
|
|
|
#[inline(always)] |
|
|
|
|
fn write_centered_clear(&self, s: &str) -> Component { |
|
|
|
|
let width = self.size().0; |
|
|
|
|
let limit = width - self.border * 2; |
|
|
|
|
let limit = width - self.theme.width * 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; |
|
|
|
|
let base_size = ((width - len) / 2) - self.theme.width; |
|
|
|
|
Component::String(super::compose::Text::PaddedBothSides( |
|
|
|
|
base_size + ((width - len) % 2), |
|
|
|
|
s.into(), |
|
|
|
@ -102,12 +89,14 @@ impl Frame { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline(always)] |
|
|
|
|
fn write_frame_line(&self, y: u16, length: u16) -> Components { |
|
|
|
|
vec![ |
|
|
|
|
self.goto_internal(0, y), |
|
|
|
|
Component::Repeated(FRAME_CHAR_HORIZONTAL, length), |
|
|
|
|
] |
|
|
|
|
.into() |
|
|
|
|
fn write_frame_line( |
|
|
|
|
&self, |
|
|
|
|
y: u16, |
|
|
|
|
length: u16, |
|
|
|
|
c: char, |
|
|
|
|
) -> Components { |
|
|
|
|
vec![self.goto_internal(0, y), Component::Repeated(c, length)] |
|
|
|
|
.into() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn make(&self, comp: Components) -> String { |
|
|
|
@ -123,31 +112,31 @@ impl Frame { |
|
|
|
|
#[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, |
|
|
|
|
self.end.0 - self.start.0 - self.theme.width * 2, |
|
|
|
|
self.end.1 - self.start.1 - self.theme.width * 2, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline(always)] |
|
|
|
|
pub fn from_terminal_size( |
|
|
|
|
theme: ColorSet, |
|
|
|
|
width: u16, |
|
|
|
|
theme: theme::Frame, |
|
|
|
|
) -> Result<Self, anyhow::Error> { |
|
|
|
|
let term = termion::terminal_size()?; |
|
|
|
|
Ok(Self::from_bottom_right(term, term, width, theme)) |
|
|
|
|
Ok(Self::from_bottom_right(term, term, theme)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline(always)] |
|
|
|
|
fn child_centered( |
|
|
|
|
&self, |
|
|
|
|
(pos_w, pos_h): (u16, u16), |
|
|
|
|
border: u16, |
|
|
|
|
theme: ColorSet, |
|
|
|
|
theme: theme::Frame, |
|
|
|
|
) -> Frame { |
|
|
|
|
let parent_offset = self.border / 2; |
|
|
|
|
let parent_offset = self.theme.width / 2; |
|
|
|
|
let (parent_w, parent_h) = self.size(); |
|
|
|
|
let (parent_w, parent_h) = |
|
|
|
|
(parent_w - self.border, parent_h - self.border); |
|
|
|
|
let (parent_w, parent_h) = ( |
|
|
|
|
parent_w - self.theme.width, |
|
|
|
|
parent_h - self.theme.width, |
|
|
|
|
); |
|
|
|
|
let start = ( |
|
|
|
|
1.max( |
|
|
|
|
(parent_w - pos_w) / 2 |
|
|
|
@ -163,7 +152,6 @@ impl Frame { |
|
|
|
|
let frame = Self { |
|
|
|
|
end: (start.0 + pos_w, start.1 + pos_h), |
|
|
|
|
start, |
|
|
|
|
border, |
|
|
|
|
theme, |
|
|
|
|
}; |
|
|
|
|
frame |
|
|
|
@ -173,21 +161,19 @@ impl Frame { |
|
|
|
|
fn from_bottom_right( |
|
|
|
|
(pos_w, pos_h): (u16, u16), |
|
|
|
|
(term_w, term_h): (u16, u16), |
|
|
|
|
border: u16, |
|
|
|
|
theme: ColorSet, |
|
|
|
|
theme: theme::Frame, |
|
|
|
|
) -> Self { |
|
|
|
|
let start = (1.max(term_w - pos_w), 1.max(term_h - pos_h)); |
|
|
|
|
let frame = Self { |
|
|
|
|
end: (start.0 + pos_w, start.1 + pos_h), |
|
|
|
|
start, |
|
|
|
|
border, |
|
|
|
|
theme, |
|
|
|
|
}; |
|
|
|
|
if frame.start.0 > frame.end.0 || frame.start.1 > frame.end.1 |
|
|
|
|
{ |
|
|
|
|
eprintln!( |
|
|
|
|
"pos_w {}, pos_h {}, term_w {}, term_h {}, border {}", |
|
|
|
|
pos_w, pos_h, term_w, term_h, border |
|
|
|
|
pos_w, pos_h, term_w, term_h, theme.width, |
|
|
|
|
); |
|
|
|
|
panic!( |
|
|
|
|
"start.0: {} end.0: {}, start.1: {}, end.1: {}", |
|
|
|
@ -211,49 +197,66 @@ impl Frame { |
|
|
|
|
pub fn compose(&self, body_theme: ColorSet) -> Components { |
|
|
|
|
let body_theme = Component::Internal(body_theme.to_string()); |
|
|
|
|
let (w_len, h_len) = self.size(); |
|
|
|
|
let frame_theme = Component::Internal(self.theme.to_string()); |
|
|
|
|
let frame_theme = |
|
|
|
|
Component::Internal(self.theme.color.to_string()); |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
let border_left = Component::Repeated( |
|
|
|
|
self.theme.frame_chars.left, |
|
|
|
|
self.theme.width, |
|
|
|
|
); |
|
|
|
|
let border_right = Component::Repeated( |
|
|
|
|
self.theme.frame_chars.right, |
|
|
|
|
self.theme.width, |
|
|
|
|
); |
|
|
|
|
// 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), |
|
|
|
|
) |
|
|
|
|
.chain((0..self.theme.width).into_iter().map(|y: u16| { |
|
|
|
|
self.write_frame_line( |
|
|
|
|
y, |
|
|
|
|
w_len, |
|
|
|
|
self.theme.frame_chars.top, |
|
|
|
|
) |
|
|
|
|
.0 |
|
|
|
|
})) |
|
|
|
|
// Frame body + vertical frame borders
|
|
|
|
|
.chain( |
|
|
|
|
(self.border..h_len - self.border).into_iter().map( |
|
|
|
|
|y: u16| { |
|
|
|
|
(self.theme.width..h_len - self.theme.width) |
|
|
|
|
.into_iter() |
|
|
|
|
.map(|y: u16| { |
|
|
|
|
let left = self.goto_internal(0, y); |
|
|
|
|
let right = self |
|
|
|
|
.goto_internal(w_len - self.border, y); |
|
|
|
|
let right = self.goto_internal( |
|
|
|
|
w_len - self.theme.width, |
|
|
|
|
y, |
|
|
|
|
); |
|
|
|
|
vec![ |
|
|
|
|
left.clone(), |
|
|
|
|
body_clear.clone(), |
|
|
|
|
frame_theme.clone(), |
|
|
|
|
left, |
|
|
|
|
border.clone(), |
|
|
|
|
border_left.clone(), |
|
|
|
|
right, |
|
|
|
|
border.clone(), |
|
|
|
|
border_right.clone(), |
|
|
|
|
] |
|
|
|
|
}, |
|
|
|
|
), |
|
|
|
|
}), |
|
|
|
|
) |
|
|
|
|
// Bottom horizontal border
|
|
|
|
|
.chain( |
|
|
|
|
(h_len - self.border..h_len) |
|
|
|
|
.into_iter() |
|
|
|
|
.map(|y: u16| self.write_frame_line(y, w_len).0), |
|
|
|
|
) |
|
|
|
|
.chain((h_len - self.theme.width..h_len).into_iter().map( |
|
|
|
|
|y: u16| { |
|
|
|
|
self.write_frame_line( |
|
|
|
|
y, |
|
|
|
|
w_len, |
|
|
|
|
self.theme.frame_chars.bottom, |
|
|
|
|
) |
|
|
|
|
.0 |
|
|
|
|
}, |
|
|
|
|
)) |
|
|
|
|
// Theme reset + move to beginning of inside frame
|
|
|
|
|
.chain( |
|
|
|
|
vec![vec![Component::Goto(0, 0), body_theme]] |
|
|
|
|