use std::{fmt, ops};
use std::rc::Rc;
use crate::console::{Component, SendableComponent};
fn rgba_to_ansi(rgba: u32) -> String {
let r = (rgba >> 16) & 0xFF;
let g = (rgba >> 8) & 0xFF;
let b = rgba & 0xFF;
format!("38;2;{};{};{}", r, g, b)
}
#[derive(Clone)]
pub struct TextComponentData {
color: Option<u32>,
text: String,
parts: Vec<Box<dyn SendableComponent>>,
}
#[derive(Clone)]
pub struct TextComponent(pub Rc<TextComponentData>);
impl TextComponent {
pub fn new(text: &str) -> TextComponent {
TextComponent(Rc::new(TextComponentData {
color: None,
text: text.to_string(),
parts: Vec::new(),
}))
}
pub fn with_color(self, color: u32) -> TextComponent {
TextComponent(Rc::new(TextComponentData {
color: Some(color),
text: self.0.text.clone(),
parts: self.0.parts.clone(),
}))
}
}
impl fmt::Display for TextComponent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(color) = &self.0.color {
let ansi_color = rgba_to_ansi(*color);
write!(f, "\x1b[{}m", ansi_color)?;
write!(f, "{}", self.0.text)?;
write!(f, "\x1b[0m")?;
} else {
write!(f, "{}", self.0.text)?;
}
for part in &self.0.parts {
write!(f, "{}", part)?;
}
Ok(())
}
}
impl SendableComponent for TextComponent {
fn clone_box(&self) -> Box<dyn SendableComponent> {
Box::new(self.clone())
}
}
impl Component {
pub fn empty() -> TextComponent {
TextComponent::new("")
}
pub fn text(text: &str) -> TextComponent {
TextComponent::new(text)
}
}
impl ops::Add for TextComponent {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
let mut new_parts = self.0.parts.clone();
new_parts.push(Box::new(other.clone()));
TextComponent(Rc::new(TextComponentData {
color: self.0.color,
text: self.0.text.clone(),
parts: new_parts,
}))
}
}