diff --git a/examples/crossterm_demo.rs b/examples/crossterm_demo.rs index 9a856a5f..2bf2b501 100644 --- a/examples/crossterm_demo.rs +++ b/examples/crossterm_demo.rs @@ -7,7 +7,7 @@ use std::sync::mpsc; use std::thread; use std::time::Duration; -use crossterm::{input, AlternateScreen, InputEvent, KeyEvent, RawScreen}; +use crossterm::{input, AlternateScreen, InputEvent, KeyEvent}; use structopt::StructOpt; use tui::backend::CrosstermBackend; use tui::Terminal; diff --git a/src/backend/crossterm.rs b/src/backend/crossterm.rs index daeecf69..fa73ef25 100644 --- a/src/backend/crossterm.rs +++ b/src/backend/crossterm.rs @@ -1,13 +1,19 @@ -use std::io; - -use crate::backend::Backend; -use crate::style::{Color, Modifier}; -use crate::{buffer::Cell, layout::Rect, style}; -use crossterm::{ - execute, queue, terminal, Clear, ClearType, Command, Crossterm, ErrorKind, Goto, Hide, Output, - SetAttr, SetBg, SetFg, Show, +use std::{ + fmt, + io::{self, Write}, +}; + +use crossterm::{ + execute, queue, terminal, Clear, ClearType, Crossterm, ErrorKind, Goto, Hide, SetAttr, SetBg, + SetFg, Show, +}; + +use crate::{ + backend::Backend, + buffer::Cell, + layout::Rect, + style::{Color, Modifier, Style}, }; -use std::io::{stdout, Stdout, Write}; pub struct CrosstermBackend { alternate_screen: Option, @@ -47,8 +53,6 @@ where } } -// TODO: consider associated Error type on Backend to allow custom error types -// per backend fn convert_error(error: ErrorKind) -> io::Error { match error { ErrorKind::IoError(err) => err, @@ -81,21 +85,15 @@ where W: Write, { fn clear(&mut self) -> io::Result<()> { - queue!(self.stdout, Clear(ClearType::All)); - self.stdout.flush(); - Ok(()) + execute!(self.stdout, Clear(ClearType::All)).map_err(convert_error) } fn hide_cursor(&mut self) -> io::Result<()> { - execute!(self.stdout, Hide); - self.stdout.flush(); - Ok(()) + execute!(self.stdout, Hide).map_err(convert_error) } fn show_cursor(&mut self) -> io::Result<()> { - execute!(self.stdout, Show); - self.stdout.flush(); - Ok(()) + execute!(self.stdout, Show).map_err(convert_error) } fn get_cursor(&mut self) -> io::Result<(u16, u16)> { @@ -104,9 +102,7 @@ where } fn set_cursor(&mut self, x: u16, y: u16) -> io::Result<()> { - queue!(self.stdout, Goto(x, y)); - self.stdout.flush(); - Ok(()) + execute!(self.stdout, Goto(x, y)).map_err(convert_error) } fn size(&self) -> io::Result { @@ -127,33 +123,35 @@ where use std::fmt::Write; let mut string = String::with_capacity(content.size_hint().0 * 3); - let mut style = style::Style::default(); + let mut style = Style::default(); let mut last_y = 0; let mut last_x = 0; let mut inst = 0; for (x, y, cell) in content { if y != last_y || x != last_x + 1 || inst == 0 { - queue!(string, Goto(x, y)); + queue!(string, Goto(x, y))?; } last_x = x; last_y = y; if cell.style.modifier != style.modifier { - for attr in to_crossterm_attributes(cell.style.modifier) { - queue!(string, SetAttr(attr)); - } + let diff = ModifierDiff { + from: style.modifier, + to: cell.style.modifier, + }; + diff.queue(&mut string)?; inst += 1; style.modifier = cell.style.modifier; } if cell.style.fg != style.fg { let color = to_crossterm_color(cell.style.fg); - queue!(string, SetFg(color)); + queue!(string, SetFg(color))?; style.fg = cell.style.fg; inst += 1; } if cell.style.bg != style.bg { let color = to_crossterm_color(cell.style.bg); - queue!(string, SetBg(color)); + queue!(string, SetBg(color))?; style.bg = cell.style.bg; inst += 1; } @@ -169,61 +167,108 @@ where SetFg(crossterm::Color::Reset), SetBg(crossterm::Color::Reset), SetAttr(crossterm::Attribute::Reset) - ); + )?; - Crossterm::new().color().reset(); + Crossterm::new().color().reset()?; Ok(()) } } +#[derive(Debug)] +struct ModifierDiff { + pub from: Modifier, + pub to: Modifier, +} + #[cfg(unix)] -fn to_crossterm_attributes(modifier: Modifier) -> Vec { - let mut result = Vec::new(); +impl ModifierDiff { + fn queue(&self, mut w: W) -> io::Result<()> + where + W: fmt::Write, + { + use crossterm::Attribute; + let removed = self.from - self.to; + if removed.contains(Modifier::REVERSED) { + queue!(w, SetAttr(Attribute::NoInverse))?; + } + if removed.contains(Modifier::BOLD) { + queue!(w, SetAttr(Attribute::NormalIntensity))?; + if self.to.contains(Modifier::DIM) { + queue!(w, SetAttr(Attribute::Dim))?; + } + } + if removed.contains(Modifier::ITALIC) { + queue!(w, SetAttr(Attribute::NoItalic))?; + } + if removed.contains(Modifier::UNDERLINED) { + queue!(w, SetAttr(Attribute::NoUnderline))?; + } + if removed.contains(Modifier::DIM) { + queue!(w, SetAttr(Attribute::NormalIntensity))?; + } + if removed.contains(Modifier::CROSSED_OUT) { + queue!(w, SetAttr(Attribute::NotCrossedOut))?; + } + if removed.contains(Modifier::SLOW_BLINK) || removed.contains(Modifier::RAPID_BLINK) { + queue!(w, SetAttr(Attribute::NoBlink))?; + } - if modifier.contains(Modifier::BOLD) { - result.push(crossterm::Attribute::Bold) - } - if modifier.contains(Modifier::DIM) { - result.push(crossterm::Attribute::Dim) - } - if modifier.contains(Modifier::ITALIC) { - result.push(crossterm::Attribute::Italic) - } - if modifier.contains(Modifier::UNDERLINED) { - result.push(crossterm::Attribute::Underlined) - } - if modifier.contains(Modifier::SLOW_BLINK) { - result.push(crossterm::Attribute::SlowBlink) - } - if modifier.contains(Modifier::RAPID_BLINK) { - result.push(crossterm::Attribute::RapidBlink) - } - if modifier.contains(Modifier::REVERSED) { - result.push(crossterm::Attribute::Reverse) - } - if modifier.contains(Modifier::HIDDEN) { - result.push(crossterm::Attribute::Hidden) - } - if modifier.contains(Modifier::CROSSED_OUT) { - result.push(crossterm::Attribute::CrossedOut) - } + let added = self.to - self.from; + if added.contains(Modifier::REVERSED) { + queue!(w, SetAttr(Attribute::Reverse))?; + } + if added.contains(Modifier::BOLD) { + queue!(w, SetAttr(Attribute::Bold))?; + } + if added.contains(Modifier::ITALIC) { + queue!(w, SetAttr(Attribute::Italic))?; + } + if added.contains(Modifier::UNDERLINED) { + queue!(w, SetAttr(Attribute::Underlined))?; + } + if added.contains(Modifier::DIM) { + queue!(w, SetAttr(Attribute::Dim))?; + } + if added.contains(Modifier::CROSSED_OUT) { + queue!(w, SetAttr(Attribute::CrossedOut))?; + } + if added.contains(Modifier::SLOW_BLINK) { + queue!(w, SetAttr(Attribute::SlowBlink))?; + } + if added.contains(Modifier::RAPID_BLINK) { + queue!(w, SetAttr(Attribute::RapidBlink))?; + } - result + Ok(()) + } } #[cfg(windows)] -fn to_crossterm_attributes(modifier: Modifier) -> Vec { - let mut result = Vec::new(); +impl ModifierDiff { + fn queue(&self, mut w: W) -> io::Result<()> + where + W: fmt::Write, + { + use crossterm::Attribute; - if modifier.contains(Modifier::BOLD) { - result.push(crossterm::Attribute::Bold) - } - if modifier.contains(Modifier::UNDERLINED) { - result.push(crossterm::Attribute::Underlined) - } + let removed = self.from - self.to; + if removed.contains(Modifier::BOLD) { + queue!(w, SetAttr(Attribute::NormalIntensity))?; + } + if removed.contains(Modifier::UNDERLINED) { + queue!(w, SetAttr(Attribute::NoUnderline))?; + } - result + let added = self.to - self.from; + if added.contains(Modifier::BOLD) { + queue!(w, SetAttr(Attribute::Bold))?; + } + if added.contains(Modifier::UNDERLINED) { + queue!(w, SetAttr(Attribute::Underlined))?; + } + Ok(()) + } } fn to_crossterm_color(color: Color) -> crossterm::Color {