ui: correctly turn on/off terminal attributes in draw_horizontal_segment()

`Attr` (terminal attributes such as bold, underline, etc) were not being
printed properly: their bitmap representation was printed instead of the
correct ANSI codes to turn them on/off. This worked so far because the
attributes and {fore,back}ground color was reset in every character
print.

draw_horizontal_segment() now keeps state of current_{fg,bg,attr} to
keep from resetting in each column draw.
async
Manos Pitsidianakis 2020-01-27 17:07:29 +02:00
parent 77d9cef6fc
commit 3c7328d901
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 85 additions and 57 deletions

View File

@ -491,39 +491,32 @@ impl State {
cursor::Goto(x_start as u16 + 1, (y + 1) as u16) cursor::Goto(x_start as u16 + 1, (y + 1) as u16)
) )
.unwrap(); .unwrap();
let mut current_fg = Color::Default;
let mut current_bg = Color::Default;
let mut current_attrs = Attr::Default;
let Self {
ref grid,
ref mut stdout,
..
} = self;
let stdout = stdout.as_mut().unwrap();
write!(stdout, "\x1B[m").unwrap();
for x in x_start..=x_end { for x in x_start..=x_end {
let c = self.grid[(x, y)]; let c = &grid[(x, y)];
if c.bg() != Color::Default { if c.attrs() != current_attrs {
c.bg().write_bg(self.stdout()).unwrap(); c.attrs().write(current_attrs, stdout).unwrap();
current_attrs = c.attrs();
} }
if c.fg() != Color::Default { if c.bg() != current_bg {
c.fg().write_fg(self.stdout()).unwrap(); c.bg().write_bg(stdout).unwrap();
current_bg = c.bg();
} }
if c.attrs() != Attr::Default { if c.fg() != current_fg {
write!(self.stdout(), "\x1B[{}m", c.attrs() as u8).unwrap(); c.fg().write_fg(stdout).unwrap();
current_fg = c.fg();
} }
if !c.empty() { if !c.empty() {
write!(self.stdout(), "{}", c.ch()).unwrap(); write!(stdout, "{}", c.ch()).unwrap();
}
if c.bg() != Color::Default {
write!(
self.stdout(),
"{}",
termion::color::Bg(termion::color::Reset)
)
.unwrap();
}
if c.fg() != Color::Default {
write!(
self.stdout(),
"{}",
termion::color::Fg(termion::color::Reset)
)
.unwrap();
}
if c.attrs() != Attr::Default {
write!(self.stdout(), "\x1B[{}m", Attr::Default as u8).unwrap();
} }
} }
} }

View File

@ -601,20 +601,23 @@ impl Cell {
self.empty self.empty
} }
pub fn set_empty(&mut self, new_val: bool) { pub fn set_empty(&mut self, new_val: bool) -> &mut Cell {
self.empty = new_val; self.empty = new_val;
self
} }
/// Sets `keep_fg` field. If true, the foreground color will not be altered if attempted so /// Sets `keep_fg` field. If true, the foreground color will not be altered if attempted so
/// until the character content of the cell is changed. /// until the character content of the cell is changed.
pub fn set_keep_fg(&mut self, new_val: bool) { pub fn set_keep_fg(&mut self, new_val: bool) -> &mut Cell {
self.keep_fg = new_val; self.keep_fg = new_val;
self
} }
/// Sets `keep_bg` field. If true, the background color will not be altered if attempted so /// Sets `keep_bg` field. If true, the background color will not be altered if attempted so
/// until the character content of the cell is changed. /// until the character content of the cell is changed.
pub fn set_keep_bg(&mut self, new_val: bool) { pub fn set_keep_bg(&mut self, new_val: bool) -> &mut Cell {
self.keep_bg = new_val; self.keep_bg = new_val;
self
} }
} }
@ -711,6 +714,7 @@ impl Color {
pub fn write_fg(self, stdout: &mut crate::StateStdout) -> std::io::Result<()> { pub fn write_fg(self, stdout: &mut crate::StateStdout) -> std::io::Result<()> {
use std::io::Write; use std::io::Write;
match self { match self {
Color::Default => write!(stdout, "{}", termion::color::Fg(termion::color::Reset)),
Color::Rgb(r, g, b) => write!(stdout, "{}", termion::color::Fg(TermionRgb(r, g, b))), Color::Rgb(r, g, b) => write!(stdout, "{}", termion::color::Fg(TermionRgb(r, g, b))),
_ => write!(stdout, "{}", termion::color::Fg(self.as_termion())), _ => write!(stdout, "{}", termion::color::Fg(self.as_termion())),
} }
@ -719,6 +723,7 @@ impl Color {
pub fn write_bg(self, stdout: &mut crate::StateStdout) -> std::io::Result<()> { pub fn write_bg(self, stdout: &mut crate::StateStdout) -> std::io::Result<()> {
use std::io::Write; use std::io::Write;
match self { match self {
Color::Default => write!(stdout, "{}", termion::color::Bg(termion::color::Reset)),
Color::Rgb(r, g, b) => write!(stdout, "{}", termion::color::Bg(TermionRgb(r, g, b))), Color::Rgb(r, g, b) => write!(stdout, "{}", termion::color::Bg(TermionRgb(r, g, b))),
_ => write!(stdout, "{}", termion::color::Bg(self.as_termion())), _ => write!(stdout, "{}", termion::color::Bg(self.as_termion())),
} }
@ -1001,6 +1006,7 @@ impl Color {
"Yellow4" => 100, "Yellow4" => 100,
"Yellow5" => 106, "Yellow5" => 106,
"Yellow6" => 148, "Yellow6" => 148,
"Default" => return Ok(Color::Default),
s if s.starts_with("#") s if s.starts_with("#")
&& s.len() == 7 && s.len() == 7
&& s[1..].as_bytes().iter().all(|&b| { && s[1..].as_bytes().iter().all(|&b| {
@ -1388,6 +1394,14 @@ pub enum Attr {
BoldReverseUnderline = 0b111, BoldReverseUnderline = 0b111,
} }
impl core::ops::BitAnd for Attr {
type Output = bool;
fn bitand(self, rhs: Self) -> Self::Output {
self as u8 & rhs as u8 > 0
}
}
impl Default for Attr { impl Default for Attr {
fn default() -> Self { fn default() -> Self {
Attr::Default Attr::Default
@ -1442,6 +1456,24 @@ impl Attr {
_ => Err(de::Error::custom("invalid attr value")), _ => Err(de::Error::custom("invalid attr value")),
} }
} }
pub fn write(self, prev: Attr, stdout: &mut crate::StateStdout) -> std::io::Result<()> {
use std::io::Write;
match (self & Attr::Bold, prev & Attr::Bold) {
(true, true) | (false, false) => Ok(()),
(false, true) => write!(stdout, "\x1B[22m"),
(true, false) => write!(stdout, "\x1B[1m"),
}
.and_then(|_| match (self & Attr::Underline, prev & Attr::Underline) {
(true, true) | (false, false) => Ok(()),
(false, true) => write!(stdout, "\x1B[24m"),
(true, false) => write!(stdout, "\x1B[4m"),
})
.and_then(|_| match (self & Attr::Reverse, prev & Attr::Reverse) {
(true, true) | (false, false) => Ok(()),
(false, true) => write!(stdout, "\x1B[27m"),
(true, false) => write!(stdout, "\x1B[7m"),
})
}
} }
pub fn copy_area_with_break( pub fn copy_area_with_break(
@ -1542,26 +1574,27 @@ pub fn copy_area(grid_dest: &mut CellBuffer, grid_src: &CellBuffer, dest: Area,
/// Change foreground and background colors in an `Area` /// Change foreground and background colors in an `Area`
pub fn change_colors(grid: &mut CellBuffer, area: Area, fg_color: Color, bg_color: Color) { pub fn change_colors(grid: &mut CellBuffer, area: Area, fg_color: Color, bg_color: Color) {
let bounds = grid.size(); if cfg!(feature = "debug-tracing") {
let upper_left = upper_left!(area); let bounds = grid.size();
let bottom_right = bottom_right!(area); let upper_left = upper_left!(area);
let (x, y) = upper_left; let bottom_right = bottom_right!(area);
if y > (get_y(bottom_right)) let (x, y) = upper_left;
|| x > get_x(bottom_right) if y > (get_y(bottom_right))
|| y >= get_y(bounds) || x > get_x(bottom_right)
|| x >= get_x(bounds) || y >= get_y(bounds)
{ || x >= get_x(bounds)
debug!("BUG: Invalid area in change_colors:\n area: {:?}", area); {
return; debug!("BUG: Invalid area in change_colors:\n area: {:?}", area);
return;
}
if !is_valid_area!(area) {
debug!("BUG: Invalid area in change_colors:\n area: {:?}", area);
return;
}
} }
if !is_valid_area!(area) { for row in grid.bounds_iter(area) {
debug!("BUG: Invalid area in change_colors:\n area: {:?}", area); for c in row {
return; grid[c].set_fg(fg_color).set_bg(bg_color);
}
for y in get_y(upper_left!(area))..=get_y(bottom_right!(area)) {
for x in get_x(upper_left!(area))..=get_x(bottom_right!(area)) {
grid[(x, y)].set_fg(fg_color);
grid[(x, y)].set_bg(bg_color);
} }
} }
} }
@ -1648,9 +1681,10 @@ pub fn write_string_to_grid(
} else { } else {
grid[(x, y)].set_ch(c); grid[(x, y)].set_ch(c);
} }
grid[(x, y)].set_attrs(attrs); grid[(x, y)]
grid[(x, y)].set_fg(fg_color); .set_fg(fg_color)
grid[(x, y)].set_bg(bg_color); .set_bg(bg_color)
.set_attrs(attrs);
match wcwidth(u32::from(c)) { match wcwidth(u32::from(c)) {
Some(0) | None => { Some(0) | None => {
@ -1663,10 +1697,11 @@ pub fn write_string_to_grid(
x += 1; x += 1;
inspect_bounds!(grid, area, x, y, line_break); inspect_bounds!(grid, area, x, y, line_break);
grid[(x, y)] = Cell::default(); grid[(x, y)] = Cell::default();
grid[(x, y)].set_attrs(attrs); grid[(x, y)]
grid[(x, y)].set_fg(fg_color); .set_fg(fg_color)
grid[(x, y)].set_bg(bg_color); .set_bg(bg_color)
grid[(x, y)].empty = true; .set_attrs(attrs)
.set_empty(true);
} }
_ => {} _ => {}
} }