145 lines
4.4 KiB
Rust
145 lines
4.4 KiB
Rust
use termion::{clear, cursor};
|
|
|
|
const ESTIMATED_FRAME_BIT_SIZE: usize = 11;
|
|
use std::io::Write;
|
|
|
|
use crate::display::debug::debug;
|
|
|
|
use super::theme::Theme;
|
|
|
|
const FRAME_CHAR_HORIZONTAL: char = ' ';
|
|
const FRAME_CHAR_VERTICAL: char = ' ';
|
|
|
|
pub enum FrameSize {
|
|
ByAbsolute(u16, u16),
|
|
ByPercent(u16, u16),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Frame {
|
|
// Both are (w, h)
|
|
start: (u16, u16),
|
|
end: (u16, u16),
|
|
width: u16,
|
|
theme: Theme,
|
|
}
|
|
|
|
impl Frame {
|
|
#[inline(always)]
|
|
pub fn goto(&self, x: u16, y: u16) -> String {
|
|
let cg = cursor::Goto(
|
|
(self.width + self.start.0 + x).min(self.end.0 - self.width),
|
|
(self.width + self.start.1 + y).min(self.end.1 - self.width),
|
|
);
|
|
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()
|
|
}
|
|
|
|
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 = words[i],
|
|
ret = self.goto(x, y + i as u16),
|
|
));
|
|
}
|
|
out_words.concat()
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn size(&self) -> (u16, u16) {
|
|
(self.end.0 - self.start.0, self.end.1 - self.start.1)
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn from_terminal_size(theme: Theme, width: u16) -> Result<Self, anyhow::Error> {
|
|
let term = termion::terminal_size()?;
|
|
// Swap term dimensions to use x/y
|
|
let term = (term.1, term.0);
|
|
Ok(Self::from_bottom_right(term, term, width, theme))
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn from_bottom_right(
|
|
(pos_h, pos_w): (u16, u16),
|
|
(term_h, term_w): (u16, u16),
|
|
width: u16,
|
|
theme: Theme,
|
|
) -> Self {
|
|
Self {
|
|
start: (1.max(term_w - pos_w), 1.max(term_h - pos_h)),
|
|
end: (pos_w, pos_h),
|
|
width,
|
|
theme,
|
|
}
|
|
}
|
|
|
|
pub fn frame_str(&self, body_theme: String) -> String {
|
|
if self.width == 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.frame();
|
|
let body_clear = body_theme.clone() + &clear::CurrentLine.to_string();
|
|
frame.push(frame_theme.clone());
|
|
|
|
let make_line =
|
|
|y: u16| format!("{left}{}", width_str, left = cursor::Goto(self.start.0, y));
|
|
for y in 0..self.width {
|
|
frame.push(make_line(self.start.1 + y));
|
|
}
|
|
for y in self.width + self.start.1..self.end.1 - self.width {
|
|
frame.push(format!(
|
|
"{left}{body_clear}{frame_theme}{char}{right}{char}",
|
|
body_clear = &body_clear,
|
|
frame_theme = &frame_theme,
|
|
left = cursor::Goto(self.start.0, y),
|
|
right = cursor::Goto(self.start.0 + self.end.0 - self.width, y),
|
|
char = FRAME_CHAR_VERTICAL.to_string().repeat(self.width as usize),
|
|
));
|
|
}
|
|
for y in self.end.1 - self.width - 1..self.end.1 {
|
|
frame.push(make_line(self.start.1 + y));
|
|
}
|
|
frame.push(make_line(self.end.1));
|
|
frame.push(self.goto(1, 1));
|
|
frame.push(body_theme);
|
|
frame.concat()
|
|
}
|
|
}
|
|
|
|
impl FrameSize {
|
|
#[inline(always)]
|
|
fn abs_size(&self, theme: Theme, width: u16) -> Frame {
|
|
let (term_height, term_width) =
|
|
termion::terminal_size().expect("could not get terminal size");
|
|
let pos = match self {
|
|
FrameSize::ByAbsolute(h, w) => ((*h).min(term_height), (*w).min(term_width)),
|
|
FrameSize::ByPercent(h, w) => {
|
|
// term_height = 100%
|
|
// x = h%
|
|
//
|
|
// term_height * h / 100 = x
|
|
(
|
|
term_height * (*h).min(100) / 100,
|
|
term_width * (*w).min(100) / 100,
|
|
)
|
|
}
|
|
};
|
|
Frame::from_bottom_right(pos, (term_height, term_width), width, theme)
|
|
}
|
|
}
|