226 lines
7.8 KiB
Rust
226 lines
7.8 KiB
Rust
use std;
|
|
use std::cell::RefCell;
|
|
use std::io;
|
|
use std::io::{BufWriter, Write};
|
|
|
|
use super::smallstring::SmallString;
|
|
use ropey::RopeSlice;
|
|
use termion;
|
|
use termion::color;
|
|
use termion::raw::{IntoRawMode, RawTerminal};
|
|
use termion::screen::AlternateScreen;
|
|
use unicode_segmentation::UnicodeSegmentation;
|
|
use unicode_width::UnicodeWidthStr;
|
|
use utils::{grapheme_width, RopeGraphemes};
|
|
|
|
pub(crate) struct Screen {
|
|
out: RefCell<AlternateScreen<RawTerminal<BufWriter<io::Stdout>>>>,
|
|
buf: RefCell<Vec<Option<(Style, SmallString)>>>,
|
|
w: usize,
|
|
h: usize,
|
|
}
|
|
|
|
impl Screen {
|
|
pub(crate) fn new() -> Self {
|
|
let (w, h) = termion::terminal_size().unwrap();
|
|
let buf = std::iter::repeat(Some((Style(Color::Black, Color::Black), " ".into())))
|
|
.take(w as usize * h as usize)
|
|
.collect();
|
|
Screen {
|
|
out: RefCell::new(AlternateScreen::from(
|
|
BufWriter::with_capacity(1 << 14, io::stdout())
|
|
.into_raw_mode()
|
|
.unwrap(),
|
|
)),
|
|
buf: RefCell::new(buf),
|
|
w: w as usize,
|
|
h: h as usize,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn clear(&self, col: Color) {
|
|
for cell in self.buf.borrow_mut().iter_mut() {
|
|
match *cell {
|
|
Some((ref mut style, ref mut text)) => {
|
|
*style = Style(col, col);
|
|
text.clear();
|
|
text.push_str(" ");
|
|
}
|
|
_ => {
|
|
*cell = Some((Style(col, col), " ".into()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn resize(&mut self, w: usize, h: usize) {
|
|
self.w = w;
|
|
self.h = h;
|
|
self.buf
|
|
.borrow_mut()
|
|
.resize(w * h, Some((Style(Color::Black, Color::Black), " ".into())));
|
|
}
|
|
|
|
pub(crate) fn present(&self) {
|
|
let mut out = self.out.borrow_mut();
|
|
let buf = self.buf.borrow();
|
|
|
|
let mut last_style = Style(Color::Black, Color::Black);
|
|
write!(out, "{}", last_style).unwrap();
|
|
|
|
// Write everything to the tmp_string first.
|
|
for y in 0..self.h {
|
|
let mut x = 0;
|
|
write!(out, "{}", termion::cursor::Goto(1, y as u16 + 1)).unwrap();
|
|
while x < self.w {
|
|
if let Some((style, ref text)) = buf[y * self.w + x] {
|
|
if style != last_style {
|
|
write!(out, "{}", style).unwrap();
|
|
last_style = style;
|
|
}
|
|
write!(out, "{}", text).unwrap();
|
|
x += 1;
|
|
} else {
|
|
x += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure everything is written out
|
|
out.flush().unwrap();
|
|
}
|
|
|
|
pub(crate) fn draw(&self, x: usize, y: usize, text: &str, style: Style) {
|
|
if y < self.h {
|
|
let mut buf = self.buf.borrow_mut();
|
|
let mut x = x;
|
|
for g in UnicodeSegmentation::graphemes(text, true) {
|
|
let width = UnicodeWidthStr::width(g);
|
|
if width > 0 {
|
|
if x < self.w {
|
|
buf[y * self.w + x] = Some((style, g.into()));
|
|
}
|
|
x += 1;
|
|
for _ in 0..(width - 1) {
|
|
if x < self.w {
|
|
buf[y * self.w + x] = None;
|
|
}
|
|
x += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn draw_rope_slice(&self, x: usize, y: usize, text: &RopeSlice, style: Style) {
|
|
if y < self.h {
|
|
let mut buf = self.buf.borrow_mut();
|
|
let mut x = x;
|
|
for g in RopeGraphemes::new(&text) {
|
|
let width = grapheme_width(&g);
|
|
if width > 0 {
|
|
if x < self.w {
|
|
buf[y * self.w + x] = Some((style, SmallString::from_rope_slice(&g)));
|
|
}
|
|
x += 1;
|
|
for _ in 0..(width - 1) {
|
|
if x < self.w {
|
|
buf[y * self.w + x] = None;
|
|
}
|
|
x += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn hide_cursor(&self) {
|
|
write!(self.out.borrow_mut(), "{}", termion::cursor::Hide).unwrap();
|
|
}
|
|
|
|
pub(crate) fn show_cursor(&self) {
|
|
write!(self.out.borrow_mut(), "{}", termion::cursor::Show).unwrap();
|
|
}
|
|
}
|
|
|
|
impl Drop for Screen {
|
|
fn drop(&mut self) {
|
|
write!(
|
|
self.out.borrow_mut(),
|
|
"{}{}{}",
|
|
color::Fg(color::Reset),
|
|
color::Bg(color::Reset),
|
|
termion::clear::All,
|
|
).unwrap();
|
|
self.show_cursor();
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
pub(crate) enum Color {
|
|
Black,
|
|
Blue,
|
|
Cyan,
|
|
Green,
|
|
LightBlack,
|
|
LightBlue,
|
|
LightCyan,
|
|
LightGreen,
|
|
LightMagenta,
|
|
LightRed,
|
|
LightWhite,
|
|
LightYellow,
|
|
Magenta,
|
|
Red,
|
|
Rgb(u8, u8, u8),
|
|
White,
|
|
Yellow,
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
pub(crate) struct Style(pub Color, pub Color); // Fg, Bg
|
|
|
|
impl std::fmt::Display for Style {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
match self.0 {
|
|
Color::Black => write!(f, "{}", color::Fg(color::Black)),
|
|
Color::Blue => write!(f, "{}", color::Fg(color::Blue)),
|
|
Color::Cyan => write!(f, "{}", color::Fg(color::Cyan)),
|
|
Color::Green => write!(f, "{}", color::Fg(color::Green)),
|
|
Color::LightBlack => write!(f, "{}", color::Fg(color::LightBlack)),
|
|
Color::LightBlue => write!(f, "{}", color::Fg(color::LightBlue)),
|
|
Color::LightCyan => write!(f, "{}", color::Fg(color::LightCyan)),
|
|
Color::LightGreen => write!(f, "{}", color::Fg(color::LightGreen)),
|
|
Color::LightMagenta => write!(f, "{}", color::Fg(color::LightMagenta)),
|
|
Color::LightRed => write!(f, "{}", color::Fg(color::LightRed)),
|
|
Color::LightWhite => write!(f, "{}", color::Fg(color::LightWhite)),
|
|
Color::LightYellow => write!(f, "{}", color::Fg(color::LightYellow)),
|
|
Color::Magenta => write!(f, "{}", color::Fg(color::Magenta)),
|
|
Color::Red => write!(f, "{}", color::Fg(color::Red)),
|
|
Color::Rgb(r, g, b) => write!(f, "{}", color::Fg(color::Rgb(r, g, b))),
|
|
Color::White => write!(f, "{}", color::Fg(color::White)),
|
|
Color::Yellow => write!(f, "{}", color::Fg(color::Yellow)),
|
|
}?;
|
|
|
|
match self.1 {
|
|
Color::Black => write!(f, "{}", color::Bg(color::Black)),
|
|
Color::Blue => write!(f, "{}", color::Bg(color::Blue)),
|
|
Color::Cyan => write!(f, "{}", color::Bg(color::Cyan)),
|
|
Color::Green => write!(f, "{}", color::Bg(color::Green)),
|
|
Color::LightBlack => write!(f, "{}", color::Bg(color::LightBlack)),
|
|
Color::LightBlue => write!(f, "{}", color::Bg(color::LightBlue)),
|
|
Color::LightCyan => write!(f, "{}", color::Bg(color::LightCyan)),
|
|
Color::LightGreen => write!(f, "{}", color::Bg(color::LightGreen)),
|
|
Color::LightMagenta => write!(f, "{}", color::Bg(color::LightMagenta)),
|
|
Color::LightRed => write!(f, "{}", color::Bg(color::LightRed)),
|
|
Color::LightWhite => write!(f, "{}", color::Bg(color::LightWhite)),
|
|
Color::LightYellow => write!(f, "{}", color::Bg(color::LightYellow)),
|
|
Color::Magenta => write!(f, "{}", color::Bg(color::Magenta)),
|
|
Color::Red => write!(f, "{}", color::Bg(color::Red)),
|
|
Color::Rgb(r, g, b) => write!(f, "{}", color::Bg(color::Rgb(r, g, b))),
|
|
Color::White => write!(f, "{}", color::Bg(color::White)),
|
|
Color::Yellow => write!(f, "{}", color::Bg(color::Yellow)),
|
|
}
|
|
}
|
|
}
|