Skip to content

Commit

Permalink
fix(backend): add feature flag for underline-color (#570)
Browse files Browse the repository at this point in the history
Windows 7 doesn't support the underline color attribute, so we need to
make it optional. This commit adds a feature flag for the underline
color attribute - it is enabled by default, but can be disabled by
passing `--no-default-features` to cargo.

We could specically check for Windows 7 and disable the feature flag
automatically, but I think it's better for this check to be done by the
crossterm crate, since it's the one that actually knows about the
underlying terminal.

To disable the feature flag in an application that supports Windows 7,
add the following to your Cargo.toml:

```toml
ratatui = { version = "0.24.0", default-features = false, features = ["crossterm"] }
```

Fixes #555
  • Loading branch information
joshka authored Oct 18, 2023
1 parent b61f65b commit 8d507c4
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 39 deletions.
26 changes: 19 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,8 @@ rust-version = "1.67.0"
[badges]

[dependencies]
#! The crate provides a set of optional features that can be enabled in your `cargo.toml` file.
#!
#! Generally an application will only use one backend, so you should only enable one of the following features:
## enables the [`CrosstermBackend`] backend and adds a dependency on the [Crossterm crate].
crossterm = { version = "0.27", optional = true }
## enables the [`TermionBackend`] backend and adds a dependency on the [Termion crate].
termion = { version = "2.0", optional = true }
## enables the [`TermwizBackend`] backend and adds a dependency on the [Termwiz crate].
termwiz = { version = "0.20.0", optional = true }

serde = { version = "1", optional = true, features = ["derive"] }
Expand Down Expand Up @@ -60,7 +54,20 @@ palette = "0.7.3"
pretty_assertions = "1.4.0"

[features]
default = ["crossterm"]
#! The crate provides a set of optional features that can be enabled in your `cargo.toml` file.
#!
## By default, we enable the crossterm backend as this is a reasonable choice for most applications
## as it is supported on Linux/Mac/Windows systems. We also enable the `underline-color` feature
## which allows you to set the underline color of text.
default = ["crossterm", "underline-color"]
#! Generally an application will only use one backend, so you should only enable one of the following features:
## enables the [`CrosstermBackend`] backend and adds a dependency on the [Crossterm crate].
crossterm = ["dep:crossterm"]
## enables the [`TermionBackend`] backend and adds a dependency on the [Termion crate].
termion = ["dep:termion"]
## enables the [`TermwizBackend`] backend and adds a dependency on the [Termwiz crate].
termwiz = ["dep:termwiz"]

#! The following optional features are available for all backends:
## enables serialization and deserialization of style and color types using the [Serde crate].
## This is useful if you want to save themes to a file.
Expand All @@ -77,6 +84,11 @@ all-widgets = ["widget-calendar"]
## enables the [`calendar`] widget module and adds a dependency on the [Time crate].
widget-calendar = ["dep:time"]

#! Underline color is only supported by the [`CrosstermBackend`] backend, and is not supported
#! on Windows 7.
## enables the backend code that sets the underline color.
underline-color = ["dep:crossterm"]

[package.metadata.docs.rs]
all-features = true
# see https://doc.rust-lang.org/nightly/rustdoc/scraped-examples.html
Expand Down
6 changes: 3 additions & 3 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ ALL_FEATURES = "all-widgets,macros,serde"

# Windows does not support building termion, so this avoids the build failure by providing two
# sets of flags, one for Windows and one for other platforms.
# Windows: --features=all-widgets,macros,serde,crossterm,termwiz
# Other: --all-features
ALL_FEATURES_FLAG = { source = "${CARGO_MAKE_RUST_TARGET_OS}", default_value = "--all-features", mapping = { "windows" = "--features=all-widgets,macros,serde,crossterm,termwiz" } }
# Windows: --features=all-widgets,macros,serde,crossterm,termwiz,underline-color
# Other: --features=all-widgets,macros,serde,crossterm,termion,termwiz,underline-color
ALL_FEATURES_FLAG = { source = "${CARGO_MAKE_RUST_TARGET_OS}", default_value = "--features=all-widgets,macros,serde,crossterm,termion,termwiz", mapping = { "windows" = "--features=all-widgets,macros,serde,crossterm,termwiz" } }

[tasks.default]
alias = "ci"
Expand Down
20 changes: 16 additions & 4 deletions src/backend/crossterm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
//! [Crossterm]: https://crates.io/crates/crossterm
use std::io::{self, Write};

#[cfg(feature = "underline-color")]
use crossterm::style::SetUnderlineColor;
use crossterm::{
cursor::{Hide, MoveTo, Show},
execute, queue,
style::{
Attribute as CAttribute, Color as CColor, Print, SetAttribute, SetBackgroundColor,
SetForegroundColor, SetUnderlineColor,
SetForegroundColor,
},
terminal::{self, Clear},
};
Expand Down Expand Up @@ -124,6 +126,7 @@ where
{
let mut fg = Color::Reset;
let mut bg = Color::Reset;
#[cfg(feature = "underline-color")]
let mut underline_color = Color::Reset;
let mut modifier = Modifier::empty();
let mut last_pos: Option<(u16, u16)> = None;
Expand Down Expand Up @@ -151,6 +154,7 @@ where
queue!(self.writer, SetBackgroundColor(color))?;
bg = cell.bg;
}
#[cfg(feature = "underline-color")]
if cell.underline_color != underline_color {
let color = CColor::from(cell.underline_color);
queue!(self.writer, SetUnderlineColor(color))?;
Expand All @@ -160,13 +164,21 @@ where
queue!(self.writer, Print(&cell.symbol))?;
}

queue!(
#[cfg(feature = "underline-color")]
return queue!(
self.writer,
SetForegroundColor(CColor::Reset),
SetBackgroundColor(CColor::Reset),
SetUnderlineColor(CColor::Reset),
SetAttribute(CAttribute::Reset)
)
SetAttribute(CAttribute::Reset),
);
#[cfg(not(feature = "underline-color"))]
return queue!(
self.writer,
SetForegroundColor(CColor::Reset),
SetBackgroundColor(CColor::Reset),
SetAttribute(CAttribute::Reset),
);
}

fn hide_cursor(&mut self) -> io::Result<()> {
Expand Down
26 changes: 13 additions & 13 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct Cell {
pub symbol: String,
pub fg: Color,
pub bg: Color,
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
pub underline_color: Color,
pub modifier: Modifier,
pub skip: bool,
Expand Down Expand Up @@ -55,7 +55,7 @@ impl Cell {
if let Some(c) = style.bg {
self.bg = c;
}
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
if let Some(c) = style.underline_color {
self.underline_color = c;
}
Expand All @@ -64,7 +64,7 @@ impl Cell {
self
}

#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
pub fn style(&self) -> Style {
Style::default()
.fg(self.fg)
Expand All @@ -73,7 +73,7 @@ impl Cell {
.add_modifier(self.modifier)
}

#[cfg(not(feature = "crossterm"))]
#[cfg(not(feature = "underline-color"))]
pub fn style(&self) -> Style {
Style::default()
.fg(self.fg)
Expand All @@ -95,7 +95,7 @@ impl Cell {
self.symbol.push(' ');
self.fg = Color::Reset;
self.bg = Color::Reset;
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
{
self.underline_color = Color::Reset;
}
Expand All @@ -110,7 +110,7 @@ impl Default for Cell {
symbol: " ".into(),
fg: Color::Reset,
bg: Color::Reset,
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
underline_color: Color::Reset,
modifier: Modifier::empty(),
skip: false,
Expand Down Expand Up @@ -138,7 +138,7 @@ impl Default for Cell {
/// symbol: String::from("r"),
/// fg: Color::Red,
/// bg: Color::White,
/// #[cfg(feature = "crossterm")]
/// #[cfg(feature = "underline-color")]
/// underline_color: Color::Reset,
/// modifier: Modifier::empty(),
/// skip: false
Expand Down Expand Up @@ -560,15 +560,15 @@ impl Debug for Buffer {
overwritten.push((x, &c.symbol));
}
skip = std::cmp::max(skip, c.symbol.width()).saturating_sub(1);
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
{
let style = (c.fg, c.bg, c.underline_color, c.modifier);
if last_style != Some(style) {
last_style = Some(style);
styles.push((x, y, c.fg, c.bg, c.underline_color, c.modifier));
}
}
#[cfg(not(feature = "crossterm"))]
#[cfg(not(feature = "underline-color"))]
{
let style = (c.fg, c.bg, c.modifier);
if last_style != Some(style) {
Expand All @@ -586,12 +586,12 @@ impl Debug for Buffer {
}
f.write_str(" ],\n styles: [\n")?;
for s in styles {
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
f.write_fmt(format_args!(
" x: {}, y: {}, fg: {:?}, bg: {:?}, underline: {:?}, modifier: {:?},\n",
s.0, s.1, s.2, s.3, s.4, s.5
))?;
#[cfg(not(feature = "crossterm"))]
#[cfg(not(feature = "underline-color"))]
f.write_fmt(format_args!(
" x: {}, y: {}, fg: {:?}, bg: {:?}, modifier: {:?},\n",
s.0, s.1, s.2, s.3, s.4
Expand Down Expand Up @@ -625,7 +625,7 @@ mod tests {
.bg(Color::Yellow)
.add_modifier(Modifier::BOLD),
);
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
assert_eq!(
format!("{buf:?}"),
indoc::indoc!(
Expand All @@ -643,7 +643,7 @@ mod tests {
}"
)
);
#[cfg(not(feature = "crossterm"))]
#[cfg(not(feature = "underline-color"))]
assert_eq!(
format!("{buf:?}"),
indoc::indoc!(
Expand Down
23 changes: 13 additions & 10 deletions src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ impl fmt::Debug for Modifier {
/// let styles = [
/// Style::default().fg(Color::Blue).add_modifier(Modifier::BOLD | Modifier::ITALIC),
/// Style::default().bg(Color::Red).add_modifier(Modifier::UNDERLINED),
/// #[cfg(feature = "crossterm")]
/// #[cfg(feature = "underline-color")]
/// Style::default().underline_color(Color::Green),
/// Style::default().fg(Color::Yellow).remove_modifier(Modifier::ITALIC),
/// ];
Expand All @@ -152,7 +152,7 @@ impl fmt::Debug for Modifier {
/// Style {
/// fg: Some(Color::Yellow),
/// bg: Some(Color::Red),
/// #[cfg(feature = "crossterm")]
/// #[cfg(feature = "underline-color")]
/// underline_color: Some(Color::Green),
/// add_modifier: Modifier::BOLD | Modifier::UNDERLINED,
/// sub_modifier: Modifier::empty(),
Expand All @@ -179,7 +179,7 @@ impl fmt::Debug for Modifier {
/// Style {
/// fg: Some(Color::Yellow),
/// bg: Some(Color::Reset),
/// #[cfg(feature = "crossterm")]
/// #[cfg(feature = "underline-color")]
/// underline_color: Some(Color::Reset),
/// add_modifier: Modifier::empty(),
/// sub_modifier: Modifier::empty(),
Expand All @@ -192,7 +192,7 @@ impl fmt::Debug for Modifier {
pub struct Style {
pub fg: Option<Color>,
pub bg: Option<Color>,
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
pub underline_color: Option<Color>,
pub add_modifier: Modifier,
pub sub_modifier: Modifier,
Expand Down Expand Up @@ -220,7 +220,7 @@ impl Style {
Style {
fg: None,
bg: None,
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
underline_color: None,
add_modifier: Modifier::empty(),
sub_modifier: Modifier::empty(),
Expand All @@ -232,7 +232,7 @@ impl Style {
Style {
fg: Some(Color::Reset),
bg: Some(Color::Reset),
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
underline_color: Some(Color::Reset),
add_modifier: Modifier::empty(),
sub_modifier: Modifier::all(),
Expand Down Expand Up @@ -272,9 +272,12 @@ impl Style {
/// Changes the underline color. The text must be underlined with a modifier for this to work.
///
/// This uses a non-standard ANSI escape sequence. It is supported by most terminal emulators,
/// but is only implemented in the crossterm backend.
/// but is only implemented in the crossterm backend and enabled by the `underline-color`
/// feature flag.
///
/// See [Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters) code `58` and `59` for more information.
/// See
/// [Wikipedia](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters)
/// code `58` and `59` for more information.
///
/// ## Examples
///
Expand All @@ -284,7 +287,7 @@ impl Style {
/// let diff = Style::default().underline_color(Color::Red).add_modifier(Modifier::UNDERLINED);
/// assert_eq!(style.patch(diff), Style::default().underline_color(Color::Red).add_modifier(Modifier::UNDERLINED));
/// ```
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
pub const fn underline_color(mut self, color: Color) -> Style {
self.underline_color = Some(color);
self
Expand Down Expand Up @@ -347,7 +350,7 @@ impl Style {
self.fg = other.fg.or(self.fg);
self.bg = other.bg.or(self.bg);

#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
{
self.underline_color = other.underline_color.or(self.underline_color);
}
Expand Down
4 changes: 2 additions & 2 deletions src/widgets/gauge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ impl<'a> Widget for LineGauge<'a> {
.set_style(Style {
fg: self.gauge_style.fg,
bg: None,
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
underline_color: self.gauge_style.underline_color,
add_modifier: self.gauge_style.add_modifier,
sub_modifier: self.gauge_style.sub_modifier,
Expand All @@ -378,7 +378,7 @@ impl<'a> Widget for LineGauge<'a> {
.set_style(Style {
fg: self.gauge_style.bg,
bg: None,
#[cfg(feature = "crossterm")]
#[cfg(feature = "underline-color")]
underline_color: self.gauge_style.underline_color,
add_modifier: self.gauge_style.add_modifier,
sub_modifier: self.gauge_style.sub_modifier,
Expand Down

0 comments on commit 8d507c4

Please sign in to comment.