pub const BORDER: [char; 8] = ['┌', '─', '┐', '│', '│', '└', '─', '┘'];
pub const ROUND_BORDER: [char; 8] = ['╭', '─', '╮', '│', '│', '╰', '─', '╯'];
use crate::{Result, Yogurt};
pub use crossterm::style::Color;
pub use crossterm::style::ContentStyle as Style;
use crossterm::{
cursor::{CursorShape, DisableBlinking, EnableBlinking, Hide, SetCursorShape, Show},
queue,
style::{Attribute, Attributes, SetStyle},
};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Styler {
foreground: Option<Color>,
background: Option<Color>,
underline: Option<Color>,
attributes: Vec<Attr>,
}
impl Default for Styler {
fn default() -> Self {
Styler::new()
}
}
impl Styler {
pub fn new() -> Self {
Styler {
foreground: None,
background: None,
underline: None,
attributes: Vec::new(),
}
}
pub fn foreground(&mut self, color: Color) -> &mut Self {
self.foreground = Some(color);
self
}
pub fn background(&mut self, color: Color) -> &mut Self {
self.background = Some(color);
self
}
pub fn underline(&mut self, color: Color) -> &mut Self {
self.underline = Some(color);
self
}
pub fn attr(&mut self, attr: Attr) -> &mut Self {
self.attributes.push(attr);
self
}
pub fn style(&mut self) -> Style {
Style {
foreground_color: self.foreground,
background_color: self.background,
underline_color: self.underline,
attributes: Attributes::from(
self.attributes
.iter()
.map(|a| <Attr as Into<Attribute>>::into(*a))
.collect::<Vec<_>>()
.as_slice(),
),
}
}
}
pub fn reset() -> Style {
Styler::new()
.foreground(Color::Reset)
.background(Color::Reset)
.underline(Color::Reset)
.attr(Attr::Reset)
.style()
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum CursorAttr {
UnderScore,
Line,
Block,
Blinking,
Hidden,
}
impl Yogurt {
pub fn cursor_on(&mut self, cursor: CursorAttr) -> Result<()> {
match cursor {
CursorAttr::UnderScore => {
queue! { self.stdout, SetCursorShape(CursorShape::UnderScore) }?
}
CursorAttr::Line => queue! { self.stdout, SetCursorShape(CursorShape::Line) }?,
CursorAttr::Block => queue! { self.stdout, SetCursorShape(CursorShape::Block) }?,
CursorAttr::Blinking => queue! { self.stdout, EnableBlinking }?,
CursorAttr::Hidden => queue! { self.stdout, Hide }?,
}
Ok(())
}
pub fn cursor_off(&mut self, cursor: CursorAttr) -> Result<()> {
match cursor {
CursorAttr::UnderScore | CursorAttr::Line | CursorAttr::Block => {
queue! { self.stdout, SetCursorShape(CursorShape::Block) }?
}
CursorAttr::Blinking => queue! { self.stdout, DisableBlinking }?,
CursorAttr::Hidden => queue! { self.stdout, Show }?,
}
Ok(())
}
pub fn style(&mut self, style: Style) -> Result<()> {
queue! {
self.stdout,
SetStyle(style),
}?;
Ok(())
}
}
macro_rules! Attribute {
(
$(
$(#[$inner:ident $($args:tt)*])*
$name:ident = $sgr:expr,
)*
) => {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum Attr {
$(
$(#[$inner $($args)*])*
$name,
)*
}
impl Attr {
pub fn iter() -> impl Iterator<Item = Attr> {
use self::Attr::*;
[ $($name,)* ].iter().copied()
}
}
}
}
Attribute! {
Reset = 0,
Bold = 1,
Dim = 2,
Italic = 3,
Underlined = 4,
Blink = 5,
Inverse = 7,
Hidden = 8,
Strikethrough = 9,
Overlined = 53,
}
impl From<Attr> for Attribute {
fn from(attr: Attr) -> Self {
use Attr::*;
match attr {
Reset => Attribute::Reset,
Bold => Attribute::Bold,
Dim => Attribute::Dim,
Italic => Attribute::Italic,
Underlined => Attribute::Underlined,
Blink => Attribute::SlowBlink,
Inverse => Attribute::Reverse,
Hidden => Attribute::Hidden,
Strikethrough => Attribute::CrossedOut,
Overlined => Attribute::OverLined,
}
}
}