termit 0.7.0

Terminal UI over crossterm
Documentation
//! Compatibility implementations for crossterm

use crate::input::events::AppEvent;
use crate::input::events::Event;
use crate::output::ansi::AnsiConfig;
use crate::output::ansi::AnsiPrintConfigured;
use crate::output::style::Pretty;
use crate::output::style::Stylist;
use crate::prelude::Window;
use crate::widget::Widget;
use crate::Painter;
use crate::Screen;
use crossterm::style::Attribute;
use crossterm::style::Stylize;
use crossterm::Command;
use std::borrow::Cow;
use std::fmt::Display;

impl<T> Command for crate::output::ansi::AnsiPrint<T>
where
    T: AsRef<str>,
{
    fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
        let ansi = AnsiPrintConfigured(&self.0, AnsiConfig::default());
        write!(f, "{ansi}")
    }
    #[cfg(windows)]
    fn execute_winapi(&self) -> crossterm::Result<()> {
        todo!()
    }
}

impl<T> Command for crate::output::ansi::AnsiPrintConfigured<T>
where
    crate::output::ansi::AnsiPrintConfigured<T>: Display,
{
    fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
        write!(f, "{self}")
    }
    #[cfg(windows)]
    fn execute_winapi(&self) -> crossterm::Result<()> {
        todo!()
    }
}

impl From<crate::output::color::Color> for crossterm::style::Color {
    fn from(val: crate::output::color::Color) -> Self {
        let (r, g, b) = val.to_24b();
        crossterm::style::Color::Rgb { r, g, b }
    }
}
impl From<crossterm::style::Color> for crate::output::color::Color {
    fn from(value: crossterm::style::Color) -> Self {
        use crate::output::color::Color;
        match value {
            crossterm::style::Color::Reset => Color::black(),
            crossterm::style::Color::Black => Color::black(),
            crossterm::style::Color::DarkGrey => Color::grey(false),
            crossterm::style::Color::Red => Color::red(true),
            crossterm::style::Color::DarkRed => Color::red(false),
            crossterm::style::Color::Green => Color::green(true),
            crossterm::style::Color::DarkGreen => Color::green(false),
            crossterm::style::Color::Yellow => Color::yellow(true),
            crossterm::style::Color::DarkYellow => Color::yellow(false),
            crossterm::style::Color::Blue => Color::blue(true),
            crossterm::style::Color::DarkBlue => Color::blue(false),
            crossterm::style::Color::Magenta => Color::magenta(true),
            crossterm::style::Color::DarkMagenta => Color::magenta(false),
            crossterm::style::Color::Cyan => Color::cyan(true),
            crossterm::style::Color::DarkCyan => Color::cyan(false),
            crossterm::style::Color::White => Color::white(),
            crossterm::style::Color::Grey => Color::grey(true),
            crossterm::style::Color::Rgb { r, g, b } => Color::rgb(r, g, b),
            crossterm::style::Color::AnsiValue(ansi) => Color::ansi(ansi),
        }
    }
}

impl From<&crossterm::style::ContentStyle> for crate::output::style::Style {
    fn from(style: &crossterm::style::ContentStyle) -> Self {
        use crate::output::color::Color;
        crate::output::style::Style::DEFAULT
            .front(style.foreground_color.map(Color::from))
            .back(style.background_color.map(Color::from))
            .frame(style.underline_color.map(Color::from))
            .underlined(style.attributes.has(Attribute::Underlined))
            .bold(style.attributes.has(Attribute::Bold))
            .italic(style.attributes.has(Attribute::Italic))
            .blink(
                style.attributes.has(Attribute::SlowBlink)
                    || style.attributes.has(Attribute::RapidBlink),
            )
    }
}
impl From<&crate::output::style::Style> for crossterm::style::ContentStyle {
    fn from(val: &crate::output::style::Style) -> Self {
        let mut style = crossterm::style::ContentStyle::default();
        if let Some(c) = val.front_color {
            style = style.with(c.into());
        }
        if let Some(c) = val.back_color {
            style = style.on(c.into());
        }
        if let Some(c) = val.frame_color {
            style = style.underline(c.into());
        }
        if val.underlined.unwrap_or_default() {
            style.attributes.set(Attribute::Underlined)
        }
        if val.crossed_out.unwrap_or_default() {
            style.attributes.set(Attribute::CrossedOut)
        }
        if val.bold.unwrap_or_default() {
            style.attributes.set(Attribute::Bold)
        }
        if val.italic.unwrap_or_default() {
            style.attributes.set(Attribute::Italic)
        }
        if val.blink.unwrap_or_default() {
            style.attributes.set(Attribute::SlowBlink)
        }
        style
    }
}

impl From<crossterm::style::ContentStyle> for crate::output::ansi::ScreenStyle {
    fn from(style: crossterm::style::ContentStyle) -> Self {
        let style = crate::output::style::Style::from(&style);
        crate::output::ansi::ScreenStyle::from(&style)
    }
}

impl<'a> From<crossterm::style::StyledContent<&'a str>> for Pretty<Cow<'a, str>> {
    fn from(content: crossterm::style::StyledContent<&'a str>) -> Self {
        let style = content.style();
        let content = content.content().clone();
        Pretty::new(Cow::Borrowed(content)).apply(crate::output::style::Style::from(style))
    }
}
impl<'a> From<crossterm::style::StyledContent<String>> for Pretty<Cow<'a, str>> {
    fn from(content: crossterm::style::StyledContent<String>) -> Self {
        let style = crate::output::style::Style::from(content.style());
        let content = content.content().clone();
        let content: Cow<'a, str> = Cow::Owned(content);
        Pretty::new(content).apply(style)
    }
}

/// Widget for displaying styled content
impl<M, C, A: AppEvent> Widget<M, A> for crossterm::style::StyledContent<C>
where
    C: AsRef<str>,
    C: std::fmt::Display,
{
    fn update(
        &mut self,
        _model: &mut M,
        _input: &Event<A>,
        screen: &mut Screen,
        painter: &Painter,
    ) -> Window {
        painter
            .apply(crate::output::style::Style::from(self.style()))
            .paint(self.content().as_ref(), screen, 0, false)
    }
}