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