chore(backend): change From<T> impls to new backend specific IntoBackend and FromBackend traits (#1464)

Adds two traits `IntoCrossterm` and `FromCrossterm` for converting
between ratatui and crossterm types. This is necessary in order to avoid
the orphan rule when implementing `From` for crossterm types once the
crossterm types are moved to a separate crate.

Similarly Termwiz and Termwiz gain FromTermion, IntoTermion, FromTermwiz
and IntoTermwiz traits.

BREAKING CHANGE: The `From` and `Into` impls for backend types are now
replaced
with specific backend traits.

```diff
+ use ratatui::backend::{FromCrossterm, IntoCrossterm};

let crossterm_color = crossterm::style::Color::Black;
- let ratatui_color = crossterm_color.into();
- let ratatui_color = ratatui::style::Color::from(crossterm_color);
+ let ratatui_color = ratatui::style::Color::from_crossterm(crossterm_color);
- let crossterm_color = ratatui_color.into();
- let crossterm_color = crossterm::style::Color::from(ratatui_color);
+ let crossterm_color = ratatui_color.into_crossterm();

let crossterm_attribute = crossterm::style::types::Attribute::Bold;
- let ratatui_modifier = crossterm_attribute.into();
- let ratatui_modifier = ratatui::style::Modifier::from(crossterm_attribute);
+ let ratatui_modifier = ratatui::style::Modifier::from_crossterm(crossterm_attribute);
- let crossterm_attribute = ratatui_modifier.into();
- let crossterm_attribute = crossterm::style::types::Attribute::from(ratatui_modifier);
+ let crossterm_attribute = ratatui_modifier.into_crossterm();
```

Similar conversions for `ContentStyle` -> `Style` and `Attributes` ->
`Modifier` exist for Crossterm,
and all the Termion and Termwiz types.

---------

Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
This commit is contained in:
Josh McKinney
2024-11-03 13:43:24 -08:00
committed by GitHub
parent fcde9cb9c3
commit abe2f27328
6 changed files with 756 additions and 522 deletions

View File

@@ -72,6 +72,41 @@ This is a quick summary of the sections below:
- MSRV is now 1.63.0
- `List` no longer ignores empty strings
## Unreleased (0.30.0)
### The `From` impls for backend types are now replaced with more specific traits [#1464]
[#1464]: https://github.com/ratatui/ratatui/pull/1464
Crossterm gains `ratatui::backend::crossterm::{FromCrossterm, IntoCrossterm}`
Termwiz gains `ratatui::backend::termwiz::{FromTermwiz, IntoTermwiz}`
This is necessary in order to avoid the orphan rule when implementing `From` for crossterm types
once the crossterm types are moved to a separate crate.
```diff
+ use ratatui::backend::crossterm::{FromCrossterm, IntoCrossterm};
let crossterm_color = crossterm::style::Color::Black;
- let ratatui_color = crossterm_color.into();
- let ratatui_color = ratatui::style::Color::from(crossterm_color);
+ let ratatui_color = ratatui::style::Color::from_crossterm(crossterm_color);
- let crossterm_color = ratatui_color.into();
- let crossterm_color = crossterm::style::Color::from(ratatui_color);
+ let crossterm_color = ratatui_color.into_crossterm();
let crossterm_attribute = crossterm::style::types::Attribute::Bold;
- let ratatui_modifier = crossterm_attribute.into();
- let ratatui_modifier = ratatui::style::Modifier::from(crossterm_attribute);
+ let ratatui_modifier = ratatui::style::Modifier::from_crossterm(crossterm_attribute);
- let crossterm_attribute = ratatui_modifier.into();
- let crossterm_attribute = crossterm::style::types::Attribute::from(ratatui_modifier);
+ let crossterm_attribute = ratatui_modifier.into_crossterm();
```
Similar conversions for `ContentStyle` -> `Style` and `Attributes` -> `Modifier` exist for
Crossterm and the various Termion and Termwiz types as well.
## [v0.29.0](https://github.com/ratatui/ratatui/releases/tag/v0.29.0)
### `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const ([#1326])

View File

@@ -109,20 +109,20 @@ use crate::{
layout::{Position, Size},
};
#[cfg(all(not(windows), feature = "termion"))]
mod termion;
#[cfg(all(not(windows), feature = "termion"))]
pub use self::termion::TermionBackend;
#[cfg(feature = "crossterm")]
mod crossterm;
#[cfg(feature = "crossterm")]
pub use self::crossterm::CrosstermBackend;
pub use self::crossterm::{CrosstermBackend, FromCrossterm, IntoCrossterm};
#[cfg(all(not(windows), feature = "termion"))]
mod termion;
#[cfg(all(not(windows), feature = "termion"))]
pub use self::termion::{FromTermion, IntoTermion, TermionBackend};
#[cfg(feature = "termwiz")]
mod termwiz;
#[cfg(feature = "termwiz")]
pub use self::termwiz::TermwizBackend;
pub use self::termwiz::{FromTermwiz, IntoTermwiz, TermwizBackend};
mod test;
pub use self::test::TestBackend;

View File

@@ -6,19 +6,20 @@ use std::io::{self, Write};
#[cfg(feature = "underline-color")]
use crossterm::style::SetUnderlineColor;
use crossterm::{
cursor::{Hide, MoveTo, Show},
execute, queue,
style::{
Attribute as CrosstermAttribute, Attributes as CrosstermAttributes,
Color as CrosstermColor, Colors as CrosstermColors, ContentStyle, Print, SetAttribute,
SetBackgroundColor, SetColors, SetForegroundColor,
},
terminal::{self, Clear},
};
use crate::{
backend::{Backend, ClearType, WindowSize},
buffer::Cell,
crossterm::{
cursor::{Hide, MoveTo, Show},
execute, queue,
style::{
Attribute as CAttribute, Attributes as CAttributes, Color as CColor, Colors,
ContentStyle, Print, SetAttribute, SetBackgroundColor, SetColors, SetForegroundColor,
},
terminal::{self, Clear},
},
layout::{Position, Size},
style::{Color, Modifier, Style},
};
@@ -175,14 +176,17 @@ where
if cell.fg != fg || cell.bg != bg {
queue!(
self.writer,
SetColors(Colors::new(cell.fg.into(), cell.bg.into()))
SetColors(CrosstermColors::new(
cell.fg.into_crossterm(),
cell.bg.into_crossterm(),
))
)?;
fg = cell.fg;
bg = cell.bg;
}
#[cfg(feature = "underline-color")]
if cell.underline_color != underline_color {
let color = CColor::from(cell.underline_color);
let color = cell.underline_color.into_crossterm();
queue!(self.writer, SetUnderlineColor(color))?;
underline_color = cell.underline_color;
}
@@ -193,17 +197,17 @@ where
#[cfg(feature = "underline-color")]
return queue!(
self.writer,
SetForegroundColor(CColor::Reset),
SetBackgroundColor(CColor::Reset),
SetUnderlineColor(CColor::Reset),
SetAttribute(CAttribute::Reset),
SetForegroundColor(CrosstermColor::Reset),
SetBackgroundColor(CrosstermColor::Reset),
SetUnderlineColor(CrosstermColor::Reset),
SetAttribute(CrosstermAttribute::Reset),
);
#[cfg(not(feature = "underline-color"))]
return queue!(
self.writer,
SetForegroundColor(CColor::Reset),
SetBackgroundColor(CColor::Reset),
SetAttribute(CAttribute::Reset),
SetForegroundColor(CrosstermColor::Reset),
SetBackgroundColor(CrosstermColor::Reset),
SetAttribute(CrosstermAttribute::Reset),
);
}
@@ -302,54 +306,72 @@ where
}
}
impl From<Color> for CColor {
fn from(color: Color) -> Self {
match color {
Color::Reset => Self::Reset,
Color::Black => Self::Black,
Color::Red => Self::DarkRed,
Color::Green => Self::DarkGreen,
Color::Yellow => Self::DarkYellow,
Color::Blue => Self::DarkBlue,
Color::Magenta => Self::DarkMagenta,
Color::Cyan => Self::DarkCyan,
Color::Gray => Self::Grey,
Color::DarkGray => Self::DarkGrey,
Color::LightRed => Self::Red,
Color::LightGreen => Self::Green,
Color::LightBlue => Self::Blue,
Color::LightYellow => Self::Yellow,
Color::LightMagenta => Self::Magenta,
Color::LightCyan => Self::Cyan,
Color::White => Self::White,
Color::Indexed(i) => Self::AnsiValue(i),
Color::Rgb(r, g, b) => Self::Rgb { r, g, b },
/// A trait for converting a Ratatui type to a Crossterm type.
///
/// This trait is needed for avoiding the orphan rule when implementing `From` for crossterm types
/// once these are moved to a separate crate.
pub trait IntoCrossterm<C> {
/// Converts the ratatui type to a crossterm type.
fn into_crossterm(self) -> C;
}
/// A trait for converting a Crossterm type to a Ratatui type.
///
/// This trait is needed for avoiding the orphan rule when implementing `From` for crossterm types
/// once these are moved to a separate crate.
pub trait FromCrossterm<C> {
/// Converts the crossterm type to a ratatui type.
fn from_crossterm(value: C) -> Self;
}
impl IntoCrossterm<CrosstermColor> for Color {
fn into_crossterm(self) -> CrosstermColor {
match self {
Self::Reset => CrosstermColor::Reset,
Self::Black => CrosstermColor::Black,
Self::Red => CrosstermColor::DarkRed,
Self::Green => CrosstermColor::DarkGreen,
Self::Yellow => CrosstermColor::DarkYellow,
Self::Blue => CrosstermColor::DarkBlue,
Self::Magenta => CrosstermColor::DarkMagenta,
Self::Cyan => CrosstermColor::DarkCyan,
Self::Gray => CrosstermColor::Grey,
Self::DarkGray => CrosstermColor::DarkGrey,
Self::LightRed => CrosstermColor::Red,
Self::LightGreen => CrosstermColor::Green,
Self::LightBlue => CrosstermColor::Blue,
Self::LightYellow => CrosstermColor::Yellow,
Self::LightMagenta => CrosstermColor::Magenta,
Self::LightCyan => CrosstermColor::Cyan,
Self::White => CrosstermColor::White,
Self::Indexed(i) => CrosstermColor::AnsiValue(i),
Self::Rgb(r, g, b) => CrosstermColor::Rgb { r, g, b },
}
}
}
impl From<CColor> for Color {
fn from(value: CColor) -> Self {
impl FromCrossterm<CrosstermColor> for Color {
fn from_crossterm(value: CrosstermColor) -> Self {
match value {
CColor::Reset => Self::Reset,
CColor::Black => Self::Black,
CColor::DarkRed => Self::Red,
CColor::DarkGreen => Self::Green,
CColor::DarkYellow => Self::Yellow,
CColor::DarkBlue => Self::Blue,
CColor::DarkMagenta => Self::Magenta,
CColor::DarkCyan => Self::Cyan,
CColor::Grey => Self::Gray,
CColor::DarkGrey => Self::DarkGray,
CColor::Red => Self::LightRed,
CColor::Green => Self::LightGreen,
CColor::Blue => Self::LightBlue,
CColor::Yellow => Self::LightYellow,
CColor::Magenta => Self::LightMagenta,
CColor::Cyan => Self::LightCyan,
CColor::White => Self::White,
CColor::Rgb { r, g, b } => Self::Rgb(r, g, b),
CColor::AnsiValue(v) => Self::Indexed(v),
CrosstermColor::Reset => Self::Reset,
CrosstermColor::Black => Self::Black,
CrosstermColor::DarkRed => Self::Red,
CrosstermColor::DarkGreen => Self::Green,
CrosstermColor::DarkYellow => Self::Yellow,
CrosstermColor::DarkBlue => Self::Blue,
CrosstermColor::DarkMagenta => Self::Magenta,
CrosstermColor::DarkCyan => Self::Cyan,
CrosstermColor::Grey => Self::Gray,
CrosstermColor::DarkGrey => Self::DarkGray,
CrosstermColor::Red => Self::LightRed,
CrosstermColor::Green => Self::LightGreen,
CrosstermColor::Blue => Self::LightBlue,
CrosstermColor::Yellow => Self::LightYellow,
CrosstermColor::Magenta => Self::LightMagenta,
CrosstermColor::Cyan => Self::LightCyan,
CrosstermColor::White => Self::White,
CrosstermColor::Rgb { r, g, b } => Self::Rgb(r, g, b),
CrosstermColor::AnsiValue(v) => Self::Indexed(v),
}
}
}
@@ -370,142 +392,138 @@ impl ModifierDiff {
//use crossterm::Attribute;
let removed = self.from - self.to;
if removed.contains(Modifier::REVERSED) {
queue!(w, SetAttribute(CAttribute::NoReverse))?;
queue!(w, SetAttribute(CrosstermAttribute::NoReverse))?;
}
if removed.contains(Modifier::BOLD) {
queue!(w, SetAttribute(CAttribute::NormalIntensity))?;
queue!(w, SetAttribute(CrosstermAttribute::NormalIntensity))?;
if self.to.contains(Modifier::DIM) {
queue!(w, SetAttribute(CAttribute::Dim))?;
queue!(w, SetAttribute(CrosstermAttribute::Dim))?;
}
}
if removed.contains(Modifier::ITALIC) {
queue!(w, SetAttribute(CAttribute::NoItalic))?;
queue!(w, SetAttribute(CrosstermAttribute::NoItalic))?;
}
if removed.contains(Modifier::UNDERLINED) {
queue!(w, SetAttribute(CAttribute::NoUnderline))?;
queue!(w, SetAttribute(CrosstermAttribute::NoUnderline))?;
}
if removed.contains(Modifier::DIM) {
queue!(w, SetAttribute(CAttribute::NormalIntensity))?;
queue!(w, SetAttribute(CrosstermAttribute::NormalIntensity))?;
}
if removed.contains(Modifier::CROSSED_OUT) {
queue!(w, SetAttribute(CAttribute::NotCrossedOut))?;
queue!(w, SetAttribute(CrosstermAttribute::NotCrossedOut))?;
}
if removed.contains(Modifier::SLOW_BLINK) || removed.contains(Modifier::RAPID_BLINK) {
queue!(w, SetAttribute(CAttribute::NoBlink))?;
queue!(w, SetAttribute(CrosstermAttribute::NoBlink))?;
}
let added = self.to - self.from;
if added.contains(Modifier::REVERSED) {
queue!(w, SetAttribute(CAttribute::Reverse))?;
queue!(w, SetAttribute(CrosstermAttribute::Reverse))?;
}
if added.contains(Modifier::BOLD) {
queue!(w, SetAttribute(CAttribute::Bold))?;
queue!(w, SetAttribute(CrosstermAttribute::Bold))?;
}
if added.contains(Modifier::ITALIC) {
queue!(w, SetAttribute(CAttribute::Italic))?;
queue!(w, SetAttribute(CrosstermAttribute::Italic))?;
}
if added.contains(Modifier::UNDERLINED) {
queue!(w, SetAttribute(CAttribute::Underlined))?;
queue!(w, SetAttribute(CrosstermAttribute::Underlined))?;
}
if added.contains(Modifier::DIM) {
queue!(w, SetAttribute(CAttribute::Dim))?;
queue!(w, SetAttribute(CrosstermAttribute::Dim))?;
}
if added.contains(Modifier::CROSSED_OUT) {
queue!(w, SetAttribute(CAttribute::CrossedOut))?;
queue!(w, SetAttribute(CrosstermAttribute::CrossedOut))?;
}
if added.contains(Modifier::SLOW_BLINK) {
queue!(w, SetAttribute(CAttribute::SlowBlink))?;
queue!(w, SetAttribute(CrosstermAttribute::SlowBlink))?;
}
if added.contains(Modifier::RAPID_BLINK) {
queue!(w, SetAttribute(CAttribute::RapidBlink))?;
queue!(w, SetAttribute(CrosstermAttribute::RapidBlink))?;
}
Ok(())
}
}
impl From<CAttribute> for Modifier {
fn from(value: CAttribute) -> Self {
// `Attribute*s*` (note the *s*) contains multiple `Attribute`
// We convert `Attribute` to `Attribute*s*` (containing only 1 value) to avoid implementing
// the conversion again
Self::from(CAttributes::from(value))
impl FromCrossterm<CrosstermAttribute> for Modifier {
fn from_crossterm(value: CrosstermAttribute) -> Self {
// `Attribute*s*` (note the *s*) contains multiple `Attribute` We convert `Attribute` to
// `Attribute*s*` (containing only 1 value) to avoid implementing the conversion again
Self::from_crossterm(CrosstermAttributes::from(value))
}
}
impl From<CAttributes> for Modifier {
fn from(value: CAttributes) -> Self {
impl FromCrossterm<CrosstermAttributes> for Modifier {
fn from_crossterm(value: CrosstermAttributes) -> Self {
let mut res = Self::empty();
if value.has(CAttribute::Bold) {
if value.has(CrosstermAttribute::Bold) {
res |= Self::BOLD;
}
if value.has(CAttribute::Dim) {
if value.has(CrosstermAttribute::Dim) {
res |= Self::DIM;
}
if value.has(CAttribute::Italic) {
if value.has(CrosstermAttribute::Italic) {
res |= Self::ITALIC;
}
if value.has(CAttribute::Underlined)
|| value.has(CAttribute::DoubleUnderlined)
|| value.has(CAttribute::Undercurled)
|| value.has(CAttribute::Underdotted)
|| value.has(CAttribute::Underdashed)
if value.has(CrosstermAttribute::Underlined)
|| value.has(CrosstermAttribute::DoubleUnderlined)
|| value.has(CrosstermAttribute::Undercurled)
|| value.has(CrosstermAttribute::Underdotted)
|| value.has(CrosstermAttribute::Underdashed)
{
res |= Self::UNDERLINED;
}
if value.has(CAttribute::SlowBlink) {
if value.has(CrosstermAttribute::SlowBlink) {
res |= Self::SLOW_BLINK;
}
if value.has(CAttribute::RapidBlink) {
if value.has(CrosstermAttribute::RapidBlink) {
res |= Self::RAPID_BLINK;
}
if value.has(CAttribute::Reverse) {
if value.has(CrosstermAttribute::Reverse) {
res |= Self::REVERSED;
}
if value.has(CAttribute::Hidden) {
if value.has(CrosstermAttribute::Hidden) {
res |= Self::HIDDEN;
}
if value.has(CAttribute::CrossedOut) {
if value.has(CrosstermAttribute::CrossedOut) {
res |= Self::CROSSED_OUT;
}
res
}
}
impl From<ContentStyle> for Style {
fn from(value: ContentStyle) -> Self {
impl FromCrossterm<ContentStyle> for Style {
fn from_crossterm(value: ContentStyle) -> Self {
let mut sub_modifier = Modifier::empty();
if value.attributes.has(CAttribute::NoBold) {
if value.attributes.has(CrosstermAttribute::NoBold) {
sub_modifier |= Modifier::BOLD;
}
if value.attributes.has(CAttribute::NoItalic) {
if value.attributes.has(CrosstermAttribute::NoItalic) {
sub_modifier |= Modifier::ITALIC;
}
if value.attributes.has(CAttribute::NotCrossedOut) {
if value.attributes.has(CrosstermAttribute::NotCrossedOut) {
sub_modifier |= Modifier::CROSSED_OUT;
}
if value.attributes.has(CAttribute::NoUnderline) {
if value.attributes.has(CrosstermAttribute::NoUnderline) {
sub_modifier |= Modifier::UNDERLINED;
}
if value.attributes.has(CAttribute::NoHidden) {
if value.attributes.has(CrosstermAttribute::NoHidden) {
sub_modifier |= Modifier::HIDDEN;
}
if value.attributes.has(CAttribute::NoBlink) {
if value.attributes.has(CrosstermAttribute::NoBlink) {
sub_modifier |= Modifier::RAPID_BLINK | Modifier::SLOW_BLINK;
}
if value.attributes.has(CAttribute::NoReverse) {
if value.attributes.has(CrosstermAttribute::NoReverse) {
sub_modifier |= Modifier::REVERSED;
}
Self {
fg: value.foreground_color.map(Into::into),
bg: value.background_color.map(Into::into),
fg: value.foreground_color.map(FromCrossterm::from_crossterm),
bg: value.background_color.map(FromCrossterm::from_crossterm),
#[cfg(feature = "underline-color")]
underline_color: value.underline_color.map(Into::into),
add_modifier: value.attributes.into(),
underline_color: value.underline_color.map(FromCrossterm::from_crossterm),
add_modifier: Modifier::from_crossterm(value.attributes),
sub_modifier,
}
}
@@ -609,212 +627,174 @@ impl crate::crossterm::Command for ScrollDownInRegion {
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[test]
fn from_crossterm_color() {
assert_eq!(Color::from(CColor::Reset), Color::Reset);
assert_eq!(Color::from(CColor::Black), Color::Black);
assert_eq!(Color::from(CColor::DarkGrey), Color::DarkGray);
assert_eq!(Color::from(CColor::Red), Color::LightRed);
assert_eq!(Color::from(CColor::DarkRed), Color::Red);
assert_eq!(Color::from(CColor::Green), Color::LightGreen);
assert_eq!(Color::from(CColor::DarkGreen), Color::Green);
assert_eq!(Color::from(CColor::Yellow), Color::LightYellow);
assert_eq!(Color::from(CColor::DarkYellow), Color::Yellow);
assert_eq!(Color::from(CColor::Blue), Color::LightBlue);
assert_eq!(Color::from(CColor::DarkBlue), Color::Blue);
assert_eq!(Color::from(CColor::Magenta), Color::LightMagenta);
assert_eq!(Color::from(CColor::DarkMagenta), Color::Magenta);
assert_eq!(Color::from(CColor::Cyan), Color::LightCyan);
assert_eq!(Color::from(CColor::DarkCyan), Color::Cyan);
assert_eq!(Color::from(CColor::White), Color::White);
assert_eq!(Color::from(CColor::Grey), Color::Gray);
assert_eq!(
Color::from(CColor::Rgb { r: 0, g: 0, b: 0 }),
Color::Rgb(0, 0, 0)
);
assert_eq!(
Color::from(CColor::Rgb {
r: 10,
g: 20,
b: 30
}),
Color::Rgb(10, 20, 30)
);
assert_eq!(Color::from(CColor::AnsiValue(32)), Color::Indexed(32));
assert_eq!(Color::from(CColor::AnsiValue(37)), Color::Indexed(37));
#[rstest]
#[case(CrosstermColor::Reset, Color::Reset)]
#[case(CrosstermColor::Black, Color::Black)]
#[case(CrosstermColor::DarkGrey, Color::DarkGray)]
#[case(CrosstermColor::Red, Color::LightRed)]
#[case(CrosstermColor::DarkRed, Color::Red)]
#[case(CrosstermColor::Green, Color::LightGreen)]
#[case(CrosstermColor::DarkGreen, Color::Green)]
#[case(CrosstermColor::Yellow, Color::LightYellow)]
#[case(CrosstermColor::DarkYellow, Color::Yellow)]
#[case(CrosstermColor::Blue, Color::LightBlue)]
#[case(CrosstermColor::DarkBlue, Color::Blue)]
#[case(CrosstermColor::Magenta, Color::LightMagenta)]
#[case(CrosstermColor::DarkMagenta, Color::Magenta)]
#[case(CrosstermColor::Cyan, Color::LightCyan)]
#[case(CrosstermColor::DarkCyan, Color::Cyan)]
#[case(CrosstermColor::White, Color::White)]
#[case(CrosstermColor::Grey, Color::Gray)]
#[case(CrosstermColor::Rgb { r: 0, g: 0, b: 0 }, Color::Rgb(0, 0, 0) )]
#[case(CrosstermColor::Rgb { r: 10, g: 20, b: 30 }, Color::Rgb(10, 20, 30) )]
#[case(CrosstermColor::AnsiValue(32), Color::Indexed(32))]
#[case(CrosstermColor::AnsiValue(37), Color::Indexed(37))]
fn from_crossterm_color(#[case] crossterm_color: CrosstermColor, #[case] color: Color) {
assert_eq!(Color::from_crossterm(crossterm_color), color);
}
mod modifier {
use super::*;
#[test]
fn from_crossterm_attribute() {
assert_eq!(Modifier::from(CAttribute::Reset), Modifier::empty());
assert_eq!(Modifier::from(CAttribute::Bold), Modifier::BOLD);
assert_eq!(Modifier::from(CAttribute::Italic), Modifier::ITALIC);
assert_eq!(Modifier::from(CAttribute::Underlined), Modifier::UNDERLINED);
#[rstest]
#[case(CrosstermAttribute::Reset, Modifier::empty())]
#[case(CrosstermAttribute::Bold, Modifier::BOLD)]
#[case(CrosstermAttribute::NoBold, Modifier::empty())]
#[case(CrosstermAttribute::Italic, Modifier::ITALIC)]
#[case(CrosstermAttribute::NoItalic, Modifier::empty())]
#[case(CrosstermAttribute::Underlined, Modifier::UNDERLINED)]
#[case(CrosstermAttribute::NoUnderline, Modifier::empty())]
#[case(CrosstermAttribute::OverLined, Modifier::empty())]
#[case(CrosstermAttribute::NotOverLined, Modifier::empty())]
#[case(CrosstermAttribute::DoubleUnderlined, Modifier::UNDERLINED)]
#[case(CrosstermAttribute::Undercurled, Modifier::UNDERLINED)]
#[case(CrosstermAttribute::Underdotted, Modifier::UNDERLINED)]
#[case(CrosstermAttribute::Underdashed, Modifier::UNDERLINED)]
#[case(CrosstermAttribute::Dim, Modifier::DIM)]
#[case(CrosstermAttribute::NormalIntensity, Modifier::empty())]
#[case(CrosstermAttribute::CrossedOut, Modifier::CROSSED_OUT)]
#[case(CrosstermAttribute::NotCrossedOut, Modifier::empty())]
#[case(CrosstermAttribute::NoUnderline, Modifier::empty())]
#[case(CrosstermAttribute::SlowBlink, Modifier::SLOW_BLINK)]
#[case(CrosstermAttribute::RapidBlink, Modifier::RAPID_BLINK)]
#[case(CrosstermAttribute::Hidden, Modifier::HIDDEN)]
#[case(CrosstermAttribute::NoHidden, Modifier::empty())]
#[case(CrosstermAttribute::Reverse, Modifier::REVERSED)]
#[case(CrosstermAttribute::NoReverse, Modifier::empty())]
fn from_crossterm_attribute(
#[case] crossterm_attribute: CrosstermAttribute,
#[case] ratatui_modifier: Modifier,
) {
assert_eq!(
Modifier::from(CAttribute::DoubleUnderlined),
Modifier::UNDERLINED
Modifier::from_crossterm(crossterm_attribute),
ratatui_modifier
);
assert_eq!(
Modifier::from(CAttribute::Underdotted),
Modifier::UNDERLINED
);
assert_eq!(Modifier::from(CAttribute::Dim), Modifier::DIM);
assert_eq!(
Modifier::from(CAttribute::NormalIntensity),
Modifier::empty()
);
assert_eq!(
Modifier::from(CAttribute::CrossedOut),
Modifier::CROSSED_OUT
);
assert_eq!(Modifier::from(CAttribute::NoUnderline), Modifier::empty());
assert_eq!(Modifier::from(CAttribute::OverLined), Modifier::empty());
assert_eq!(Modifier::from(CAttribute::SlowBlink), Modifier::SLOW_BLINK);
assert_eq!(
Modifier::from(CAttribute::RapidBlink),
Modifier::RAPID_BLINK
);
assert_eq!(Modifier::from(CAttribute::Hidden), Modifier::HIDDEN);
assert_eq!(Modifier::from(CAttribute::NoHidden), Modifier::empty());
assert_eq!(Modifier::from(CAttribute::Reverse), Modifier::REVERSED);
}
#[test]
fn from_crossterm_attributes() {
#[rstest]
#[case(&[CrosstermAttribute::Bold], Modifier::BOLD)]
#[case(&[CrosstermAttribute::Bold, CrosstermAttribute::Italic], Modifier::BOLD | Modifier::ITALIC)]
#[case(&[CrosstermAttribute::Bold, CrosstermAttribute::NotCrossedOut], Modifier::BOLD)]
#[case(&[CrosstermAttribute::Dim, CrosstermAttribute::Underdotted], Modifier::DIM | Modifier::UNDERLINED)]
#[case(&[CrosstermAttribute::Dim, CrosstermAttribute::SlowBlink, CrosstermAttribute::Italic], Modifier::DIM | Modifier::SLOW_BLINK | Modifier::ITALIC)]
#[case(&[CrosstermAttribute::Hidden, CrosstermAttribute::NoUnderline, CrosstermAttribute::NotCrossedOut], Modifier::HIDDEN)]
#[case(&[CrosstermAttribute::Reverse], Modifier::REVERSED)]
#[case(&[CrosstermAttribute::Reset], Modifier::empty())]
#[case(&[CrosstermAttribute::RapidBlink, CrosstermAttribute::CrossedOut], Modifier::RAPID_BLINK | Modifier::CROSSED_OUT)]
fn from_crossterm_attributes(
#[case] crossterm_attributes: &[CrosstermAttribute],
#[case] ratatui_modifier: Modifier,
) {
assert_eq!(
Modifier::from(CAttributes::from(CAttribute::Bold)),
Modifier::BOLD
);
assert_eq!(
Modifier::from(CAttributes::from(
[CAttribute::Bold, CAttribute::Italic].as_ref()
)),
Modifier::BOLD | Modifier::ITALIC
);
assert_eq!(
Modifier::from(CAttributes::from(
[CAttribute::Bold, CAttribute::NotCrossedOut].as_ref()
)),
Modifier::BOLD
);
assert_eq!(
Modifier::from(CAttributes::from(
[CAttribute::Dim, CAttribute::Underdotted].as_ref()
)),
Modifier::DIM | Modifier::UNDERLINED
);
assert_eq!(
Modifier::from(CAttributes::from(
[CAttribute::Dim, CAttribute::SlowBlink, CAttribute::Italic].as_ref()
)),
Modifier::DIM | Modifier::SLOW_BLINK | Modifier::ITALIC
);
assert_eq!(
Modifier::from(CAttributes::from(
[
CAttribute::Hidden,
CAttribute::NoUnderline,
CAttribute::NotCrossedOut
]
.as_ref()
)),
Modifier::HIDDEN
);
assert_eq!(
Modifier::from(CAttributes::from(CAttribute::Reverse)),
Modifier::REVERSED
);
assert_eq!(
Modifier::from(CAttributes::from(CAttribute::Reset)),
Modifier::empty()
);
assert_eq!(
Modifier::from(CAttributes::from(
[CAttribute::RapidBlink, CAttribute::CrossedOut].as_ref()
)),
Modifier::RAPID_BLINK | Modifier::CROSSED_OUT
Modifier::from_crossterm(CrosstermAttributes::from(crossterm_attributes)),
ratatui_modifier
);
}
}
#[test]
fn from_crossterm_content_style() {
assert_eq!(Style::from(ContentStyle::default()), Style::default());
assert_eq!(
Style::from(ContentStyle {
foreground_color: Some(CColor::DarkYellow),
..Default::default()
}),
Style::default().fg(Color::Yellow)
);
assert_eq!(
Style::from(ContentStyle {
background_color: Some(CColor::DarkYellow),
..Default::default()
}),
Style::default().bg(Color::Yellow)
);
assert_eq!(
Style::from(ContentStyle {
attributes: CAttributes::from(CAttribute::Bold),
..Default::default()
}),
Style::default().add_modifier(Modifier::BOLD)
);
assert_eq!(
Style::from(ContentStyle {
attributes: CAttributes::from(CAttribute::NoBold),
..Default::default()
}),
Style::default().remove_modifier(Modifier::BOLD)
);
assert_eq!(
Style::from(ContentStyle {
attributes: CAttributes::from(CAttribute::Italic),
..Default::default()
}),
Style::default().add_modifier(Modifier::ITALIC)
);
assert_eq!(
Style::from(ContentStyle {
attributes: CAttributes::from(CAttribute::NoItalic),
..Default::default()
}),
Style::default().remove_modifier(Modifier::ITALIC)
);
assert_eq!(
Style::from(ContentStyle {
attributes: CAttributes::from([CAttribute::Bold, CAttribute::Italic].as_ref()),
..Default::default()
}),
Style::default()
.add_modifier(Modifier::BOLD)
.add_modifier(Modifier::ITALIC)
);
assert_eq!(
Style::from(ContentStyle {
attributes: CAttributes::from([CAttribute::NoBold, CAttribute::NoItalic].as_ref()),
..Default::default()
}),
Style::default()
.remove_modifier(Modifier::BOLD)
.remove_modifier(Modifier::ITALIC)
);
#[rstest]
#[case(ContentStyle::default(), Style::default())]
#[case(
ContentStyle {
foreground_color: Some(CrosstermColor::DarkYellow),
..Default::default()
},
Style::default().fg(Color::Yellow)
)]
#[case(
ContentStyle {
background_color: Some(CrosstermColor::DarkYellow),
..Default::default()
},
Style::default().bg(Color::Yellow)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(CrosstermAttribute::Bold),
..Default::default()
},
Style::default().add_modifier(Modifier::BOLD)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(CrosstermAttribute::NoBold),
..Default::default()
},
Style::default().remove_modifier(Modifier::BOLD)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(CrosstermAttribute::Italic),
..Default::default()
},
Style::default().add_modifier(Modifier::ITALIC)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(CrosstermAttribute::NoItalic),
..Default::default()
},
Style::default().remove_modifier(Modifier::ITALIC)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(
[CrosstermAttribute::Bold, CrosstermAttribute::Italic].as_ref()
),
..Default::default()
},
Style::default()
.add_modifier(Modifier::BOLD)
.add_modifier(Modifier::ITALIC)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(
[CrosstermAttribute::NoBold, CrosstermAttribute::NoItalic].as_ref()
),
..Default::default()
},
Style::default()
.remove_modifier(Modifier::BOLD)
.remove_modifier(Modifier::ITALIC)
)]
fn from_crossterm_content_style(#[case] content_style: ContentStyle, #[case] style: Style) {
assert_eq!(Style::from_crossterm(content_style), style);
}
#[test]
#[cfg(feature = "underline-color")]
fn from_crossterm_content_style_underline() {
let content_style = ContentStyle {
underline_color: Some(CrosstermColor::DarkRed),
..Default::default()
};
assert_eq!(
Style::from(ContentStyle {
underline_color: Some(CColor::DarkRed),
..Default::default()
}),
Style::from_crossterm(content_style),
Style::default().underline_color(Color::Red)
);
}

View File

@@ -327,22 +327,40 @@ impl fmt::Display for Bg {
}
}
/// A trait for converting a Termion type to a Ratatui type.
///
/// This trait is necessary to avoid the orphan rule, as we cannot implement a trait for a type
/// defined in another crate.
pub trait FromTermion<T> {
/// Convert the Termion type to the Ratatui type.
fn from_termion(termion: T) -> Self;
}
/// A trait for converting a Ratatui type to a Termion type.
///
/// This trait is necessary to avoid the orphan rule, as we cannot implement a trait for a type
/// defined in another crate.
pub trait IntoTermion<T> {
/// Convert the Ratatui type to the Termion type.
fn into_termion(self) -> T;
}
macro_rules! from_termion_for_color {
($termion_color:ident, $color:ident) => {
impl From<tcolor::$termion_color> for Color {
fn from(_: tcolor::$termion_color) -> Self {
impl FromTermion<tcolor::$termion_color> for Color {
fn from_termion(_: tcolor::$termion_color) -> Self {
Color::$color
}
}
impl From<tcolor::Bg<tcolor::$termion_color>> for Style {
fn from(_: tcolor::Bg<tcolor::$termion_color>) -> Self {
impl FromTermion<tcolor::Bg<tcolor::$termion_color>> for Style {
fn from_termion(_: tcolor::Bg<tcolor::$termion_color>) -> Self {
Style::default().bg(Color::$color)
}
}
impl From<tcolor::Fg<tcolor::$termion_color>> for Style {
fn from(_: tcolor::Fg<tcolor::$termion_color>) -> Self {
impl FromTermion<tcolor::Fg<tcolor::$termion_color>> for Style {
fn from_termion(_: tcolor::Fg<tcolor::$termion_color>) -> Self {
Style::default().fg(Color::$color)
}
}
@@ -367,38 +385,38 @@ from_termion_for_color!(LightMagenta, LightMagenta);
from_termion_for_color!(LightCyan, LightCyan);
from_termion_for_color!(LightWhite, White);
impl From<tcolor::AnsiValue> for Color {
fn from(value: tcolor::AnsiValue) -> Self {
impl FromTermion<tcolor::AnsiValue> for Color {
fn from_termion(value: tcolor::AnsiValue) -> Self {
Self::Indexed(value.0)
}
}
impl From<tcolor::Bg<tcolor::AnsiValue>> for Style {
fn from(value: tcolor::Bg<tcolor::AnsiValue>) -> Self {
impl FromTermion<tcolor::Bg<tcolor::AnsiValue>> for Style {
fn from_termion(value: tcolor::Bg<tcolor::AnsiValue>) -> Self {
Self::default().bg(Color::Indexed(value.0 .0))
}
}
impl From<tcolor::Fg<tcolor::AnsiValue>> for Style {
fn from(value: tcolor::Fg<tcolor::AnsiValue>) -> Self {
impl FromTermion<tcolor::Fg<tcolor::AnsiValue>> for Style {
fn from_termion(value: tcolor::Fg<tcolor::AnsiValue>) -> Self {
Self::default().fg(Color::Indexed(value.0 .0))
}
}
impl From<tcolor::Rgb> for Color {
fn from(value: tcolor::Rgb) -> Self {
impl FromTermion<tcolor::Rgb> for Color {
fn from_termion(value: tcolor::Rgb) -> Self {
Self::Rgb(value.0, value.1, value.2)
}
}
impl From<tcolor::Bg<tcolor::Rgb>> for Style {
fn from(value: tcolor::Bg<tcolor::Rgb>) -> Self {
impl FromTermion<tcolor::Bg<tcolor::Rgb>> for Style {
fn from_termion(value: tcolor::Bg<tcolor::Rgb>) -> Self {
Self::default().bg(Color::Rgb(value.0 .0, value.0 .1, value.0 .2))
}
}
impl From<tcolor::Fg<tcolor::Rgb>> for Style {
fn from(value: tcolor::Fg<tcolor::Rgb>) -> Self {
impl FromTermion<tcolor::Fg<tcolor::Rgb>> for Style {
fn from_termion(value: tcolor::Fg<tcolor::Rgb>) -> Self {
Self::default().fg(Color::Rgb(value.0 .0, value.0 .1, value.0 .2))
}
}
@@ -470,8 +488,8 @@ impl fmt::Display for ModifierDiff {
macro_rules! from_termion_for_modifier {
($termion_modifier:ident, $modifier:ident) => {
impl From<tstyle::$termion_modifier> for Modifier {
fn from(_: tstyle::$termion_modifier) -> Self {
impl FromTermion<tstyle::$termion_modifier> for Modifier {
fn from_termion(_: tstyle::$termion_modifier) -> Self {
Modifier::$modifier
}
}
@@ -486,8 +504,8 @@ from_termion_for_modifier!(Faint, DIM);
from_termion_for_modifier!(CrossedOut, CROSSED_OUT);
from_termion_for_modifier!(Blink, SLOW_BLINK);
impl From<termion::style::Reset> for Modifier {
fn from(_: termion::style::Reset) -> Self {
impl FromTermion<termion::style::Reset> for Modifier {
fn from_termion(_: termion::style::Reset) -> Self {
Self::empty()
}
}
@@ -519,25 +537,34 @@ mod tests {
#[test]
fn from_termion_color() {
assert_eq!(Color::from(tcolor::Reset), Color::Reset);
assert_eq!(Color::from(tcolor::Black), Color::Black);
assert_eq!(Color::from(tcolor::Red), Color::Red);
assert_eq!(Color::from(tcolor::Green), Color::Green);
assert_eq!(Color::from(tcolor::Yellow), Color::Yellow);
assert_eq!(Color::from(tcolor::Blue), Color::Blue);
assert_eq!(Color::from(tcolor::Magenta), Color::Magenta);
assert_eq!(Color::from(tcolor::Cyan), Color::Cyan);
assert_eq!(Color::from(tcolor::White), Color::Gray);
assert_eq!(Color::from(tcolor::LightBlack), Color::DarkGray);
assert_eq!(Color::from(tcolor::LightRed), Color::LightRed);
assert_eq!(Color::from(tcolor::LightGreen), Color::LightGreen);
assert_eq!(Color::from(tcolor::LightBlue), Color::LightBlue);
assert_eq!(Color::from(tcolor::LightYellow), Color::LightYellow);
assert_eq!(Color::from(tcolor::LightMagenta), Color::LightMagenta);
assert_eq!(Color::from(tcolor::LightCyan), Color::LightCyan);
assert_eq!(Color::from(tcolor::LightWhite), Color::White);
assert_eq!(Color::from(tcolor::AnsiValue(31)), Color::Indexed(31));
assert_eq!(Color::from(tcolor::Rgb(1, 2, 3)), Color::Rgb(1, 2, 3));
assert_eq!(Color::from_termion(tcolor::Reset), Color::Reset);
assert_eq!(Color::from_termion(tcolor::Black), Color::Black);
assert_eq!(Color::from_termion(tcolor::Red), Color::Red);
assert_eq!(Color::from_termion(tcolor::Green), Color::Green);
assert_eq!(Color::from_termion(tcolor::Yellow), Color::Yellow);
assert_eq!(Color::from_termion(tcolor::Blue), Color::Blue);
assert_eq!(Color::from_termion(tcolor::Magenta), Color::Magenta);
assert_eq!(Color::from_termion(tcolor::Cyan), Color::Cyan);
assert_eq!(Color::from_termion(tcolor::White), Color::Gray);
assert_eq!(Color::from_termion(tcolor::LightBlack), Color::DarkGray);
assert_eq!(Color::from_termion(tcolor::LightRed), Color::LightRed);
assert_eq!(Color::from_termion(tcolor::LightGreen), Color::LightGreen);
assert_eq!(Color::from_termion(tcolor::LightBlue), Color::LightBlue);
assert_eq!(Color::from_termion(tcolor::LightYellow), Color::LightYellow);
assert_eq!(
Color::from_termion(tcolor::LightMagenta),
Color::LightMagenta
);
assert_eq!(Color::from_termion(tcolor::LightCyan), Color::LightCyan);
assert_eq!(Color::from_termion(tcolor::LightWhite), Color::White);
assert_eq!(
Color::from_termion(tcolor::AnsiValue(31)),
Color::Indexed(31)
);
assert_eq!(
Color::from_termion(tcolor::Rgb(1, 2, 3)),
Color::Rgb(1, 2, 3)
);
}
#[test]
@@ -545,38 +572,62 @@ mod tests {
use tc::Bg;
use tcolor as tc;
assert_eq!(Style::from(Bg(tc::Reset)), Style::new().bg(Color::Reset));
assert_eq!(Style::from(Bg(tc::Black)), Style::new().on_black());
assert_eq!(Style::from(Bg(tc::Red)), Style::new().on_red());
assert_eq!(Style::from(Bg(tc::Green)), Style::new().on_green());
assert_eq!(Style::from(Bg(tc::Yellow)), Style::new().on_yellow());
assert_eq!(Style::from(Bg(tc::Blue)), Style::new().on_blue());
assert_eq!(Style::from(Bg(tc::Magenta)), Style::new().on_magenta());
assert_eq!(Style::from(Bg(tc::Cyan)), Style::new().on_cyan());
assert_eq!(Style::from(Bg(tc::White)), Style::new().on_gray());
assert_eq!(Style::from(Bg(tc::LightBlack)), Style::new().on_dark_gray());
assert_eq!(Style::from(Bg(tc::LightRed)), Style::new().on_light_red());
assert_eq!(
Style::from(Bg(tc::LightGreen)),
Style::from_termion(Bg(tc::Reset)),
Style::new().bg(Color::Reset)
);
assert_eq!(Style::from_termion(Bg(tc::Black)), Style::new().on_black());
assert_eq!(Style::from_termion(Bg(tc::Red)), Style::new().on_red());
assert_eq!(Style::from_termion(Bg(tc::Green)), Style::new().on_green());
assert_eq!(
Style::from_termion(Bg(tc::Yellow)),
Style::new().on_yellow()
);
assert_eq!(Style::from_termion(Bg(tc::Blue)), Style::new().on_blue());
assert_eq!(
Style::from_termion(Bg(tc::Magenta)),
Style::new().on_magenta()
);
assert_eq!(Style::from_termion(Bg(tc::Cyan)), Style::new().on_cyan());
assert_eq!(Style::from_termion(Bg(tc::White)), Style::new().on_gray());
assert_eq!(
Style::from_termion(Bg(tc::LightBlack)),
Style::new().on_dark_gray()
);
assert_eq!(
Style::from_termion(Bg(tc::LightRed)),
Style::new().on_light_red()
);
assert_eq!(
Style::from_termion(Bg(tc::LightGreen)),
Style::new().on_light_green()
);
assert_eq!(Style::from(Bg(tc::LightBlue)), Style::new().on_light_blue());
assert_eq!(
Style::from(Bg(tc::LightYellow)),
Style::from_termion(Bg(tc::LightBlue)),
Style::new().on_light_blue()
);
assert_eq!(
Style::from_termion(Bg(tc::LightYellow)),
Style::new().on_light_yellow()
);
assert_eq!(
Style::from(Bg(tc::LightMagenta)),
Style::from_termion(Bg(tc::LightMagenta)),
Style::new().on_light_magenta()
);
assert_eq!(Style::from(Bg(tc::LightCyan)), Style::new().on_light_cyan());
assert_eq!(Style::from(Bg(tc::LightWhite)), Style::new().on_white());
assert_eq!(
Style::from(Bg(tc::AnsiValue(31))),
Style::from_termion(Bg(tc::LightCyan)),
Style::new().on_light_cyan()
);
assert_eq!(
Style::from_termion(Bg(tc::LightWhite)),
Style::new().on_white()
);
assert_eq!(
Style::from_termion(Bg(tc::AnsiValue(31))),
Style::new().bg(Color::Indexed(31))
);
assert_eq!(
Style::from(Bg(tc::Rgb(1, 2, 3))),
Style::from_termion(Bg(tc::Rgb(1, 2, 3))),
Style::new().bg(Color::Rgb(1, 2, 3))
);
}
@@ -586,48 +637,78 @@ mod tests {
use tc::Fg;
use tcolor as tc;
assert_eq!(Style::from(Fg(tc::Reset)), Style::new().fg(Color::Reset));
assert_eq!(Style::from(Fg(tc::Black)), Style::new().black());
assert_eq!(Style::from(Fg(tc::Red)), Style::new().red());
assert_eq!(Style::from(Fg(tc::Green)), Style::new().green());
assert_eq!(Style::from(Fg(tc::Yellow)), Style::new().yellow());
assert_eq!(Style::from(Fg(tc::Blue)), Style::default().blue());
assert_eq!(Style::from(Fg(tc::Magenta)), Style::default().magenta());
assert_eq!(Style::from(Fg(tc::Cyan)), Style::default().cyan());
assert_eq!(Style::from(Fg(tc::White)), Style::default().gray());
assert_eq!(Style::from(Fg(tc::LightBlack)), Style::new().dark_gray());
assert_eq!(Style::from(Fg(tc::LightRed)), Style::new().light_red());
assert_eq!(Style::from(Fg(tc::LightGreen)), Style::new().light_green());
assert_eq!(Style::from(Fg(tc::LightBlue)), Style::new().light_blue());
assert_eq!(
Style::from(Fg(tc::LightYellow)),
Style::from_termion(Fg(tc::Reset)),
Style::new().fg(Color::Reset)
);
assert_eq!(Style::from_termion(Fg(tc::Black)), Style::new().black());
assert_eq!(Style::from_termion(Fg(tc::Red)), Style::new().red());
assert_eq!(Style::from_termion(Fg(tc::Green)), Style::new().green());
assert_eq!(Style::from_termion(Fg(tc::Yellow)), Style::new().yellow());
assert_eq!(Style::from_termion(Fg(tc::Blue)), Style::default().blue());
assert_eq!(
Style::from_termion(Fg(tc::Magenta)),
Style::default().magenta()
);
assert_eq!(Style::from_termion(Fg(tc::Cyan)), Style::default().cyan());
assert_eq!(Style::from_termion(Fg(tc::White)), Style::default().gray());
assert_eq!(
Style::from_termion(Fg(tc::LightBlack)),
Style::new().dark_gray()
);
assert_eq!(
Style::from_termion(Fg(tc::LightRed)),
Style::new().light_red()
);
assert_eq!(
Style::from_termion(Fg(tc::LightGreen)),
Style::new().light_green()
);
assert_eq!(
Style::from_termion(Fg(tc::LightBlue)),
Style::new().light_blue()
);
assert_eq!(
Style::from_termion(Fg(tc::LightYellow)),
Style::new().light_yellow()
);
assert_eq!(
Style::from(Fg(tc::LightMagenta)),
Style::from_termion(Fg(tc::LightMagenta)),
Style::new().light_magenta()
);
assert_eq!(Style::from(Fg(tc::LightCyan)), Style::new().light_cyan());
assert_eq!(Style::from(Fg(tc::LightWhite)), Style::new().white());
assert_eq!(
Style::from(Fg(tc::AnsiValue(31))),
Style::from_termion(Fg(tc::LightCyan)),
Style::new().light_cyan()
);
assert_eq!(
Style::from_termion(Fg(tc::LightWhite)),
Style::new().white()
);
assert_eq!(
Style::from_termion(Fg(tc::AnsiValue(31))),
Style::default().fg(Color::Indexed(31))
);
assert_eq!(
Style::from(Fg(tc::Rgb(1, 2, 3))),
Style::from_termion(Fg(tc::Rgb(1, 2, 3))),
Style::default().fg(Color::Rgb(1, 2, 3))
);
}
#[test]
fn from_termion_style() {
assert_eq!(Modifier::from(tstyle::Invert), Modifier::REVERSED);
assert_eq!(Modifier::from(tstyle::Bold), Modifier::BOLD);
assert_eq!(Modifier::from(tstyle::Italic), Modifier::ITALIC);
assert_eq!(Modifier::from(tstyle::Underline), Modifier::UNDERLINED);
assert_eq!(Modifier::from(tstyle::Faint), Modifier::DIM);
assert_eq!(Modifier::from(tstyle::CrossedOut), Modifier::CROSSED_OUT);
assert_eq!(Modifier::from(tstyle::Blink), Modifier::SLOW_BLINK);
assert_eq!(Modifier::from(tstyle::Reset), Modifier::empty());
assert_eq!(Modifier::from_termion(tstyle::Invert), Modifier::REVERSED);
assert_eq!(Modifier::from_termion(tstyle::Bold), Modifier::BOLD);
assert_eq!(Modifier::from_termion(tstyle::Italic), Modifier::ITALIC);
assert_eq!(
Modifier::from_termion(tstyle::Underline),
Modifier::UNDERLINED
);
assert_eq!(Modifier::from_termion(tstyle::Faint), Modifier::DIM);
assert_eq!(
Modifier::from_termion(tstyle::CrossedOut),
Modifier::CROSSED_OUT
);
assert_eq!(Modifier::from_termion(tstyle::Blink), Modifier::SLOW_BLINK);
assert_eq!(Modifier::from_termion(tstyle::Reset), Modifier::empty());
}
}

View File

@@ -120,8 +120,8 @@ impl Backend for TermwizBackend {
x: Position::Absolute(x as usize),
y: Position::Absolute(y as usize),
},
Change::Attribute(AttributeChange::Foreground(cell.fg.into())),
Change::Attribute(AttributeChange::Background(cell.bg.into())),
Change::Attribute(AttributeChange::Foreground(cell.fg.into_termwiz())),
Change::Attribute(AttributeChange::Background(cell.bg.into_termwiz())),
]);
self.buffered_terminal
@@ -298,12 +298,49 @@ impl Backend for TermwizBackend {
}
}
impl From<CellAttributes> for Style {
fn from(value: CellAttributes) -> Self {
/// A trait for converting types from Termwiz to Ratatui.
///
/// This trait replaces the `From` trait for converting types from Termwiz to Ratatui. It is
/// necessary because the `From` trait is not implemented for types defined in external crates.
pub trait FromTermwiz<T> {
/// Converts the given Termwiz type to the Ratatui type.
fn from_termwiz(termwiz: T) -> Self;
}
/// A trait for converting types from Ratatui to Termwiz.
///
/// This trait replaces the `Into` trait for converting types from Ratatui to Termwiz. It is
/// necessary because the `Into` trait is not implemented for types defined in external crates.
pub trait IntoTermwiz<T> {
/// Converts the given Ratatui type to the Termwiz type.
fn into_termwiz(self) -> T;
}
/// A replacement for the `Into` trait for converting types from Ratatui to Termwiz.
///
/// This trait is necessary because the `Into` trait is not implemented for types defined in
/// external crates.
///
/// A blanket implementation is provided for all types that implement `FromTermwiz`.
///
/// This trait is private to the module as it would otherwise conflict with the other backend
/// modules. It is mainly used to avoid rewriting all the `.into()` calls in this module.
trait IntoRatatui<R> {
fn into_ratatui(self) -> R;
}
impl<C, R: FromTermwiz<C>> IntoRatatui<R> for C {
fn into_ratatui(self) -> R {
R::from_termwiz(self)
}
}
impl FromTermwiz<CellAttributes> for Style {
fn from_termwiz(value: CellAttributes) -> Self {
let mut style = Self::new()
.add_modifier(value.intensity().into())
.add_modifier(value.underline().into())
.add_modifier(value.blink().into());
.add_modifier(value.intensity().into_ratatui())
.add_modifier(value.underline().into_ratatui())
.add_modifier(value.blink().into_ratatui());
if value.italic() {
style.add_modifier |= Modifier::ITALIC;
@@ -318,19 +355,19 @@ impl From<CellAttributes> for Style {
style.add_modifier |= Modifier::HIDDEN;
}
style.fg = Some(value.foreground().into());
style.bg = Some(value.background().into());
style.fg = Some(value.foreground().into_ratatui());
style.bg = Some(value.background().into_ratatui());
#[cfg(feature = "underline-color")]
{
style.underline_color = Some(value.underline_color().into());
style.underline_color = Some(value.underline_color().into_ratatui());
}
style
}
}
impl From<Intensity> for Modifier {
fn from(value: Intensity) -> Self {
impl FromTermwiz<Intensity> for Modifier {
fn from_termwiz(value: Intensity) -> Self {
match value {
Intensity::Normal => Self::empty(),
Intensity::Bold => Self::BOLD,
@@ -339,8 +376,8 @@ impl From<Intensity> for Modifier {
}
}
impl From<Underline> for Modifier {
fn from(value: Underline) -> Self {
impl FromTermwiz<Underline> for Modifier {
fn from_termwiz(value: Underline) -> Self {
match value {
Underline::None => Self::empty(),
_ => Self::UNDERLINED,
@@ -348,8 +385,8 @@ impl From<Underline> for Modifier {
}
}
impl From<Blink> for Modifier {
fn from(value: Blink) -> Self {
impl FromTermwiz<Blink> for Modifier {
fn from_termwiz(value: Blink) -> Self {
match value {
Blink::None => Self::empty(),
Blink::Slow => Self::SLOW_BLINK,
@@ -358,34 +395,36 @@ impl From<Blink> for Modifier {
}
}
impl From<Color> for ColorAttribute {
fn from(color: Color) -> Self {
match color {
Color::Reset => Self::Default,
Color::Black => AnsiColor::Black.into(),
Color::DarkGray => AnsiColor::Grey.into(),
Color::Gray => AnsiColor::Silver.into(),
Color::Red => AnsiColor::Maroon.into(),
Color::LightRed => AnsiColor::Red.into(),
Color::Green => AnsiColor::Green.into(),
Color::LightGreen => AnsiColor::Lime.into(),
Color::Yellow => AnsiColor::Olive.into(),
Color::LightYellow => AnsiColor::Yellow.into(),
Color::Magenta => AnsiColor::Purple.into(),
Color::LightMagenta => AnsiColor::Fuchsia.into(),
Color::Cyan => AnsiColor::Teal.into(),
Color::LightCyan => AnsiColor::Aqua.into(),
Color::White => AnsiColor::White.into(),
Color::Blue => AnsiColor::Navy.into(),
Color::LightBlue => AnsiColor::Blue.into(),
Color::Indexed(i) => Self::PaletteIndex(i),
Color::Rgb(r, g, b) => Self::TrueColorWithDefaultFallback(SrgbaTuple::from((r, g, b))),
impl IntoTermwiz<ColorAttribute> for Color {
fn into_termwiz(self) -> ColorAttribute {
match self {
Self::Reset => ColorAttribute::Default,
Self::Black => AnsiColor::Black.into(),
Self::DarkGray => AnsiColor::Grey.into(),
Self::Gray => AnsiColor::Silver.into(),
Self::Red => AnsiColor::Maroon.into(),
Self::LightRed => AnsiColor::Red.into(),
Self::Green => AnsiColor::Green.into(),
Self::LightGreen => AnsiColor::Lime.into(),
Self::Yellow => AnsiColor::Olive.into(),
Self::LightYellow => AnsiColor::Yellow.into(),
Self::Magenta => AnsiColor::Purple.into(),
Self::LightMagenta => AnsiColor::Fuchsia.into(),
Self::Cyan => AnsiColor::Teal.into(),
Self::LightCyan => AnsiColor::Aqua.into(),
Self::White => AnsiColor::White.into(),
Self::Blue => AnsiColor::Navy.into(),
Self::LightBlue => AnsiColor::Blue.into(),
Self::Indexed(i) => ColorAttribute::PaletteIndex(i),
Self::Rgb(r, g, b) => {
ColorAttribute::TrueColorWithDefaultFallback(SrgbaTuple::from((r, g, b)))
}
}
}
}
impl From<AnsiColor> for Color {
fn from(value: AnsiColor) -> Self {
impl FromTermwiz<AnsiColor> for Color {
fn from_termwiz(value: AnsiColor) -> Self {
match value {
AnsiColor::Black => Self::Black,
AnsiColor::Grey => Self::DarkGray,
@@ -407,44 +446,44 @@ impl From<AnsiColor> for Color {
}
}
impl From<ColorAttribute> for Color {
fn from(value: ColorAttribute) -> Self {
impl FromTermwiz<ColorAttribute> for Color {
fn from_termwiz(value: ColorAttribute) -> Self {
match value {
ColorAttribute::TrueColorWithDefaultFallback(srgba)
| ColorAttribute::TrueColorWithPaletteFallback(srgba, _) => srgba.into(),
| ColorAttribute::TrueColorWithPaletteFallback(srgba, _) => srgba.into_ratatui(),
ColorAttribute::PaletteIndex(i) => Self::Indexed(i),
ColorAttribute::Default => Self::Reset,
}
}
}
impl From<ColorSpec> for Color {
fn from(value: ColorSpec) -> Self {
impl FromTermwiz<ColorSpec> for Color {
fn from_termwiz(value: ColorSpec) -> Self {
match value {
ColorSpec::Default => Self::Reset,
ColorSpec::PaletteIndex(i) => Self::Indexed(i),
ColorSpec::TrueColor(srgba) => srgba.into(),
ColorSpec::TrueColor(srgba) => srgba.into_ratatui(),
}
}
}
impl From<SrgbaTuple> for Color {
fn from(value: SrgbaTuple) -> Self {
impl FromTermwiz<SrgbaTuple> for Color {
fn from_termwiz(value: SrgbaTuple) -> Self {
let (r, g, b, _) = value.to_srgb_u8();
Self::Rgb(r, g, b)
}
}
impl From<RgbColor> for Color {
fn from(value: RgbColor) -> Self {
impl FromTermwiz<RgbColor> for Color {
fn from_termwiz(value: RgbColor) -> Self {
let (r, g, b) = value.to_tuple_rgb8();
Self::Rgb(r, g, b)
}
}
impl From<LinearRgba> for Color {
fn from(value: LinearRgba) -> Self {
value.to_srgb().into()
impl FromTermwiz<LinearRgba> for Color {
fn from_termwiz(value: LinearRgba) -> Self {
value.to_srgb().into_ratatui()
}
}
@@ -465,107 +504,191 @@ mod tests {
#[test]
fn from_linear_rgba() {
// full black + opaque
assert_eq!(C::from(LinearRgba(0., 0., 0., 1.)), Color::Rgb(0, 0, 0));
assert_eq!(
C::from_termwiz(LinearRgba(0., 0., 0., 1.)),
Color::Rgb(0, 0, 0)
);
// full black + transparent
assert_eq!(C::from(LinearRgba(0., 0., 0., 0.)), Color::Rgb(0, 0, 0));
assert_eq!(
C::from_termwiz(LinearRgba(0., 0., 0., 0.)),
Color::Rgb(0, 0, 0)
);
// full white + opaque
assert_eq!(C::from(LinearRgba(1., 1., 1., 1.)), C::Rgb(254, 254, 254));
assert_eq!(
C::from_termwiz(LinearRgba(1., 1., 1., 1.)),
C::Rgb(254, 254, 254)
);
// full white + transparent
assert_eq!(C::from(LinearRgba(1., 1., 1., 0.)), C::Rgb(254, 254, 254));
assert_eq!(
C::from_termwiz(LinearRgba(1., 1., 1., 0.)),
C::Rgb(254, 254, 254)
);
// full red
assert_eq!(C::from(LinearRgba(1., 0., 0., 1.)), C::Rgb(254, 0, 0));
assert_eq!(
C::from_termwiz(LinearRgba(1., 0., 0., 1.)),
C::Rgb(254, 0, 0)
);
// full green
assert_eq!(C::from(LinearRgba(0., 1., 0., 1.)), C::Rgb(0, 254, 0));
assert_eq!(
C::from_termwiz(LinearRgba(0., 1., 0., 1.)),
C::Rgb(0, 254, 0)
);
// full blue
assert_eq!(C::from(LinearRgba(0., 0., 1., 1.)), C::Rgb(0, 0, 254));
assert_eq!(
C::from_termwiz(LinearRgba(0., 0., 1., 1.)),
C::Rgb(0, 0, 254)
);
// See https://stackoverflow.com/questions/12524623/what-are-the-practical-differences-when-working-with-colors-in-a-linear-vs-a-no
// for an explanation
// half red
assert_eq!(C::from(LinearRgba(0.214, 0., 0., 1.)), C::Rgb(127, 0, 0));
assert_eq!(
C::from_termwiz(LinearRgba(0.214, 0., 0., 1.)),
C::Rgb(127, 0, 0)
);
// half green
assert_eq!(C::from(LinearRgba(0., 0.214, 0., 1.)), C::Rgb(0, 127, 0));
assert_eq!(
C::from_termwiz(LinearRgba(0., 0.214, 0., 1.)),
C::Rgb(0, 127, 0)
);
// half blue
assert_eq!(C::from(LinearRgba(0., 0., 0.214, 1.)), C::Rgb(0, 0, 127));
assert_eq!(
C::from_termwiz(LinearRgba(0., 0., 0.214, 1.)),
C::Rgb(0, 0, 127)
);
}
#[test]
fn from_srgba() {
// full black + opaque
assert_eq!(C::from(SrgbaTuple(0., 0., 0., 1.)), Color::Rgb(0, 0, 0));
assert_eq!(
C::from_termwiz(SrgbaTuple(0., 0., 0., 1.)),
Color::Rgb(0, 0, 0)
);
// full black + transparent
assert_eq!(C::from(SrgbaTuple(0., 0., 0., 0.)), Color::Rgb(0, 0, 0));
assert_eq!(
C::from_termwiz(SrgbaTuple(0., 0., 0., 0.)),
Color::Rgb(0, 0, 0)
);
// full white + opaque
assert_eq!(C::from(SrgbaTuple(1., 1., 1., 1.)), C::Rgb(255, 255, 255));
assert_eq!(
C::from_termwiz(SrgbaTuple(1., 1., 1., 1.)),
C::Rgb(255, 255, 255)
);
// full white + transparent
assert_eq!(C::from(SrgbaTuple(1., 1., 1., 0.)), C::Rgb(255, 255, 255));
assert_eq!(
C::from_termwiz(SrgbaTuple(1., 1., 1., 0.)),
C::Rgb(255, 255, 255)
);
// full red
assert_eq!(C::from(SrgbaTuple(1., 0., 0., 1.)), C::Rgb(255, 0, 0));
assert_eq!(
C::from_termwiz(SrgbaTuple(1., 0., 0., 1.)),
C::Rgb(255, 0, 0)
);
// full green
assert_eq!(C::from(SrgbaTuple(0., 1., 0., 1.)), C::Rgb(0, 255, 0));
assert_eq!(
C::from_termwiz(SrgbaTuple(0., 1., 0., 1.)),
C::Rgb(0, 255, 0)
);
// full blue
assert_eq!(C::from(SrgbaTuple(0., 0., 1., 1.)), C::Rgb(0, 0, 255));
assert_eq!(
C::from_termwiz(SrgbaTuple(0., 0., 1., 1.)),
C::Rgb(0, 0, 255)
);
// half red
assert_eq!(C::from(SrgbaTuple(0.5, 0., 0., 1.)), C::Rgb(127, 0, 0));
assert_eq!(
C::from_termwiz(SrgbaTuple(0.5, 0., 0., 1.)),
C::Rgb(127, 0, 0)
);
// half green
assert_eq!(C::from(SrgbaTuple(0., 0.5, 0., 1.)), C::Rgb(0, 127, 0));
assert_eq!(
C::from_termwiz(SrgbaTuple(0., 0.5, 0., 1.)),
C::Rgb(0, 127, 0)
);
// half blue
assert_eq!(C::from(SrgbaTuple(0., 0., 0.5, 1.)), C::Rgb(0, 0, 127));
assert_eq!(
C::from_termwiz(SrgbaTuple(0., 0., 0.5, 1.)),
C::Rgb(0, 0, 127)
);
}
#[test]
fn from_rgbcolor() {
// full black
assert_eq!(C::from(RgbColor::new_8bpc(0, 0, 0)), Color::Rgb(0, 0, 0));
assert_eq!(
C::from_termwiz(RgbColor::new_8bpc(0, 0, 0)),
Color::Rgb(0, 0, 0)
);
// full white
assert_eq!(
C::from(RgbColor::new_8bpc(255, 255, 255)),
C::from_termwiz(RgbColor::new_8bpc(255, 255, 255)),
C::Rgb(255, 255, 255)
);
// full red
assert_eq!(C::from(RgbColor::new_8bpc(255, 0, 0)), C::Rgb(255, 0, 0));
assert_eq!(
C::from_termwiz(RgbColor::new_8bpc(255, 0, 0)),
C::Rgb(255, 0, 0)
);
// full green
assert_eq!(C::from(RgbColor::new_8bpc(0, 255, 0)), C::Rgb(0, 255, 0));
assert_eq!(
C::from_termwiz(RgbColor::new_8bpc(0, 255, 0)),
C::Rgb(0, 255, 0)
);
// full blue
assert_eq!(C::from(RgbColor::new_8bpc(0, 0, 255)), C::Rgb(0, 0, 255));
assert_eq!(
C::from_termwiz(RgbColor::new_8bpc(0, 0, 255)),
C::Rgb(0, 0, 255)
);
// half red
assert_eq!(C::from(RgbColor::new_8bpc(127, 0, 0)), C::Rgb(127, 0, 0));
assert_eq!(
C::from_termwiz(RgbColor::new_8bpc(127, 0, 0)),
C::Rgb(127, 0, 0)
);
// half green
assert_eq!(C::from(RgbColor::new_8bpc(0, 127, 0)), C::Rgb(0, 127, 0));
assert_eq!(
C::from_termwiz(RgbColor::new_8bpc(0, 127, 0)),
C::Rgb(0, 127, 0)
);
// half blue
assert_eq!(C::from(RgbColor::new_8bpc(0, 0, 127)), C::Rgb(0, 0, 127));
assert_eq!(
C::from_termwiz(RgbColor::new_8bpc(0, 0, 127)),
C::Rgb(0, 0, 127)
);
}
#[test]
fn from_colorspec() {
assert_eq!(C::from(ColorSpec::Default), C::Reset);
assert_eq!(C::from(ColorSpec::PaletteIndex(33)), C::Indexed(33));
assert_eq!(C::from_termwiz(ColorSpec::Default), C::Reset);
assert_eq!(C::from_termwiz(ColorSpec::PaletteIndex(33)), C::Indexed(33));
assert_eq!(
C::from(ColorSpec::TrueColor(SrgbaTuple(0., 0., 0., 1.))),
C::from_termwiz(ColorSpec::TrueColor(SrgbaTuple(0., 0., 0., 1.))),
C::Rgb(0, 0, 0)
);
}
#[test]
fn from_colorattribute() {
assert_eq!(C::from(ColorAttribute::Default), C::Reset);
assert_eq!(C::from(ColorAttribute::PaletteIndex(32)), C::Indexed(32));
assert_eq!(C::from_termwiz(ColorAttribute::Default), C::Reset);
assert_eq!(
C::from(ColorAttribute::TrueColorWithDefaultFallback(SrgbaTuple(
C::from_termwiz(ColorAttribute::PaletteIndex(32)),
C::Indexed(32)
);
assert_eq!(
C::from_termwiz(ColorAttribute::TrueColorWithDefaultFallback(SrgbaTuple(
0., 0., 0., 1.
))),
C::Rgb(0, 0, 0)
);
assert_eq!(
C::from(ColorAttribute::TrueColorWithPaletteFallback(
C::from_termwiz(ColorAttribute::TrueColorWithPaletteFallback(
SrgbaTuple(0., 0., 0., 1.),
31
)),
@@ -575,22 +698,22 @@ mod tests {
#[test]
fn from_ansicolor() {
assert_eq!(C::from(AnsiColor::Black), Color::Black);
assert_eq!(C::from(AnsiColor::Grey), Color::DarkGray);
assert_eq!(C::from(AnsiColor::Silver), Color::Gray);
assert_eq!(C::from(AnsiColor::Maroon), Color::Red);
assert_eq!(C::from(AnsiColor::Red), Color::LightRed);
assert_eq!(C::from(AnsiColor::Green), Color::Green);
assert_eq!(C::from(AnsiColor::Lime), Color::LightGreen);
assert_eq!(C::from(AnsiColor::Olive), Color::Yellow);
assert_eq!(C::from(AnsiColor::Yellow), Color::LightYellow);
assert_eq!(C::from(AnsiColor::Purple), Color::Magenta);
assert_eq!(C::from(AnsiColor::Fuchsia), Color::LightMagenta);
assert_eq!(C::from(AnsiColor::Teal), Color::Cyan);
assert_eq!(C::from(AnsiColor::Aqua), Color::LightCyan);
assert_eq!(C::from(AnsiColor::White), Color::White);
assert_eq!(C::from(AnsiColor::Navy), Color::Blue);
assert_eq!(C::from(AnsiColor::Blue), Color::LightBlue);
assert_eq!(C::from_termwiz(AnsiColor::Black), Color::Black);
assert_eq!(C::from_termwiz(AnsiColor::Grey), Color::DarkGray);
assert_eq!(C::from_termwiz(AnsiColor::Silver), Color::Gray);
assert_eq!(C::from_termwiz(AnsiColor::Maroon), Color::Red);
assert_eq!(C::from_termwiz(AnsiColor::Red), Color::LightRed);
assert_eq!(C::from_termwiz(AnsiColor::Green), Color::Green);
assert_eq!(C::from_termwiz(AnsiColor::Lime), Color::LightGreen);
assert_eq!(C::from_termwiz(AnsiColor::Olive), Color::Yellow);
assert_eq!(C::from_termwiz(AnsiColor::Yellow), Color::LightYellow);
assert_eq!(C::from_termwiz(AnsiColor::Purple), Color::Magenta);
assert_eq!(C::from_termwiz(AnsiColor::Fuchsia), Color::LightMagenta);
assert_eq!(C::from_termwiz(AnsiColor::Teal), Color::Cyan);
assert_eq!(C::from_termwiz(AnsiColor::Aqua), Color::LightCyan);
assert_eq!(C::from_termwiz(AnsiColor::White), Color::White);
assert_eq!(C::from_termwiz(AnsiColor::Navy), Color::Blue);
assert_eq!(C::from_termwiz(AnsiColor::Blue), Color::LightBlue);
}
}
@@ -599,26 +722,41 @@ mod tests {
#[test]
fn from_intensity() {
assert_eq!(Modifier::from(Intensity::Normal), Modifier::empty());
assert_eq!(Modifier::from(Intensity::Bold), Modifier::BOLD);
assert_eq!(Modifier::from(Intensity::Half), Modifier::DIM);
assert_eq!(Modifier::from_termwiz(Intensity::Normal), Modifier::empty());
assert_eq!(Modifier::from_termwiz(Intensity::Bold), Modifier::BOLD);
assert_eq!(Modifier::from_termwiz(Intensity::Half), Modifier::DIM);
}
#[test]
fn from_underline() {
assert_eq!(Modifier::from(Underline::None), Modifier::empty());
assert_eq!(Modifier::from(Underline::Single), Modifier::UNDERLINED);
assert_eq!(Modifier::from(Underline::Double), Modifier::UNDERLINED);
assert_eq!(Modifier::from(Underline::Curly), Modifier::UNDERLINED);
assert_eq!(Modifier::from(Underline::Dashed), Modifier::UNDERLINED);
assert_eq!(Modifier::from(Underline::Dotted), Modifier::UNDERLINED);
assert_eq!(Modifier::from_termwiz(Underline::None), Modifier::empty());
assert_eq!(
Modifier::from_termwiz(Underline::Single),
Modifier::UNDERLINED
);
assert_eq!(
Modifier::from_termwiz(Underline::Double),
Modifier::UNDERLINED
);
assert_eq!(
Modifier::from_termwiz(Underline::Curly),
Modifier::UNDERLINED
);
assert_eq!(
Modifier::from_termwiz(Underline::Dashed),
Modifier::UNDERLINED
);
assert_eq!(
Modifier::from_termwiz(Underline::Dotted),
Modifier::UNDERLINED
);
}
#[test]
fn from_blink() {
assert_eq!(Modifier::from(Blink::None), Modifier::empty());
assert_eq!(Modifier::from(Blink::Slow), Modifier::SLOW_BLINK);
assert_eq!(Modifier::from(Blink::Rapid), Modifier::RAPID_BLINK);
assert_eq!(Modifier::from_termwiz(Blink::None), Modifier::empty());
assert_eq!(Modifier::from_termwiz(Blink::Slow), Modifier::SLOW_BLINK);
assert_eq!(Modifier::from_termwiz(Blink::Rapid), Modifier::RAPID_BLINK);
}
}
@@ -635,11 +773,11 @@ mod tests {
const STYLE: Style = Style::new().fg(Color::Reset).bg(Color::Reset);
// default
assert_eq!(Style::from(CellAttributes::default()), STYLE);
assert_eq!(Style::from_termwiz(CellAttributes::default()), STYLE);
// foreground color
assert_eq!(
Style::from(
Style::from_termwiz(
CellAttributes::default()
.set_foreground(ColorAttribute::PaletteIndex(31))
.to_owned()
@@ -648,7 +786,7 @@ mod tests {
);
// background color
assert_eq!(
Style::from(
Style::from_termwiz(
CellAttributes::default()
.set_background(ColorAttribute::PaletteIndex(31))
.to_owned()
@@ -657,7 +795,7 @@ mod tests {
);
// underlined
assert_eq!(
Style::from(
Style::from_termwiz(
CellAttributes::default()
.set_underline(Underline::Single)
.to_owned()
@@ -666,12 +804,12 @@ mod tests {
);
// blink
assert_eq!(
Style::from(CellAttributes::default().set_blink(Blink::Slow).to_owned()),
Style::from_termwiz(CellAttributes::default().set_blink(Blink::Slow).to_owned()),
STYLE.slow_blink()
);
// intensity
assert_eq!(
Style::from(
Style::from_termwiz(
CellAttributes::default()
.set_intensity(Intensity::Bold)
.to_owned()
@@ -680,29 +818,29 @@ mod tests {
);
// italic
assert_eq!(
Style::from(CellAttributes::default().set_italic(true).to_owned()),
Style::from_termwiz(CellAttributes::default().set_italic(true).to_owned()),
STYLE.italic()
);
// reversed
assert_eq!(
Style::from(CellAttributes::default().set_reverse(true).to_owned()),
Style::from_termwiz(CellAttributes::default().set_reverse(true).to_owned()),
STYLE.reversed()
);
// strikethrough
assert_eq!(
Style::from(CellAttributes::default().set_strikethrough(true).to_owned()),
Style::from_termwiz(CellAttributes::default().set_strikethrough(true).to_owned()),
STYLE.crossed_out()
);
// hidden
assert_eq!(
Style::from(CellAttributes::default().set_invisible(true).to_owned()),
Style::from_termwiz(CellAttributes::default().set_invisible(true).to_owned()),
STYLE.hidden()
);
// underline color
#[cfg(feature = "underline-color")]
assert_eq!(
Style::from(
Style::from_termwiz(
CellAttributes::default()
.set_underline_color(AnsiColor::Red)
.to_owned()

View File

@@ -28,11 +28,11 @@
//! ```
#[cfg(feature = "crossterm")]
pub use crate::backend::CrosstermBackend;
pub use crate::backend::{CrosstermBackend, FromCrossterm, IntoCrossterm};
#[cfg(all(not(windows), feature = "termion"))]
pub use crate::backend::TermionBackend;
pub use crate::backend::{FromTermion, IntoTermion, TermionBackend};
#[cfg(feature = "termwiz")]
pub use crate::backend::TermwizBackend;
pub use crate::backend::{FromTermwiz, IntoTermwiz, TermwizBackend};
pub use crate::{
backend::{self, Backend},
buffer::{self, Buffer},