diff --git a/src/term_ui/mod.rs b/src/term_ui/mod.rs index 7a988a7..20ea143 100644 --- a/src/term_ui/mod.rs +++ b/src/term_ui/mod.rs @@ -17,7 +17,7 @@ use utils::digit_count; pub mod formatter; mod screen; -use self::screen::Screen; +use self::screen::{Color, Screen, Style}; /// Generalized ui loop. macro_rules! ui_loop { @@ -203,8 +203,7 @@ impl<'a> TermUI<'a> { } fn go_to_line_ui_loop(&mut self) { - let foreground = color::Black; - let background = color::Cyan; + let style = Style(Color::Black, Color::Cyan); let mut cancel = false; let prefix = "Jump to line: "; @@ -220,15 +219,14 @@ impl<'a> TermUI<'a> { self.screen.clear(); self.draw_editor(&self.editor, (0, 0), (self.height - 1, self.width - 1)); for i in 0..self.width { - self.screen.draw(i, 0, " ", foreground, background); + self.screen.draw(i, 0, " ", style); } - self.screen.draw(1, 0, prefix, foreground, background); + self.screen.draw(1, 0, prefix, style); self.screen.draw( prefix.len() + 1, 0, &line[..], - foreground, - background, + style, ); }, @@ -281,19 +279,18 @@ impl<'a> TermUI<'a> { c1: (usize, usize), c2: (usize, usize), ) { - let fg = color::Black; - let bg = color::Cyan; + let style = Style(Color::Black, Color::Cyan); // Fill in top row with info line color for i in c1.1..(c2.1 + 1) { - self.screen.draw(i, c1.0, " ", fg, bg); + self.screen.draw(i, c1.0, " ", style); } // Filename and dirty marker let filename = editor.file_path.display(); let dirty_char = if editor.dirty { "*" } else { "" }; let name = format!("{}{}", filename, dirty_char); - self.screen.draw(c1.1 + 1, c1.0, &name[..], fg, bg); + self.screen.draw(c1.1 + 1, c1.0, &name[..], style); // Percentage position in document // TODO: use view instead of cursor for calculation if there is more @@ -306,7 +303,7 @@ impl<'a> TermUI<'a> { }; let pstring = format!("{}%", percentage); self.screen - .draw(c2.1 - pstring.len(), c1.0, &pstring[..], fg, bg); + .draw(c2.1 - pstring.len(), c1.0, &pstring[..], style); // Text encoding info and tab style let nl = match editor.line_ending_type { @@ -325,7 +322,7 @@ impl<'a> TermUI<'a> { "UTF8:{} {}:{}", nl, soft_tabs_str, editor.soft_tab_width as usize ); - self.screen.draw(c2.1 - 30, c1.0, &info_line[..], fg, bg); + self.screen.draw(c2.1 - 30, c1.0, &info_line[..], style); // Draw main text editing area self.draw_editor_text(editor, (c1.0 + 1, c1.1), c2); @@ -364,7 +361,8 @@ impl<'a> TermUI<'a> { // Fill in the gutter with the appropriate background for y in c1.0..(c2.0 + 1) { for x in c1.1..(c1.1 + gutter_width - 1) { - self.screen.draw(x, y, " ", color::White, color::Blue); + self.screen + .draw(x, y, " ", Style(Color::White, Color::Blue)); } } @@ -379,8 +377,7 @@ impl<'a> TermUI<'a> { lnx, lny, &format!("{}", line_num)[..], - color::White, - color::Blue, + Style(Color::White, Color::Blue), ); } } @@ -430,8 +427,7 @@ impl<'a> TermUI<'a> { px as usize, py as usize, " ", - color::Black, - color::White, + Style(Color::Black, Color::White), ); } } else if g == "\t" { @@ -442,8 +438,7 @@ impl<'a> TermUI<'a> { tpx as usize, py as usize, " ", - color::White, - color::Black, + Style(Color::White, Color::Black), ); } } @@ -453,8 +448,7 @@ impl<'a> TermUI<'a> { px as usize, py as usize, " ", - color::Black, - color::White, + Style(Color::Black, Color::White), ); } } else { @@ -463,16 +457,14 @@ impl<'a> TermUI<'a> { px as usize, py as usize, g, - color::Black, - color::White, + Style(Color::Black, Color::White), ); } else { self.screen.draw( px as usize, py as usize, g, - color::White, - color::Black, + Style(Color::White, Color::Black), ); } } @@ -524,8 +516,12 @@ impl<'a> TermUI<'a> { if (px >= c1.1 as isize) && (py >= c1.0 as isize) && (px <= c2.1 as isize) && (py <= c2.0 as isize) { - self.screen - .draw(px as usize, py as usize, " ", color::Black, color::White); + self.screen.draw( + px as usize, + py as usize, + " ", + Style(Color::Black, Color::White), + ); } } } diff --git a/src/term_ui/screen.rs b/src/term_ui/screen.rs index c4ccf62..b522bf6 100644 --- a/src/term_ui/screen.rs +++ b/src/term_ui/screen.rs @@ -12,7 +12,7 @@ use termion::raw::{IntoRawMode, RawTerminal}; pub(crate) struct Screen { out: RefCell>>, - buf: RefCell>>, + buf: RefCell>>, w: usize, h: usize, } @@ -20,11 +20,8 @@ pub(crate) struct Screen { impl Screen { pub(crate) fn new() -> Self { let (w, h) = termion::terminal_size().unwrap(); - let buf = std::iter::repeat(Some(format!( - "{}{} ", - color::Fg(color::Black), - color::Bg(color::Black) - ))).take(w as usize * h as usize) + let buf = std::iter::repeat(Some((Style(Color::Black, Color::Black), " ".to_string()))) + .take(w as usize * h as usize) .collect(); Screen { out: RefCell::new(AlternateScreen::from(io::stdout().into_raw_mode().unwrap())), @@ -37,20 +34,13 @@ impl Screen { pub(crate) fn clear(&self) { for cell in self.buf.borrow_mut().iter_mut() { match *cell { - Some(ref mut text) => { + Some((ref mut style, ref mut text)) => { + *style = Style(Color::Black, Color::Black); text.clear(); - text.push_str(&format!( - "{}{} ", - color::Fg(color::Black), - color::Bg(color::Black) - )); + text.push_str(" "); } _ => { - *cell = Some(format!( - "{}{} ", - color::Fg(color::Black), - color::Bg(color::Black) - )); + *cell = Some((Style(Color::Black, Color::Black), " ".to_string())); } } } @@ -61,45 +51,40 @@ impl Screen { self.h = h; self.buf.borrow_mut().resize( w * h, - Some(format!( - "{}{} ", - color::Fg(color::Black), - color::Bg(color::Black) - )), + Some((Style(Color::Black, Color::Black), " ".to_string())), ); } pub(crate) fn present(&self) { let buf = self.buf.borrow(); + let mut tmp_string = String::new(); for y in 0..self.h { - for x in 0..self.w { - if let Some(ref cell) = buf[y * self.w + x] { + let mut x = 0; + let mut last_style = Style(Color::Black, Color::Black); + let mut left_x = 0; + while x < self.w { + if let Some((style, ref text)) = buf[y * self.w + x] { write!( self.out.borrow_mut(), - "{}{}", + "{}{}{}", termion::cursor::Goto((x + 1) as u16, (y + 1) as u16), - cell + style, + text, ).unwrap(); } + x += 1; } } self.out.borrow_mut().flush().unwrap(); } - pub(crate) fn draw( - &self, - x: usize, - y: usize, - text: &str, - fg: C1, - bg: C2, - ) { + pub(crate) fn draw(&self, x: usize, y: usize, text: &str, style: Style) { 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 { - buf[y * self.w + x] = Some(format!("{}{}{}", color::Fg(fg), color::Bg(bg), g)); + buf[y * self.w + x] = Some((style, g.to_string())); x += 1; for _ in 0..(width - 1) { buf[y * self.w + x] = None; @@ -130,3 +115,71 @@ impl Drop for Screen { 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)), + } + } +}