Support several modifiers and indexed colors at once

This commit is contained in:
David Flemström 2019-02-25 15:15:00 +01:00 committed by Florian Dehau
parent d360cd3434
commit b7664a4108
17 changed files with 265 additions and 197 deletions

View file

@ -97,7 +97,7 @@ fn main() -> Result<(), failure::Error> {
.bar_width(5) .bar_width(5)
.bar_gap(3) .bar_gap(3)
.style(Style::default().fg(Color::Green)) .style(Style::default().fg(Color::Green))
.value_style(Style::default().bg(Color::Green).modifier(Modifier::Bold)) .value_style(Style::default().bg(Color::Green).modifier(Modifier::BOLD))
.render(&mut f, chunks[0]); .render(&mut f, chunks[0]);
BarChart::default() BarChart::default()
.block(Block::default().title("Data3").borders(Borders::ALL)) .block(Block::default().title("Data3").borders(Borders::ALL))
@ -106,7 +106,7 @@ fn main() -> Result<(), failure::Error> {
.bar_width(7) .bar_width(7)
.bar_gap(0) .bar_gap(0)
.value_style(Style::default().bg(Color::Red)) .value_style(Style::default().bg(Color::Red))
.label_style(Style::default().fg(Color::Cyan).modifier(Modifier::Italic)) .label_style(Style::default().fg(Color::Cyan).modifier(Modifier::ITALIC))
.render(&mut f, chunks[1]); .render(&mut f, chunks[1]);
} }
})?; })?;

View file

@ -54,7 +54,7 @@ fn main() -> Result<(), failure::Error> {
Style::default() Style::default()
.fg(Color::White) .fg(Color::White)
.bg(Color::Red) .bg(Color::Red)
.modifier(Modifier::Bold), .modifier(Modifier::BOLD),
) )
.render(&mut f, chunks[1]); .render(&mut f, chunks[1]);
} }

View file

@ -73,14 +73,14 @@ fn main() -> Result<(), failure::Error> {
.block( .block(
Block::default() Block::default()
.title("Chart") .title("Chart")
.title_style(Style::default().fg(Color::Cyan).modifier(Modifier::Bold)) .title_style(Style::default().fg(Color::Cyan).modifier(Modifier::BOLD))
.borders(Borders::ALL), .borders(Borders::ALL),
) )
.x_axis( .x_axis(
Axis::default() Axis::default()
.title("X Axis") .title("X Axis")
.style(Style::default().fg(Color::Gray)) .style(Style::default().fg(Color::White))
.labels_style(Style::default().modifier(Modifier::Italic)) .labels_style(Style::default().modifier(Modifier::ITALIC))
.bounds(app.window) .bounds(app.window)
.labels(&[ .labels(&[
&format!("{}", app.window[0]), &format!("{}", app.window[0]),
@ -91,8 +91,8 @@ fn main() -> Result<(), failure::Error> {
.y_axis( .y_axis(
Axis::default() Axis::default()
.title("Y Axis") .title("Y Axis")
.style(Style::default().fg(Color::Gray)) .style(Style::default().fg(Color::White))
.labels_style(Style::default().modifier(Modifier::Italic)) .labels_style(Style::default().modifier(Modifier::ITALIC))
.bounds([-20.0, 20.0]) .bounds([-20.0, 20.0])
.labels(&["-20", "0", "20"]), .labels(&["-20", "0", "20"]),
) )

View file

@ -69,7 +69,7 @@ where
Style::default() Style::default()
.fg(Color::Magenta) .fg(Color::Magenta)
.bg(Color::Black) .bg(Color::Black)
.modifier(Modifier::Italic), .modifier(Modifier::ITALIC),
) )
.label(&format!("{} / 100", app.progress)) .label(&format!("{} / 100", app.progress))
.percent(app.progress) .percent(app.progress)
@ -107,7 +107,7 @@ where
.block(Block::default().borders(Borders::ALL).title("List")) .block(Block::default().borders(Borders::ALL).title("List"))
.items(&app.tasks.items) .items(&app.tasks.items)
.select(Some(app.tasks.selected)) .select(Some(app.tasks.selected))
.highlight_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold)) .highlight_style(Style::default().fg(Color::Yellow).modifier(Modifier::BOLD))
.highlight_symbol(">") .highlight_symbol(">")
.render(f, chunks[0]); .render(f, chunks[0]);
let info_style = Style::default().fg(Color::White); let info_style = Style::default().fg(Color::White);
@ -138,7 +138,7 @@ where
Style::default() Style::default()
.fg(Color::Black) .fg(Color::Black)
.bg(Color::Green) .bg(Color::Green)
.modifier(Modifier::Italic), .modifier(Modifier::ITALIC),
) )
.label_style(Style::default().fg(Color::Yellow)) .label_style(Style::default().fg(Color::Yellow))
.style(Style::default().fg(Color::Green)) .style(Style::default().fg(Color::Green))
@ -149,14 +149,14 @@ where
.block( .block(
Block::default() Block::default()
.title("Chart") .title("Chart")
.title_style(Style::default().fg(Color::Cyan).modifier(Modifier::Bold)) .title_style(Style::default().fg(Color::Cyan).modifier(Modifier::BOLD))
.borders(Borders::ALL), .borders(Borders::ALL),
) )
.x_axis( .x_axis(
Axis::default() Axis::default()
.title("X Axis") .title("X Axis")
.style(Style::default().fg(Color::Gray)) .style(Style::default().fg(Color::Gray))
.labels_style(Style::default().modifier(Modifier::Italic)) .labels_style(Style::default().modifier(Modifier::ITALIC))
.bounds(app.signals.window) .bounds(app.signals.window)
.labels(&[ .labels(&[
&format!("{}", app.signals.window[0]), &format!("{}", app.signals.window[0]),
@ -168,7 +168,7 @@ where
Axis::default() Axis::default()
.title("Y Axis") .title("Y Axis")
.style(Style::default().fg(Color::Gray)) .style(Style::default().fg(Color::Gray))
.labels_style(Style::default().modifier(Modifier::Italic)) .labels_style(Style::default().modifier(Modifier::ITALIC))
.bounds([-20.0, 20.0]) .bounds([-20.0, 20.0])
.labels(&["-20", "0", "20"]), .labels(&["-20", "0", "20"]),
) )
@ -200,13 +200,13 @@ where
Text::raw(" "), Text::raw(" "),
Text::styled("rainbow", Style::default().fg(Color::Blue)), Text::styled("rainbow", Style::default().fg(Color::Blue)),
Text::raw(".\nOh and if you didn't "), Text::raw(".\nOh and if you didn't "),
Text::styled("notice", Style::default().modifier(Modifier::Italic)), Text::styled("notice", Style::default().modifier(Modifier::ITALIC)),
Text::raw(" you can "), Text::raw(" you can "),
Text::styled("automatically", Style::default().modifier(Modifier::Bold)), Text::styled("automatically", Style::default().modifier(Modifier::BOLD)),
Text::raw(" "), Text::raw(" "),
Text::styled("wrap", Style::default().modifier(Modifier::Invert)), Text::styled("wrap", Style::default().modifier(Modifier::REVERSED)),
Text::raw(" your "), Text::raw(" your "),
Text::styled("text", Style::default().modifier(Modifier::Underline)), Text::styled("text", Style::default().modifier(Modifier::UNDERLINED)),
Text::raw(".\nOne more thing is that it should display unicode characters: 10€") Text::raw(".\nOne more thing is that it should display unicode characters: 10€")
]; ];
Paragraph::new(text.iter()) Paragraph::new(text.iter())
@ -214,7 +214,7 @@ where
Block::default() Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.title("Footer") .title("Footer")
.title_style(Style::default().fg(Color::Magenta).modifier(Modifier::Bold)), .title_style(Style::default().fg(Color::Magenta).modifier(Modifier::BOLD)),
) )
.wrap(true) .wrap(true)
.render(f, area); .render(f, area);

View file

@ -99,7 +99,7 @@ fn main() -> Result<(), failure::Error> {
.render(&mut f, chunks[2]); .render(&mut f, chunks[2]);
Gauge::default() Gauge::default()
.block(Block::default().title("Gauge4").borders(Borders::ALL)) .block(Block::default().title("Gauge4").borders(Borders::ALL))
.style(Style::default().fg(Color::Cyan).modifier(Modifier::Italic)) .style(Style::default().fg(Color::Cyan).modifier(Modifier::ITALIC))
.percent(app.progress4) .percent(app.progress4)
.label(&format!("{}/100", app.progress2)) .label(&format!("{}/100", app.progress2))
.render(&mut f, chunks[3]); .render(&mut f, chunks[3]);

View file

@ -102,7 +102,7 @@ fn main() -> Result<(), failure::Error> {
.items(&app.items) .items(&app.items)
.select(app.selected) .select(app.selected)
.style(style) .style(style)
.highlight_style(style.fg(Color::LightGreen).modifier(Modifier::Bold)) .highlight_style(style.fg(Color::LightGreen).modifier(Modifier::BOLD))
.highlight_symbol(">") .highlight_symbol(">")
.render(&mut f, chunks[0]); .render(&mut f, chunks[0]);
{ {

View file

@ -60,18 +60,18 @@ fn main() -> Result<(), failure::Error> {
Text::styled("This is a line\n", Style::default().bg(Color::Blue)), Text::styled("This is a line\n", Style::default().bg(Color::Blue)),
Text::styled( Text::styled(
"This is a longer line\n", "This is a longer line\n",
Style::default().modifier(Modifier::CrossedOut), Style::default().modifier(Modifier::CROSSED_OUT),
), ),
Text::styled(&long_line, Style::default().bg(Color::Green)), Text::styled(&long_line, Style::default().bg(Color::Green)),
Text::styled( Text::styled(
"This is a line\n", "This is a line\n",
Style::default().fg(Color::Green).modifier(Modifier::Italic), Style::default().fg(Color::Green).modifier(Modifier::ITALIC),
), ),
]; ];
let block = Block::default() let block = Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
.title_style(Style::default().modifier(Modifier::Bold)); .title_style(Style::default().modifier(Modifier::BOLD));
Paragraph::new(text.iter()) Paragraph::new(text.iter())
.block(block.clone().title("Left, no wrap")) .block(block.clone().title("Left, no wrap"))
.alignment(Alignment::Left) .alignment(Alignment::Left)

View file

@ -53,7 +53,7 @@ fn main() -> Result<(), failure::Error> {
// Input // Input
loop { loop {
terminal.draw(|mut f| { terminal.draw(|mut f| {
let selected_style = Style::default().fg(Color::Yellow).modifier(Modifier::Bold); let selected_style = Style::default().fg(Color::Yellow).modifier(Modifier::BOLD);
let normal_style = Style::default().fg(Color::White); let normal_style = Style::default().fg(Color::White);
let header = ["Header1", "Header2", "Header3"]; let header = ["Header1", "Header2", "Header3"];
let rows = app.items.iter().enumerate().map(|(i, item)| { let rows = app.items.iter().enumerate().map(|(i, item)| {

View file

@ -149,9 +149,8 @@ impl Backend for CrosstermBackend {
if let Some(color) = cell.style.bg.into() { if let Some(color) = cell.style.bg.into() {
s = s.on(color) s = s.on(color)
} }
if let Some(attr) = cell.style.modifier.into() { s.object_style.attrs = cell.style.modifier.into();
s = s.attr(attr)
}
self.crossterm.paint(s).map_err(convert_error)?; self.crossterm.paint(s).map_err(convert_error)?;
} }
Ok(()) Ok(())
@ -178,32 +177,59 @@ impl From<Color> for Option<crossterm::Color> {
Color::LightMagenta => Some(crossterm::Color::Magenta), Color::LightMagenta => Some(crossterm::Color::Magenta),
Color::LightCyan => Some(crossterm::Color::Cyan), Color::LightCyan => Some(crossterm::Color::Cyan),
Color::White => Some(crossterm::Color::White), Color::White => Some(crossterm::Color::White),
Color::Indexed(i) => Some(crossterm::Color::AnsiValue(i)),
Color::Rgb(r, g, b) => Some(crossterm::Color::Rgb { r, g, b }), Color::Rgb(r, g, b) => Some(crossterm::Color::Rgb { r, g, b }),
} }
} }
} }
impl From<Modifier> for Option<crossterm::Attribute> { impl From<Modifier> for Vec<crossterm::Attribute> {
#[cfg(unix)] #[cfg(unix)]
fn from(modifier: Modifier) -> Option<crossterm::Attribute> { fn from(modifier: Modifier) -> Vec<crossterm::Attribute> {
match modifier { let mut result = Vec::new();
Modifier::Blink => Some(crossterm::Attribute::SlowBlink),
Modifier::Bold => Some(crossterm::Attribute::Bold), if modifier.contains(Modifier::BOLD) {
Modifier::CrossedOut => Some(crossterm::Attribute::CrossedOut), result.push(crossterm::Attribute::Bold)
Modifier::Faint => Some(crossterm::Attribute::Dim),
Modifier::Invert => Some(crossterm::Attribute::Reverse),
Modifier::Italic => Some(crossterm::Attribute::Italic),
Modifier::Underline => Some(crossterm::Attribute::Underlined),
_ => None,
} }
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)
}
result
} }
#[cfg(windows)] #[cfg(windows)]
fn from(modifier: Modifier) -> Option<crossterm::Attribute> { fn from(modifier: Modifier) -> Vec<crossterm::Attribute> {
match modifier { let mut result = Vec::new();
Modifier::Bold => Some(crossterm::Attribute::Bold),
Modifier::Underline => Some(crossterm::Attribute::Underlined), if modifier.contains(Modifier::BOLD) {
_ => None, result.push(crossterm::Attribute::Bold)
} }
if modifier.contains(Modifier::UNDERLINED) {
result.push(crossterm::Attribute::Underlined)
}
result
} }
} }

View file

@ -103,6 +103,7 @@ impl Into<rustbox::Color> for Color {
Color::Cyan | Color::LightCyan => rustbox::Color::Cyan, Color::Cyan | Color::LightCyan => rustbox::Color::Cyan,
Color::White => rustbox::Color::White, Color::White => rustbox::Color::White,
Color::Blue | Color::LightBlue => rustbox::Color::Blue, Color::Blue | Color::LightBlue => rustbox::Color::Blue,
Color::Indexed(i) => rustbox::Color::Byte(i as u16),
Color::Rgb(r, g, b) => rustbox::Color::Byte(rgb_to_byte(r, g, b)), Color::Rgb(r, g, b) => rustbox::Color::Byte(rgb_to_byte(r, g, b)),
} }
} }
@ -110,11 +111,16 @@ impl Into<rustbox::Color> for Color {
impl Into<rustbox::Style> for Modifier { impl Into<rustbox::Style> for Modifier {
fn into(self) -> rustbox::Style { fn into(self) -> rustbox::Style {
match self { let mut result = rustbox::Style::empty();
Modifier::Bold => rustbox::RB_BOLD, if self.contains(Modifier::BOLD) {
Modifier::Underline => rustbox::RB_UNDERLINE, result.insert(rustbox::RB_BOLD);
Modifier::Invert => rustbox::RB_REVERSE,
_ => rustbox::RB_NORMAL,
} }
if self.contains(Modifier::UNDERLINED) {
result.insert(rustbox::RB_UNDERLINE);
}
if self.contains(Modifier::REVERSED) {
result.insert(rustbox::RB_REVERSE);
}
result
} }
} }

View file

@ -1,11 +1,12 @@
use log::debug; use log::debug;
use std::fmt;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use super::Backend; use super::Backend;
use crate::buffer::Cell; use crate::buffer::Cell;
use crate::layout::Rect; use crate::layout::Rect;
use crate::style::{Color, Modifier, Style}; use crate::style;
pub struct TermionBackend<W> pub struct TermionBackend<W>
where where
@ -74,34 +75,40 @@ where
where where
I: Iterator<Item = (u16, u16, &'a Cell)>, I: Iterator<Item = (u16, u16, &'a Cell)>,
{ {
use std::fmt::Write;
let mut string = String::with_capacity(content.size_hint().0 * 3); let mut string = String::with_capacity(content.size_hint().0 * 3);
let mut style = Style::default(); let mut style = style::Style::default();
let mut last_y = 0; let mut last_y = 0;
let mut last_x = 0; let mut last_x = 0;
let mut inst = 0; let mut inst = 0;
for (x, y, cell) in content { for (x, y, cell) in content {
if y != last_y || x != last_x + 1 || inst == 0 { if y != last_y || x != last_x + 1 || inst == 0 {
string.push_str(&format!("{}", termion::cursor::Goto(x + 1, y + 1))); write!(string, "{}", termion::cursor::Goto(x + 1, y + 1)).unwrap();
inst += 1; inst += 1;
} }
last_x = x; last_x = x;
last_y = y; last_y = y;
if cell.style.modifier != style.modifier { if cell.style.modifier != style.modifier {
string.push_str(&cell.style.modifier.termion_modifier()); write!(
string,
"{}",
ModifierDiff {
from: style.modifier,
to: cell.style.modifier
}
)
.unwrap();
style.modifier = cell.style.modifier; style.modifier = cell.style.modifier;
if style.modifier == Modifier::Reset {
style.bg = Color::Reset;
style.fg = Color::Reset;
}
inst += 1; inst += 1;
} }
if cell.style.fg != style.fg { if cell.style.fg != style.fg {
string.push_str(&cell.style.fg.termion_fg()); write!(string, "{}", Fg(cell.style.fg)).unwrap();
style.fg = cell.style.fg; style.fg = cell.style.fg;
inst += 1; inst += 1;
} }
if cell.style.bg != style.bg { if cell.style.bg != style.bg {
string.push_str(&cell.style.bg.termion_bg()); write!(string, "{}", Bg(cell.style.bg)).unwrap();
style.bg = cell.style.bg; style.bg = cell.style.bg;
inst += 1; inst += 1;
} }
@ -113,9 +120,9 @@ where
self.stdout, self.stdout,
"{}{}{}{}", "{}{}{}{}",
string, string,
Color::Reset.termion_fg(), Fg(style::Color::Reset),
Color::Reset.termion_bg(), Bg(style::Color::Reset),
Modifier::Reset.termion_modifier() termion::style::Reset,
) )
} }
@ -130,102 +137,118 @@ where
} }
} }
macro_rules! termion_fg { struct Fg(style::Color);
($color:ident) => {
format!("{}", termion::color::Fg(termion::color::$color)) struct Bg(style::Color);
};
struct ModifierDiff {
from: style::Modifier,
to: style::Modifier,
} }
macro_rules! termion_fg_rgb { impl fmt::Display for Fg {
($r:expr, $g:expr, $b:expr) => { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format!("{}", termion::color::Fg(termion::color::Rgb($r, $g, $b))) use termion::color::Color;
}; match self.0 {
} style::Color::Reset => termion::color::Reset.write_fg(f),
style::Color::Black => termion::color::Black.write_fg(f),
macro_rules! termion_bg { style::Color::Red => termion::color::Red.write_fg(f),
($color:ident) => { style::Color::Green => termion::color::Green.write_fg(f),
format!("{}", termion::color::Bg(termion::color::$color)) style::Color::Yellow => termion::color::Yellow.write_fg(f),
}; style::Color::Blue => termion::color::Blue.write_fg(f),
} style::Color::Magenta => termion::color::Magenta.write_fg(f),
style::Color::Cyan => termion::color::Cyan.write_fg(f),
macro_rules! termion_bg_rgb { style::Color::Gray => termion::color::White.write_fg(f),
($r:expr, $g:expr, $b:expr) => { style::Color::DarkGray => termion::color::LightBlack.write_fg(f),
format!("{}", termion::color::Bg(termion::color::Rgb($r, $g, $b))) style::Color::LightRed => termion::color::LightRed.write_fg(f),
}; style::Color::LightGreen => termion::color::LightGreen.write_fg(f),
} style::Color::LightBlue => termion::color::LightBlue.write_fg(f),
style::Color::LightYellow => termion::color::LightYellow.write_fg(f),
macro_rules! termion_modifier { style::Color::LightMagenta => termion::color::LightMagenta.write_fg(f),
($style:ident) => { style::Color::LightCyan => termion::color::LightCyan.write_fg(f),
format!("{}", termion::style::$style) style::Color::White => termion::color::LightWhite.write_fg(f),
}; style::Color::Indexed(i) => termion::color::AnsiValue(i).write_fg(f),
} style::Color::Rgb(r, g, b) => termion::color::Rgb(r, g, b).write_fg(f),
impl Color {
pub fn termion_fg(self) -> String {
match self {
Color::Reset => termion_fg!(Reset),
Color::Black => termion_fg!(Black),
Color::Red => termion_fg!(Red),
Color::Green => termion_fg!(Green),
Color::Yellow => termion_fg!(Yellow),
Color::Blue => termion_fg!(Blue),
Color::Magenta => termion_fg!(Magenta),
Color::Cyan => termion_fg!(Cyan),
Color::Gray => termion_fg!(White),
Color::DarkGray => termion_fg!(LightBlack),
Color::LightRed => termion_fg!(LightRed),
Color::LightGreen => termion_fg!(LightGreen),
Color::LightBlue => termion_fg!(LightBlue),
Color::LightYellow => termion_fg!(LightYellow),
Color::LightMagenta => termion_fg!(LightMagenta),
Color::LightCyan => termion_fg!(LightCyan),
Color::White => termion_fg!(LightWhite),
Color::Rgb(r, g, b) => termion_fg_rgb!(r, g, b),
} }
} }
pub fn termion_bg(self) -> String { }
match self { impl fmt::Display for Bg {
Color::Reset => termion_bg!(Reset), fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Color::Black => termion_bg!(Black), use termion::color::Color;
Color::Red => termion_bg!(Red), match self.0 {
Color::Green => termion_bg!(Green), style::Color::Reset => termion::color::Reset.write_bg(f),
Color::Yellow => termion_bg!(Yellow), style::Color::Black => termion::color::Black.write_bg(f),
Color::Blue => termion_bg!(Blue), style::Color::Red => termion::color::Red.write_bg(f),
Color::Magenta => termion_bg!(Magenta), style::Color::Green => termion::color::Green.write_bg(f),
Color::Cyan => termion_bg!(Cyan), style::Color::Yellow => termion::color::Yellow.write_bg(f),
Color::Gray => termion_bg!(White), style::Color::Blue => termion::color::Blue.write_bg(f),
Color::DarkGray => termion_bg!(LightBlack), style::Color::Magenta => termion::color::Magenta.write_bg(f),
Color::LightRed => termion_bg!(LightRed), style::Color::Cyan => termion::color::Cyan.write_bg(f),
Color::LightGreen => termion_bg!(LightGreen), style::Color::Gray => termion::color::White.write_bg(f),
Color::LightBlue => termion_bg!(LightBlue), style::Color::DarkGray => termion::color::LightBlack.write_bg(f),
Color::LightYellow => termion_bg!(LightYellow), style::Color::LightRed => termion::color::LightRed.write_bg(f),
Color::LightMagenta => termion_bg!(LightMagenta), style::Color::LightGreen => termion::color::LightGreen.write_bg(f),
Color::LightCyan => termion_bg!(LightCyan), style::Color::LightBlue => termion::color::LightBlue.write_bg(f),
Color::White => termion_bg!(LightWhite), style::Color::LightYellow => termion::color::LightYellow.write_bg(f),
Color::Rgb(r, g, b) => termion_bg_rgb!(r, g, b), style::Color::LightMagenta => termion::color::LightMagenta.write_bg(f),
style::Color::LightCyan => termion::color::LightCyan.write_bg(f),
style::Color::White => termion::color::LightWhite.write_bg(f),
style::Color::Indexed(i) => termion::color::AnsiValue(i).write_bg(f),
style::Color::Rgb(r, g, b) => termion::color::Rgb(r, g, b).write_bg(f),
} }
} }
} }
impl Modifier { impl fmt::Display for ModifierDiff {
pub fn termion_modifier(self) -> String { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { let remove = self.from - self.to;
Modifier::Blink => termion_modifier!(Blink), if remove.contains(style::Modifier::REVERSED) {
Modifier::Bold => termion_modifier!(Bold), write!(f, "{}", termion::style::NoInvert)?;
Modifier::CrossedOut => termion_modifier!(CrossedOut),
Modifier::Faint => termion_modifier!(Faint),
Modifier::Framed => termion_modifier!(Framed),
Modifier::Invert => termion_modifier!(Invert),
Modifier::Italic => termion_modifier!(Italic),
Modifier::NoBlink => termion_modifier!(NoBlink),
Modifier::NoBold => termion_modifier!(NoBold),
Modifier::NoCrossedOut => termion_modifier!(NoCrossedOut),
Modifier::NoFaint => termion_modifier!(NoFaint),
Modifier::NoInvert => termion_modifier!(NoInvert),
Modifier::NoItalic => termion_modifier!(NoItalic),
Modifier::NoUnderline => termion_modifier!(NoUnderline),
Modifier::Reset => termion_modifier!(Reset),
Modifier::Underline => termion_modifier!(Underline),
} }
if remove.contains(style::Modifier::BOLD) {
write!(f, "{}", termion::style::NoBold)?;
}
if remove.contains(style::Modifier::ITALIC) {
write!(f, "{}", termion::style::NoItalic)?;
}
if remove.contains(style::Modifier::UNDERLINED) {
write!(f, "{}", termion::style::NoUnderline)?;
}
if remove.contains(style::Modifier::DIM) {
write!(f, "{}", termion::style::NoFaint)?;
}
if remove.contains(style::Modifier::CROSSED_OUT) {
write!(f, "{}", termion::style::NoCrossedOut)?;
}
if remove.contains(style::Modifier::SLOW_BLINK)
|| remove.contains(style::Modifier::RAPID_BLINK)
{
write!(f, "{}", termion::style::NoBlink)?;
}
let add = self.to - self.from;
if add.contains(style::Modifier::REVERSED) {
write!(f, "{}", termion::style::Invert)?;
}
if add.contains(style::Modifier::BOLD) {
write!(f, "{}", termion::style::Bold)?;
}
if add.contains(style::Modifier::ITALIC) {
write!(f, "{}", termion::style::Italic)?;
}
if add.contains(style::Modifier::UNDERLINED) {
write!(f, "{}", termion::style::Underline)?;
}
if add.contains(style::Modifier::DIM) {
write!(f, "{}", termion::style::Faint)?;
}
if add.contains(style::Modifier::CROSSED_OUT) {
write!(f, "{}", termion::style::CrossedOut)?;
}
if add.contains(style::Modifier::SLOW_BLINK) || add.contains(style::Modifier::RAPID_BLINK) {
write!(f, "{}", termion::style::Blink)?;
}
Ok(())
} }
} }

View file

@ -88,7 +88,7 @@ impl Default for Cell {
/// style: Style { /// style: Style {
/// fg: Color::Red, /// fg: Color::Red,
/// bg: Color::White, /// bg: Color::White,
/// modifier: Modifier::Reset /// modifier: Modifier::empty()
/// }}); /// }});
/// buf.get_mut(5, 0).set_char('x'); /// buf.get_mut(5, 0).set_char('x');
/// assert_eq!(buf.get(5, 0).symbol, "x"); /// assert_eq!(buf.get(5, 0).symbol, "x");

View file

@ -1,3 +1,5 @@
use bitflags::bitflags;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Color { pub enum Color {
Reset, Reset,
@ -18,6 +20,7 @@ pub enum Color {
LightCyan, LightCyan,
White, White,
Rgb(u8, u8, u8), Rgb(u8, u8, u8),
Indexed(u8),
} }
impl Color { impl Color {
@ -33,62 +36,72 @@ impl Color {
Color::Blue => "b", Color::Blue => "b",
Color::Magenta => "m", Color::Magenta => "m",
Color::Cyan => "c", Color::Cyan => "c",
Color::Gray => "g", Color::Gray => "w",
Color::DarkGray => "G", Color::DarkGray => "B",
Color::LightRed => "R", Color::LightRed => "R",
Color::LightGreen => "G", Color::LightGreen => "G",
Color::LightYellow => "Y", Color::LightYellow => "Y",
Color::LightBlue => "B", Color::LightBlue => "B",
Color::LightMagenta => "M", Color::LightMagenta => "M",
Color::LightCyan => "C", Color::LightCyan => "C",
Color::White => "w", Color::White => "W",
Color::Indexed(_) => "i",
Color::Rgb(_, _, _) => "o", Color::Rgb(_, _, _) => "o",
} }
} }
} }
#[derive(Debug, Clone, Copy, PartialEq)] bitflags! {
pub enum Modifier { pub struct Modifier: u16 {
Blink, const BOLD = 0b0000_0000_0001;
Bold, const DIM = 0b0000_0000_0010;
CrossedOut, const ITALIC = 0b0000_0000_0100;
Faint, const UNDERLINED = 0b0000_0000_1000;
Framed, const SLOW_BLINK = 0b0000_0001_0000;
Invert, const RAPID_BLINK = 0b0000_0010_0000;
Italic, const REVERSED = 0b0000_0100_0000;
NoBlink, const HIDDEN = 0b0000_1000_0000;
NoBold, const CROSSED_OUT = 0b0001_0000_0000;
NoCrossedOut, }
NoFaint,
NoInvert,
NoItalic,
NoUnderline,
Reset,
Underline,
} }
impl Modifier { impl Modifier {
/// Returns a short code associated with the color, used for debug purpose /// Returns a short code associated with the color, used for debug purpose
/// only /// only
pub(crate) fn code(&self) -> &str { pub(crate) fn code(&self) -> String {
match self { use std::fmt::Write;
Modifier::Blink => "bl",
Modifier::Bold => "bo", let mut result = String::new();
Modifier::CrossedOut => "cr",
Modifier::Faint => "fa", if self.contains(Modifier::BOLD) {
Modifier::Framed => "fr", write!(result, "BO").unwrap();
Modifier::Invert => "in",
Modifier::Italic => "it",
Modifier::NoBlink => "BL",
Modifier::NoBold => "BO",
Modifier::NoCrossedOut => "CR",
Modifier::NoFaint => "FA",
Modifier::NoInvert => "IN",
Modifier::NoItalic => "IT",
Modifier::NoUnderline => "UN",
Modifier::Reset => "re",
Modifier::Underline => "un",
} }
if self.contains(Modifier::DIM) {
write!(result, "DI").unwrap();
}
if self.contains(Modifier::ITALIC) {
write!(result, "IT").unwrap();
}
if self.contains(Modifier::UNDERLINED) {
write!(result, "UN").unwrap();
}
if self.contains(Modifier::SLOW_BLINK) {
write!(result, "SL").unwrap();
}
if self.contains(Modifier::RAPID_BLINK) {
write!(result, "RA").unwrap();
}
if self.contains(Modifier::REVERSED) {
write!(result, "RE").unwrap();
}
if self.contains(Modifier::HIDDEN) {
write!(result, "HI").unwrap();
}
if self.contains(Modifier::CROSSED_OUT) {
write!(result, "CR").unwrap();
}
result
} }
} }
@ -104,7 +117,7 @@ impl Default for Style {
Style { Style {
fg: Color::Reset, fg: Color::Reset,
bg: Color::Reset, bg: Color::Reset,
modifier: Modifier::Reset, modifier: Modifier::empty(),
} }
} }
} }
@ -113,7 +126,7 @@ impl Style {
pub fn reset(&mut self) { pub fn reset(&mut self) {
self.fg = Color::Reset; self.fg = Color::Reset;
self.bg = Color::Reset; self.bg = Color::Reset;
self.modifier = Modifier::Reset; self.modifier = Modifier::empty();
} }
pub fn fg(mut self, color: Color) -> Style { pub fn fg(mut self, color: Color) -> Style {

View file

@ -21,7 +21,7 @@ use crate::widgets::{Block, Widget};
/// .bar_width(3) /// .bar_width(3)
/// .bar_gap(1) /// .bar_gap(1)
/// .style(Style::default().fg(Color::Yellow).bg(Color::Red)) /// .style(Style::default().fg(Color::Yellow).bg(Color::Red))
/// .value_style(Style::default().fg(Color::Red).modifier(Modifier::Bold)) /// .value_style(Style::default().fg(Color::Red).modifier(Modifier::BOLD))
/// .label_style(Style::default().fg(Color::White)) /// .label_style(Style::default().fg(Color::White))
/// .data(&[("B0", 0), ("B1", 2), ("B2", 4), ("B3", 3)]) /// .data(&[("B0", 0), ("B1", 2), ("B2", 4), ("B3", 3)])
/// .max(4); /// .max(4);

View file

@ -174,13 +174,13 @@ impl Default for ChartLayout {
/// .x_axis(Axis::default() /// .x_axis(Axis::default()
/// .title("X Axis") /// .title("X Axis")
/// .title_style(Style::default().fg(Color::Red)) /// .title_style(Style::default().fg(Color::Red))
/// .style(Style::default().fg(Color::Gray)) /// .style(Style::default().fg(Color::White))
/// .bounds([0.0, 10.0]) /// .bounds([0.0, 10.0])
/// .labels(&["0.0", "5.0", "10.0"])) /// .labels(&["0.0", "5.0", "10.0"]))
/// .y_axis(Axis::default() /// .y_axis(Axis::default()
/// .title("Y Axis") /// .title("Y Axis")
/// .title_style(Style::default().fg(Color::Red)) /// .title_style(Style::default().fg(Color::Red))
/// .style(Style::default().fg(Color::Gray)) /// .style(Style::default().fg(Color::White))
/// .bounds([0.0, 10.0]) /// .bounds([0.0, 10.0])
/// .labels(&["0.0", "5.0", "10.0"])) /// .labels(&["0.0", "5.0", "10.0"]))
/// .datasets(&[Dataset::default() /// .datasets(&[Dataset::default()

View file

@ -15,7 +15,7 @@ use crate::widgets::{Block, Widget};
/// # fn main() { /// # fn main() {
/// Gauge::default() /// Gauge::default()
/// .block(Block::default().borders(Borders::ALL).title("Progress")) /// .block(Block::default().borders(Borders::ALL).title("Progress"))
/// .style(Style::default().fg(Color::White).bg(Color::Black).modifier(Modifier::Italic)) /// .style(Style::default().fg(Color::White).bg(Color::Black).modifier(Modifier::ITALIC))
/// .percent(20); /// .percent(20);
/// # } /// # }
/// ``` /// ```

View file

@ -125,7 +125,7 @@ where
/// .items(&["Item 1", "Item 2", "Item 3"]) /// .items(&["Item 1", "Item 2", "Item 3"])
/// .select(Some(1)) /// .select(Some(1))
/// .style(Style::default().fg(Color::White)) /// .style(Style::default().fg(Color::White))
/// .highlight_style(Style::default().modifier(Modifier::Italic)) /// .highlight_style(Style::default().modifier(Modifier::ITALIC))
/// .highlight_symbol(">>"); /// .highlight_symbol(">>");
/// # } /// # }
/// ``` /// ```