1#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc(
4 html_logo_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/logo.png",
5 html_favicon_url = "https://raw.githubusercontent.com/ratatui/ratatui/main/assets/favicon.ico"
6)]
7#![warn(missing_docs)]
8#![cfg_attr(feature = "document-features", doc = "\n## Features")]
39#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
40
41use std::error::Error;
42use std::io;
43
44use ratatui_core::backend::{Backend, ClearType, WindowSize};
45use ratatui_core::buffer::Cell;
46use ratatui_core::layout::{Position, Size};
47use ratatui_core::style::{Color, Modifier, Style};
48pub use termwiz;
49use termwiz::caps::Capabilities;
50use termwiz::cell::{AttributeChange, Blink, CellAttributes, Intensity, Underline};
51use termwiz::color::{AnsiColor, ColorAttribute, ColorSpec, LinearRgba, RgbColor, SrgbaTuple};
52use termwiz::surface::{Change, CursorVisibility, Position as TermwizPosition};
53use termwiz::terminal::buffered::BufferedTerminal;
54use termwiz::terminal::{ScreenSize, SystemTerminal, Terminal};
55
56pub struct TermwizBackend {
95 buffered_terminal: BufferedTerminal<SystemTerminal>,
96}
97
98impl TermwizBackend {
99 pub fn new() -> Result<Self, Box<dyn Error>> {
120 let mut buffered_terminal =
121 BufferedTerminal::new(SystemTerminal::new(Capabilities::new_from_env()?)?)?;
122 buffered_terminal.terminal().set_raw_mode()?;
123 buffered_terminal.terminal().enter_alternate_screen()?;
124 Ok(Self { buffered_terminal })
125 }
126
127 pub const fn with_buffered_terminal(instance: BufferedTerminal<SystemTerminal>) -> Self {
129 Self {
130 buffered_terminal: instance,
131 }
132 }
133
134 pub const fn buffered_terminal(&self) -> &BufferedTerminal<SystemTerminal> {
136 &self.buffered_terminal
137 }
138
139 pub const fn buffered_terminal_mut(&mut self) -> &mut BufferedTerminal<SystemTerminal> {
141 &mut self.buffered_terminal
142 }
143}
144
145impl Backend for TermwizBackend {
146 type Error = io::Error;
147
148 fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
149 where
150 I: Iterator<Item = (u16, u16, &'a Cell)>,
151 {
152 for (x, y, cell) in content {
153 self.buffered_terminal.add_changes(vec![
154 Change::CursorPosition {
155 x: TermwizPosition::Absolute(x as usize),
156 y: TermwizPosition::Absolute(y as usize),
157 },
158 Change::Attribute(AttributeChange::Foreground(cell.fg.into_termwiz())),
159 Change::Attribute(AttributeChange::Background(cell.bg.into_termwiz())),
160 ]);
161
162 self.buffered_terminal
163 .add_change(Change::Attribute(AttributeChange::Intensity(
164 if cell.modifier.contains(Modifier::BOLD) {
165 Intensity::Bold
166 } else if cell.modifier.contains(Modifier::DIM) {
167 Intensity::Half
168 } else {
169 Intensity::Normal
170 },
171 )));
172
173 self.buffered_terminal
174 .add_change(Change::Attribute(AttributeChange::Italic(
175 cell.modifier.contains(Modifier::ITALIC),
176 )));
177
178 self.buffered_terminal
179 .add_change(Change::Attribute(AttributeChange::Underline(
180 if cell.modifier.contains(Modifier::UNDERLINED) {
181 Underline::Single
182 } else {
183 Underline::None
184 },
185 )));
186
187 self.buffered_terminal
188 .add_change(Change::Attribute(AttributeChange::Reverse(
189 cell.modifier.contains(Modifier::REVERSED),
190 )));
191
192 self.buffered_terminal
193 .add_change(Change::Attribute(AttributeChange::Invisible(
194 cell.modifier.contains(Modifier::HIDDEN),
195 )));
196
197 self.buffered_terminal
198 .add_change(Change::Attribute(AttributeChange::StrikeThrough(
199 cell.modifier.contains(Modifier::CROSSED_OUT),
200 )));
201
202 self.buffered_terminal
203 .add_change(Change::Attribute(AttributeChange::Blink(
204 if cell.modifier.contains(Modifier::SLOW_BLINK) {
205 Blink::Slow
206 } else if cell.modifier.contains(Modifier::RAPID_BLINK) {
207 Blink::Rapid
208 } else {
209 Blink::None
210 },
211 )));
212
213 self.buffered_terminal.add_change(cell.symbol());
214 }
215 Ok(())
216 }
217
218 fn hide_cursor(&mut self) -> io::Result<()> {
219 self.buffered_terminal
220 .add_change(Change::CursorVisibility(CursorVisibility::Hidden));
221 Ok(())
222 }
223
224 fn show_cursor(&mut self) -> io::Result<()> {
225 self.buffered_terminal
226 .add_change(Change::CursorVisibility(CursorVisibility::Visible));
227 Ok(())
228 }
229
230 fn get_cursor_position(&mut self) -> io::Result<Position> {
231 let (x, y) = self.buffered_terminal.cursor_position();
232 Ok(Position::new(x as u16, y as u16))
233 }
234
235 fn set_cursor_position<P: Into<Position>>(&mut self, position: P) -> io::Result<()> {
236 let Position { x, y } = position.into();
237 self.buffered_terminal.add_change(Change::CursorPosition {
238 x: TermwizPosition::Absolute(x as usize),
239 y: TermwizPosition::Absolute(y as usize),
240 });
241
242 Ok(())
243 }
244
245 fn clear(&mut self) -> io::Result<()> {
246 self.buffered_terminal
247 .add_change(Change::ClearScreen(termwiz::color::ColorAttribute::Default));
248 Ok(())
249 }
250
251 fn clear_region(&mut self, clear_type: ClearType) -> io::Result<()> {
252 match clear_type {
253 ClearType::All => self.clear(),
254 ClearType::AfterCursor
255 | ClearType::BeforeCursor
256 | ClearType::CurrentLine
257 | ClearType::UntilNewLine => Err(io::Error::other(format!(
258 "clear_type [{clear_type:?}] not supported with this backend"
259 ))),
260 }
261 }
262
263 fn size(&self) -> io::Result<Size> {
264 let (cols, rows) = self.buffered_terminal.dimensions();
265 Ok(Size::new(u16_max(cols), u16_max(rows)))
266 }
267
268 fn window_size(&mut self) -> io::Result<WindowSize> {
269 let ScreenSize {
270 cols,
271 rows,
272 xpixel,
273 ypixel,
274 } = self
275 .buffered_terminal
276 .terminal()
277 .get_screen_size()
278 .map_err(io::Error::other)?;
279 Ok(WindowSize {
280 columns_rows: Size {
281 width: u16_max(cols),
282 height: u16_max(rows),
283 },
284 pixels: Size {
285 width: u16_max(xpixel),
286 height: u16_max(ypixel),
287 },
288 })
289 }
290
291 fn flush(&mut self) -> io::Result<()> {
292 self.buffered_terminal.flush().map_err(io::Error::other)?;
293 Ok(())
294 }
295
296 #[cfg(feature = "scrolling-regions")]
297 fn scroll_region_up(&mut self, region: std::ops::Range<u16>, amount: u16) -> io::Result<()> {
298 let (_, rows) = self.buffered_terminal.dimensions();
304 self.buffered_terminal.add_changes(vec![
305 Change::ScrollRegionUp {
306 first_row: region.start as usize,
307 region_size: region.len(),
308 scroll_count: amount as usize,
309 },
310 Change::ScrollRegionUp {
311 first_row: 0,
312 region_size: rows,
313 scroll_count: 0,
314 },
315 ]);
316 Ok(())
317 }
318
319 #[cfg(feature = "scrolling-regions")]
320 fn scroll_region_down(&mut self, region: std::ops::Range<u16>, amount: u16) -> io::Result<()> {
321 let (_, rows) = self.buffered_terminal.dimensions();
327 self.buffered_terminal.add_changes(vec![
328 Change::ScrollRegionDown {
329 first_row: region.start as usize,
330 region_size: region.len(),
331 scroll_count: amount as usize,
332 },
333 Change::ScrollRegionDown {
334 first_row: 0,
335 region_size: rows,
336 scroll_count: 0,
337 },
338 ]);
339 Ok(())
340 }
341}
342
343pub trait FromTermwiz<T> {
348 fn from_termwiz(termwiz: T) -> Self;
350}
351
352pub trait IntoTermwiz<T> {
357 fn into_termwiz(self) -> T;
359}
360
361trait IntoRatatui<R> {
371 fn into_ratatui(self) -> R;
372}
373
374impl<C, R: FromTermwiz<C>> IntoRatatui<R> for C {
375 fn into_ratatui(self) -> R {
376 R::from_termwiz(self)
377 }
378}
379
380impl FromTermwiz<CellAttributes> for Style {
381 fn from_termwiz(value: CellAttributes) -> Self {
382 let mut style = Self::new()
383 .add_modifier(value.intensity().into_ratatui())
384 .add_modifier(value.underline().into_ratatui())
385 .add_modifier(value.blink().into_ratatui());
386
387 if value.italic() {
388 style.add_modifier |= Modifier::ITALIC;
389 }
390 if value.reverse() {
391 style.add_modifier |= Modifier::REVERSED;
392 }
393 if value.strikethrough() {
394 style.add_modifier |= Modifier::CROSSED_OUT;
395 }
396 if value.invisible() {
397 style.add_modifier |= Modifier::HIDDEN;
398 }
399
400 style.fg = Some(value.foreground().into_ratatui());
401 style.bg = Some(value.background().into_ratatui());
402 #[cfg(feature = "underline-color")]
403 {
404 style.underline_color = Some(value.underline_color().into_ratatui());
405 }
406
407 style
408 }
409}
410
411impl FromTermwiz<Intensity> for Modifier {
412 fn from_termwiz(value: Intensity) -> Self {
413 match value {
414 Intensity::Normal => Self::empty(),
415 Intensity::Bold => Self::BOLD,
416 Intensity::Half => Self::DIM,
417 }
418 }
419}
420
421impl FromTermwiz<Underline> for Modifier {
422 fn from_termwiz(value: Underline) -> Self {
423 match value {
424 Underline::None => Self::empty(),
425 _ => Self::UNDERLINED,
426 }
427 }
428}
429
430impl FromTermwiz<Blink> for Modifier {
431 fn from_termwiz(value: Blink) -> Self {
432 match value {
433 Blink::None => Self::empty(),
434 Blink::Slow => Self::SLOW_BLINK,
435 Blink::Rapid => Self::RAPID_BLINK,
436 }
437 }
438}
439
440impl IntoTermwiz<ColorAttribute> for Color {
441 fn into_termwiz(self) -> ColorAttribute {
442 match self {
443 Self::Reset => ColorAttribute::Default,
444 Self::Black => AnsiColor::Black.into(),
445 Self::DarkGray => AnsiColor::Grey.into(),
446 Self::Gray => AnsiColor::Silver.into(),
447 Self::Red => AnsiColor::Maroon.into(),
448 Self::LightRed => AnsiColor::Red.into(),
449 Self::Green => AnsiColor::Green.into(),
450 Self::LightGreen => AnsiColor::Lime.into(),
451 Self::Yellow => AnsiColor::Olive.into(),
452 Self::LightYellow => AnsiColor::Yellow.into(),
453 Self::Magenta => AnsiColor::Purple.into(),
454 Self::LightMagenta => AnsiColor::Fuchsia.into(),
455 Self::Cyan => AnsiColor::Teal.into(),
456 Self::LightCyan => AnsiColor::Aqua.into(),
457 Self::White => AnsiColor::White.into(),
458 Self::Blue => AnsiColor::Navy.into(),
459 Self::LightBlue => AnsiColor::Blue.into(),
460 Self::Indexed(i) => ColorAttribute::PaletteIndex(i),
461 Self::Rgb(r, g, b) => {
462 ColorAttribute::TrueColorWithDefaultFallback(SrgbaTuple::from((r, g, b)))
463 }
464 }
465 }
466}
467
468impl FromTermwiz<AnsiColor> for Color {
469 fn from_termwiz(value: AnsiColor) -> Self {
470 match value {
471 AnsiColor::Black => Self::Black,
472 AnsiColor::Grey => Self::DarkGray,
473 AnsiColor::Silver => Self::Gray,
474 AnsiColor::Maroon => Self::Red,
475 AnsiColor::Red => Self::LightRed,
476 AnsiColor::Green => Self::Green,
477 AnsiColor::Lime => Self::LightGreen,
478 AnsiColor::Olive => Self::Yellow,
479 AnsiColor::Yellow => Self::LightYellow,
480 AnsiColor::Purple => Self::Magenta,
481 AnsiColor::Fuchsia => Self::LightMagenta,
482 AnsiColor::Teal => Self::Cyan,
483 AnsiColor::Aqua => Self::LightCyan,
484 AnsiColor::White => Self::White,
485 AnsiColor::Navy => Self::Blue,
486 AnsiColor::Blue => Self::LightBlue,
487 }
488 }
489}
490
491impl FromTermwiz<ColorAttribute> for Color {
492 fn from_termwiz(value: ColorAttribute) -> Self {
493 match value {
494 ColorAttribute::TrueColorWithDefaultFallback(srgba)
495 | ColorAttribute::TrueColorWithPaletteFallback(srgba, _) => srgba.into_ratatui(),
496 ColorAttribute::PaletteIndex(i) => Self::Indexed(i),
497 ColorAttribute::Default => Self::Reset,
498 }
499 }
500}
501
502impl FromTermwiz<ColorSpec> for Color {
503 fn from_termwiz(value: ColorSpec) -> Self {
504 match value {
505 ColorSpec::Default => Self::Reset,
506 ColorSpec::PaletteIndex(i) => Self::Indexed(i),
507 ColorSpec::TrueColor(srgba) => srgba.into_ratatui(),
508 }
509 }
510}
511
512impl FromTermwiz<SrgbaTuple> for Color {
513 fn from_termwiz(value: SrgbaTuple) -> Self {
514 let (r, g, b, _) = value.to_srgb_u8();
515 Self::Rgb(r, g, b)
516 }
517}
518
519impl FromTermwiz<RgbColor> for Color {
520 fn from_termwiz(value: RgbColor) -> Self {
521 let (r, g, b) = value.to_tuple_rgb8();
522 Self::Rgb(r, g, b)
523 }
524}
525
526impl FromTermwiz<LinearRgba> for Color {
527 fn from_termwiz(value: LinearRgba) -> Self {
528 value.to_srgb().into_ratatui()
529 }
530}
531
532#[inline]
533fn u16_max(i: usize) -> u16 {
534 u16::try_from(i).unwrap_or(u16::MAX)
535}
536
537#[cfg(test)]
538mod tests {
539 use super::*;
540
541 mod into_color {
542 use Color as C;
543
544 use super::*;
545
546 #[test]
547 fn from_linear_rgba() {
548 assert_eq!(
550 C::from_termwiz(LinearRgba(0., 0., 0., 1.)),
551 Color::Rgb(0, 0, 0)
552 );
553 assert_eq!(
555 C::from_termwiz(LinearRgba(0., 0., 0., 0.)),
556 Color::Rgb(0, 0, 0)
557 );
558
559 assert_eq!(
561 C::from_termwiz(LinearRgba(1., 1., 1., 1.)),
562 C::Rgb(254, 254, 254)
563 );
564 assert_eq!(
566 C::from_termwiz(LinearRgba(1., 1., 1., 0.)),
567 C::Rgb(254, 254, 254)
568 );
569
570 assert_eq!(
572 C::from_termwiz(LinearRgba(1., 0., 0., 1.)),
573 C::Rgb(254, 0, 0)
574 );
575 assert_eq!(
577 C::from_termwiz(LinearRgba(0., 1., 0., 1.)),
578 C::Rgb(0, 254, 0)
579 );
580 assert_eq!(
582 C::from_termwiz(LinearRgba(0., 0., 1., 1.)),
583 C::Rgb(0, 0, 254)
584 );
585
586 assert_eq!(
591 C::from_termwiz(LinearRgba(0.214, 0., 0., 1.)),
592 C::Rgb(127, 0, 0)
593 );
594 assert_eq!(
596 C::from_termwiz(LinearRgba(0., 0.214, 0., 1.)),
597 C::Rgb(0, 127, 0)
598 );
599 assert_eq!(
601 C::from_termwiz(LinearRgba(0., 0., 0.214, 1.)),
602 C::Rgb(0, 0, 127)
603 );
604 }
605
606 #[test]
607 fn from_srgba() {
608 assert_eq!(
610 C::from_termwiz(SrgbaTuple(0., 0., 0., 1.)),
611 Color::Rgb(0, 0, 0)
612 );
613 assert_eq!(
615 C::from_termwiz(SrgbaTuple(0., 0., 0., 0.)),
616 Color::Rgb(0, 0, 0)
617 );
618
619 assert_eq!(
621 C::from_termwiz(SrgbaTuple(1., 1., 1., 1.)),
622 C::Rgb(255, 255, 255)
623 );
624 assert_eq!(
626 C::from_termwiz(SrgbaTuple(1., 1., 1., 0.)),
627 C::Rgb(255, 255, 255)
628 );
629
630 assert_eq!(
632 C::from_termwiz(SrgbaTuple(1., 0., 0., 1.)),
633 C::Rgb(255, 0, 0)
634 );
635 assert_eq!(
637 C::from_termwiz(SrgbaTuple(0., 1., 0., 1.)),
638 C::Rgb(0, 255, 0)
639 );
640 assert_eq!(
642 C::from_termwiz(SrgbaTuple(0., 0., 1., 1.)),
643 C::Rgb(0, 0, 255)
644 );
645
646 assert_eq!(
648 C::from_termwiz(SrgbaTuple(0.5, 0., 0., 1.)),
649 C::Rgb(127, 0, 0)
650 );
651 assert_eq!(
653 C::from_termwiz(SrgbaTuple(0., 0.5, 0., 1.)),
654 C::Rgb(0, 127, 0)
655 );
656 assert_eq!(
658 C::from_termwiz(SrgbaTuple(0., 0., 0.5, 1.)),
659 C::Rgb(0, 0, 127)
660 );
661 }
662
663 #[test]
664 fn from_rgbcolor() {
665 assert_eq!(
667 C::from_termwiz(RgbColor::new_8bpc(0, 0, 0)),
668 Color::Rgb(0, 0, 0)
669 );
670 assert_eq!(
672 C::from_termwiz(RgbColor::new_8bpc(255, 255, 255)),
673 C::Rgb(255, 255, 255)
674 );
675
676 assert_eq!(
678 C::from_termwiz(RgbColor::new_8bpc(255, 0, 0)),
679 C::Rgb(255, 0, 0)
680 );
681 assert_eq!(
683 C::from_termwiz(RgbColor::new_8bpc(0, 255, 0)),
684 C::Rgb(0, 255, 0)
685 );
686 assert_eq!(
688 C::from_termwiz(RgbColor::new_8bpc(0, 0, 255)),
689 C::Rgb(0, 0, 255)
690 );
691
692 assert_eq!(
694 C::from_termwiz(RgbColor::new_8bpc(127, 0, 0)),
695 C::Rgb(127, 0, 0)
696 );
697 assert_eq!(
699 C::from_termwiz(RgbColor::new_8bpc(0, 127, 0)),
700 C::Rgb(0, 127, 0)
701 );
702 assert_eq!(
704 C::from_termwiz(RgbColor::new_8bpc(0, 0, 127)),
705 C::Rgb(0, 0, 127)
706 );
707 }
708
709 #[test]
710 fn from_colorspec() {
711 assert_eq!(C::from_termwiz(ColorSpec::Default), C::Reset);
712 assert_eq!(C::from_termwiz(ColorSpec::PaletteIndex(33)), C::Indexed(33));
713 assert_eq!(
714 C::from_termwiz(ColorSpec::TrueColor(SrgbaTuple(0., 0., 0., 1.))),
715 C::Rgb(0, 0, 0)
716 );
717 }
718
719 #[test]
720 fn from_colorattribute() {
721 assert_eq!(C::from_termwiz(ColorAttribute::Default), C::Reset);
722 assert_eq!(
723 C::from_termwiz(ColorAttribute::PaletteIndex(32)),
724 C::Indexed(32)
725 );
726 assert_eq!(
727 C::from_termwiz(ColorAttribute::TrueColorWithDefaultFallback(SrgbaTuple(
728 0., 0., 0., 1.
729 ))),
730 C::Rgb(0, 0, 0)
731 );
732 assert_eq!(
733 C::from_termwiz(ColorAttribute::TrueColorWithPaletteFallback(
734 SrgbaTuple(0., 0., 0., 1.),
735 31
736 )),
737 C::Rgb(0, 0, 0)
738 );
739 }
740
741 #[test]
742 fn from_ansicolor() {
743 assert_eq!(C::from_termwiz(AnsiColor::Black), Color::Black);
744 assert_eq!(C::from_termwiz(AnsiColor::Grey), Color::DarkGray);
745 assert_eq!(C::from_termwiz(AnsiColor::Silver), Color::Gray);
746 assert_eq!(C::from_termwiz(AnsiColor::Maroon), Color::Red);
747 assert_eq!(C::from_termwiz(AnsiColor::Red), Color::LightRed);
748 assert_eq!(C::from_termwiz(AnsiColor::Green), Color::Green);
749 assert_eq!(C::from_termwiz(AnsiColor::Lime), Color::LightGreen);
750 assert_eq!(C::from_termwiz(AnsiColor::Olive), Color::Yellow);
751 assert_eq!(C::from_termwiz(AnsiColor::Yellow), Color::LightYellow);
752 assert_eq!(C::from_termwiz(AnsiColor::Purple), Color::Magenta);
753 assert_eq!(C::from_termwiz(AnsiColor::Fuchsia), Color::LightMagenta);
754 assert_eq!(C::from_termwiz(AnsiColor::Teal), Color::Cyan);
755 assert_eq!(C::from_termwiz(AnsiColor::Aqua), Color::LightCyan);
756 assert_eq!(C::from_termwiz(AnsiColor::White), Color::White);
757 assert_eq!(C::from_termwiz(AnsiColor::Navy), Color::Blue);
758 assert_eq!(C::from_termwiz(AnsiColor::Blue), Color::LightBlue);
759 }
760 }
761
762 mod into_modifier {
763 use super::*;
764
765 #[test]
766 fn from_intensity() {
767 assert_eq!(Modifier::from_termwiz(Intensity::Normal), Modifier::empty());
768 assert_eq!(Modifier::from_termwiz(Intensity::Bold), Modifier::BOLD);
769 assert_eq!(Modifier::from_termwiz(Intensity::Half), Modifier::DIM);
770 }
771
772 #[test]
773 fn from_underline() {
774 assert_eq!(Modifier::from_termwiz(Underline::None), Modifier::empty());
775 assert_eq!(
776 Modifier::from_termwiz(Underline::Single),
777 Modifier::UNDERLINED
778 );
779 assert_eq!(
780 Modifier::from_termwiz(Underline::Double),
781 Modifier::UNDERLINED
782 );
783 assert_eq!(
784 Modifier::from_termwiz(Underline::Curly),
785 Modifier::UNDERLINED
786 );
787 assert_eq!(
788 Modifier::from_termwiz(Underline::Dashed),
789 Modifier::UNDERLINED
790 );
791 assert_eq!(
792 Modifier::from_termwiz(Underline::Dotted),
793 Modifier::UNDERLINED
794 );
795 }
796
797 #[test]
798 fn from_blink() {
799 assert_eq!(Modifier::from_termwiz(Blink::None), Modifier::empty());
800 assert_eq!(Modifier::from_termwiz(Blink::Slow), Modifier::SLOW_BLINK);
801 assert_eq!(Modifier::from_termwiz(Blink::Rapid), Modifier::RAPID_BLINK);
802 }
803 }
804
805 #[test]
806 fn from_cell_attribute_for_style() {
807 #[cfg(feature = "underline-color")]
808 const STYLE: Style = Style::new()
809 .underline_color(Color::Reset)
810 .fg(Color::Reset)
811 .bg(Color::Reset);
812 #[cfg(not(feature = "underline-color"))]
813 const STYLE: Style = Style::new().fg(Color::Reset).bg(Color::Reset);
814
815 assert_eq!(Style::from_termwiz(CellAttributes::default()), STYLE);
817
818 assert_eq!(
820 Style::from_termwiz(
821 CellAttributes::default()
822 .set_foreground(ColorAttribute::PaletteIndex(31))
823 .to_owned()
824 ),
825 STYLE.fg(Color::Indexed(31))
826 );
827 assert_eq!(
829 Style::from_termwiz(
830 CellAttributes::default()
831 .set_background(ColorAttribute::PaletteIndex(31))
832 .to_owned()
833 ),
834 STYLE.bg(Color::Indexed(31))
835 );
836 assert_eq!(
838 Style::from_termwiz(
839 CellAttributes::default()
840 .set_underline(Underline::Single)
841 .to_owned()
842 ),
843 STYLE.underlined()
844 );
845 assert_eq!(
847 Style::from_termwiz(CellAttributes::default().set_blink(Blink::Slow).to_owned()),
848 STYLE.slow_blink()
849 );
850 assert_eq!(
852 Style::from_termwiz(
853 CellAttributes::default()
854 .set_intensity(Intensity::Bold)
855 .to_owned()
856 ),
857 STYLE.bold()
858 );
859 assert_eq!(
861 Style::from_termwiz(CellAttributes::default().set_italic(true).to_owned()),
862 STYLE.italic()
863 );
864 assert_eq!(
866 Style::from_termwiz(CellAttributes::default().set_reverse(true).to_owned()),
867 STYLE.reversed()
868 );
869 assert_eq!(
871 Style::from_termwiz(CellAttributes::default().set_strikethrough(true).to_owned()),
872 STYLE.crossed_out()
873 );
874 assert_eq!(
876 Style::from_termwiz(CellAttributes::default().set_invisible(true).to_owned()),
877 STYLE.hidden()
878 );
879
880 #[cfg(feature = "underline-color")]
882 assert_eq!(
883 Style::from_termwiz(
884 CellAttributes::default()
885 .set_underline_color(AnsiColor::Red)
886 .to_owned()
887 ),
888 STYLE.underline_color(Color::Indexed(9))
889 );
890 }
891}