281 lines
6.8 KiB
Rust
281 lines
6.8 KiB
Rust
use crossterm::{
|
|
queue,
|
|
style::{
|
|
Attribute, Color, Print, ResetColor, SetAttribute, SetBackgroundColor, SetForegroundColor,
|
|
},
|
|
};
|
|
|
|
/// Rune repesents the state of the screen at a specific position. It stores
|
|
/// the character content and styling information that will be rendered.
|
|
#[derive(Clone, Copy, Default, Eq, PartialEq)]
|
|
pub struct Rune {
|
|
pub content: Option<char>,
|
|
pub fg: Option<Color>,
|
|
pub bg: Option<Color>,
|
|
pub bold: bool,
|
|
}
|
|
|
|
impl std::fmt::Debug for Rune {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct(&format!("Rn({})", self.content.unwrap_or_default()))
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl std::ops::Add<Rune> for Rune {
|
|
type Output = Rune;
|
|
|
|
fn add(self, mut rhs: Rune) -> Self::Output {
|
|
rhs.fg = rhs.fg.or(self.fg);
|
|
rhs.bg = rhs.bg.or(self.bg);
|
|
rhs
|
|
}
|
|
}
|
|
|
|
impl From<char> for Rune {
|
|
fn from(value: char) -> Self {
|
|
Rune {
|
|
content: Some(value),
|
|
..Default::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Color> for Rune {
|
|
fn from(value: Color) -> Self {
|
|
Rune {
|
|
content: Some(' '),
|
|
bg: Some(value),
|
|
..Default::default()
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Rune {
|
|
/// Create a new empty Rune. This can be used with the settings functions as a _builder_ pattern
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let rune:Rune = Rune::new().bg(Color::Blue).fg(Color::White).bold();
|
|
/// ```
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
/// Set the content of the rune. The rune's content is a single character.
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let rune = Rune::new().content('A');
|
|
/// assert_eq!(rune.content, Some('A'));
|
|
/// ```
|
|
pub fn content(mut self, content: char) -> Self {
|
|
self.content = Some(content);
|
|
self
|
|
}
|
|
|
|
/// Set the background color of the rune.
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let rune = Rune::new().bg(Color::Green);
|
|
/// assert_eq!(rune.bg, Some(Color::Green));
|
|
/// ```
|
|
pub fn bg(mut self, bg: Color) -> Self {
|
|
self.bg = Some(bg);
|
|
self
|
|
}
|
|
|
|
/// Set the text color of the rune.
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let rune = Rune::new().fg(Color::Green);
|
|
/// assert_eq!(rune.fg, Some(Color::Green));
|
|
/// ```
|
|
pub fn fg(mut self, fg: Color) -> Self {
|
|
self.fg = Some(fg);
|
|
self
|
|
}
|
|
|
|
/// Set the text color of the rune.
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let rune = Rune::new().fg(Color::Green);
|
|
/// assert_eq!(rune.fg, Some(Color::Green));
|
|
/// ```
|
|
pub fn bold(mut self) -> Self {
|
|
self.bold = true;
|
|
self
|
|
}
|
|
|
|
/// Renders a Print command into the terminal's output queue
|
|
pub(crate) fn render<W>(self, out: &mut W) -> anyhow::Result<()>
|
|
where
|
|
W: std::io::Write,
|
|
{
|
|
if let Some(content) = self.content {
|
|
queue!(out, ResetColor)?;
|
|
if let Some(c) = self.fg {
|
|
queue!(out, SetForegroundColor(c))?;
|
|
}
|
|
if let Some(c) = self.bg {
|
|
queue!(out, SetBackgroundColor(c))?;
|
|
}
|
|
if self.bold {
|
|
queue!(out, SetAttribute(Attribute::Bold))?;
|
|
}
|
|
queue!(out, Print(content))?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Runes represents a series of runes. This is generally used to convert
|
|
/// strings into Runes and apply styling information to them.
|
|
///
|
|
/// Building runes from a string:
|
|
///
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let runes = "This is a test string".to_runes().fg(Color::White);
|
|
/// ```
|
|
#[derive(Clone, Debug, Default)]
|
|
pub struct Runes(pub(crate) Vec<Rune>);
|
|
|
|
impl std::ops::Deref for Runes {
|
|
type Target = Vec<Rune>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl From<Rune> for Runes {
|
|
fn from(value: Rune) -> Self {
|
|
Runes::new(vec![value])
|
|
}
|
|
}
|
|
|
|
impl<T: ToString> From<T> for Runes {
|
|
fn from(value: T) -> Self {
|
|
Runes(
|
|
value
|
|
.to_string()
|
|
.chars()
|
|
.map(|c| Rune::new().content(c))
|
|
.collect(),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl Runes {
|
|
/// Create a new runes collection from a vector of Rune.
|
|
pub fn new(runes: Vec<Rune>) -> Self {
|
|
Self(runes)
|
|
}
|
|
/// Set the text color of the rune.
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let runes = "blue".to_runes().fg(Color::Blue);
|
|
/// assert!(runes.iter().all(|r| r.fg == Some(Color::Blue)))
|
|
/// ```
|
|
pub fn fg(mut self, color: Color) -> Self {
|
|
for r in self.0.iter_mut() {
|
|
r.fg = Some(color);
|
|
}
|
|
self
|
|
}
|
|
|
|
/// Set the text color of the rune.
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let mut runes = "on blue".to_runes().bg(Color::Blue);
|
|
/// let runes = runes.clear_fg();
|
|
/// assert!(runes.iter().all(|r| r.fg == None))
|
|
pub fn clear_fg(mut self) -> Self {
|
|
for r in self.0.iter_mut() {
|
|
r.fg = None;
|
|
}
|
|
self
|
|
}
|
|
|
|
/// Set the text color of the rune.
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let runes = "on blue".to_runes().bg(Color::Blue);
|
|
/// assert!(runes.iter().all(|r| r.bg == Some(Color::Blue)))
|
|
/// ```
|
|
pub fn bg(mut self, color: Color) -> Self {
|
|
for r in self.0.iter_mut() {
|
|
r.bg = Some(color);
|
|
}
|
|
self
|
|
}
|
|
|
|
/// Set the text color of the rune.
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let mut runes = "on blue".to_runes().bg(Color::Blue);
|
|
/// let runes = runes.clear_bg();
|
|
/// assert!(runes.iter().all(|r| r.bg == None))
|
|
pub fn clear_bg(mut self) -> Self {
|
|
for r in self.0.iter_mut() {
|
|
r.bg = None;
|
|
}
|
|
self
|
|
}
|
|
|
|
pub fn bold(mut self) -> Self {
|
|
for r in self.0.iter_mut() {
|
|
r.bold = true;
|
|
}
|
|
self
|
|
}
|
|
|
|
/// Append runes or a string displayable object to the Runes
|
|
///
|
|
/// Example:
|
|
/// ```
|
|
/// use arkham::prelude::*;
|
|
/// let mut runes = "This is a test string. ".to_runes();
|
|
/// runes.add("This is a colored string".to_runes().fg(Color::Blue));
|
|
/// runes.add("This is another basic string");
|
|
pub fn add<R>(&mut self, runes: R)
|
|
where
|
|
R: Into<Runes>,
|
|
{
|
|
self.0.append(&mut runes.into().0);
|
|
}
|
|
}
|
|
|
|
pub trait ToRuneExt {
|
|
fn to_runes(&self) -> Runes;
|
|
}
|
|
|
|
impl ToRuneExt for String {
|
|
fn to_runes(&self) -> Runes {
|
|
Runes::from(self.to_string())
|
|
}
|
|
}
|
|
|
|
impl ToRuneExt for &str {
|
|
fn to_runes(&self) -> Runes {
|
|
Runes::from(self.to_string())
|
|
}
|
|
}
|