120 lines
3.6 KiB
Rust
120 lines
3.6 KiB
Rust
use termion::cursor;
|
|
|
|
const ESTIMATED_FRAME_BIT_SIZE: usize = 11;
|
|
|
|
const FRAME_CHAR: char = ' ';
|
|
|
|
pub enum FrameSize {
|
|
ByAbsolute(u16, u16),
|
|
ByPercent(u16, u16),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Frame {
|
|
start: (u16, u16), // (w, h)
|
|
end: (u16, u16), // (w, h)
|
|
}
|
|
|
|
impl Frame {
|
|
#[inline]
|
|
pub fn goto(&self, x: u16, y: u16) -> String {
|
|
let cg = cursor::Goto(
|
|
(self.start.0 + x).min(self.end.0 - 1),
|
|
(self.start.1 + y).min(self.end.1 - 1),
|
|
);
|
|
// eprintln!(
|
|
// "goto x: {x} y: {y} -> x: {nx} y: {ny}",
|
|
// x = x,
|
|
// y = y,
|
|
// nx = cg.0,
|
|
// ny = cg.1
|
|
// );
|
|
// eprintln!("self start: {:#?} self end: {:#?}", self.start, self.end);
|
|
cg.into()
|
|
}
|
|
|
|
pub fn write(&self, s: &str, x: u16, y: u16) -> String {
|
|
let words = s.split('\n');
|
|
let (width, height) = self.size();
|
|
let mut lines_down = 0;
|
|
let mut curr_len = width;
|
|
let mut write_parts = Vec::with_capacity(s.len() / 5);
|
|
write_parts.push(self.goto(x, y));
|
|
for w in words {
|
|
// if curr_len + (w.len() as u16) > width {
|
|
// if y + 1 >= height {
|
|
// panic!("out of vertical space")
|
|
// }
|
|
// lines_down += 1;
|
|
// curr_len = 1;
|
|
// }
|
|
write_parts.push(self.goto(1, lines_down + y));
|
|
lines_down += 1;
|
|
write_parts.push(w.to_string());
|
|
}
|
|
write_parts.concat()
|
|
}
|
|
|
|
// (x, y)
|
|
#[inline]
|
|
pub fn size(&self) -> (u16, u16) {
|
|
(self.end.0 - self.start.0, self.end.1 - self.start.1)
|
|
}
|
|
|
|
pub fn from_terminal_size() -> Result<Self, anyhow::Error> {
|
|
let term = termion::terminal_size()?;
|
|
|
|
Ok(Self::from_bottom_right(term, term))
|
|
}
|
|
|
|
fn from_bottom_right((pos_h, pos_w): (u16, u16), (term_h, term_w): (u16, u16)) -> Self {
|
|
Self {
|
|
start: (1.max(term_w - pos_w), 1.max(term_h - pos_h)),
|
|
end: (pos_w, pos_h),
|
|
}
|
|
}
|
|
|
|
pub fn frame_str(&self) -> String {
|
|
let (w_len, h_len) = (
|
|
(self.end.0 - self.start.0) as usize,
|
|
(self.end.1 - self.start.1 - 1) as usize,
|
|
);
|
|
let width_str = FRAME_CHAR.to_string().repeat(w_len + 1);
|
|
let mut frame = String::with_capacity((h_len * 2) + (w_len * 2) * ESTIMATED_FRAME_BIT_SIZE);
|
|
let make_line =
|
|
|y: u16| format!("{left}{}", width_str, left = cursor::Goto(self.start.0, y));
|
|
frame.push_str(&make_line(self.start.1));
|
|
for y in self.start.1 + 1..self.end.1 {
|
|
frame.push_str(&format!(
|
|
"{left}{char}{right}{char}",
|
|
left = self.goto(self.start.0, y),
|
|
right = self.goto(self.end.0, y),
|
|
char = FRAME_CHAR,
|
|
));
|
|
}
|
|
frame.push_str(&make_line(self.end.1));
|
|
frame.push_str(&self.goto(1, 1));
|
|
frame
|
|
}
|
|
}
|
|
|
|
impl FrameSize {
|
|
fn abs_size(&self) -> 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%
|
|
// x = term_height * h / 100
|
|
(
|
|
term_height * (*h).min(100) / 100,
|
|
term_width * (*w).min(100) / 100,
|
|
)
|
|
}
|
|
};
|
|
Frame::from_bottom_right(pos, (term_height, term_width))
|
|
}
|
|
}
|