use std::hash::{DefaultHasher, Hash, Hasher};
use crate::{
buffer::Buffer,
enums::{Modifier, Wrap},
prelude::{TextAlign, Vec2},
style::{Style, Styleable},
text::{Line, Text, TextParser, text_render},
widgets::{Element, LayoutNode, Widget},
};
mod to_span;
pub use to_span::ToSpan;
#[derive(Debug)]
pub struct Span {
text: String,
style: Style,
align: TextAlign,
wrap: Wrap,
ellipsis: String,
}
impl Span {
#[must_use]
pub fn new<T>(text: T) -> Self
where
T: AsRef<str>,
{
Self {
text: text.as_ref().to_string(),
..Default::default()
}
}
#[must_use]
pub fn style<T>(mut self, style: T) -> Self
where
T: Into<Style>,
{
self.style = style.into();
self
}
#[must_use]
pub fn modifier(mut self, modifier: Modifier) -> Self {
self.style = self.style.modifier(modifier);
self
}
#[must_use]
pub fn add_modifier(mut self, flag: Modifier) -> Self {
self.style = self.style.add_modifier(flag);
self
}
#[must_use]
pub fn remove_modifier(mut self, flag: Modifier) -> Self {
self.style = self.style.remove_modifier(flag);
self
}
#[must_use]
pub fn align(mut self, align: TextAlign) -> Self {
self.align = align;
self
}
#[must_use]
pub fn wrap(mut self, wrap: Wrap) -> Self {
self.wrap = wrap;
self
}
#[must_use]
pub fn ellipsis<T>(mut self, ellipsis: T) -> Self
where
T: AsRef<str>,
{
self.ellipsis = ellipsis.as_ref().to_string();
self
}
}
impl<M: Clone + 'static> Widget<M> for Span {
fn render(&self, buffer: &mut Buffer, layout: &LayoutNode) {
text_render(self, buffer, layout.area, &self.ellipsis, self.align);
}
fn height(&self, size: &Vec2) -> usize {
let mut parser = TextParser::new(&self.text).wrap(self.wrap);
parser.height(size)
}
fn width(&self, size: &Vec2) -> usize {
let mut parser = TextParser::new(&self.text).wrap(self.wrap);
parser.width(size)
}
fn layout_hash(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.text.hash(&mut hasher);
self.wrap.hash(&mut hasher);
hasher.finish()
}
}
impl Text for Span {
fn append_lines<'a>(
&'a self,
lines: &mut Vec<Line<'a>>,
size: &Vec2,
wrap: Option<Wrap>,
) -> bool {
let wrap = wrap.unwrap_or(self.wrap);
let mut parser = TextParser::new(&self.text).wrap(wrap);
let height = lines.len().saturating_sub(1);
let is_end = parser.is_end();
if size.x == 0 || height >= size.y || is_end {
return is_end;
}
let mut line = lines.pop().unwrap_or_else(Line::empty);
for _ in height..size.y {
let line_len = size.x.saturating_sub(line.width);
let Some((text, len)) = parser.next_line(line_len) else {
break;
};
line.push(text, len, self.style);
lines.push(line);
line = Line::empty();
}
parser.is_end()
}
fn get_align(&self) -> TextAlign {
self.align
}
}
impl Default for Span {
fn default() -> Self {
Self {
text: Default::default(),
style: Default::default(),
align: Default::default(),
wrap: Default::default(),
ellipsis: "...".to_string(),
}
}
}
impl Styleable for Span {
fn style_mut(&mut self) -> &mut Style {
&mut self.style
}
}
impl<T> From<T> for Span
where
T: AsRef<str>,
{
fn from(value: T) -> Self {
Span::new(value)
}
}
impl<M: Clone + 'static, T> From<T> for Box<dyn Widget<M>>
where
T: AsRef<str>,
{
fn from(value: T) -> Self {
Box::new(Span::new(value.as_ref()))
}
}
impl<T> From<T> for Box<dyn Text>
where
T: AsRef<str>,
{
fn from(value: T) -> Self {
Box::new(Span::new(value))
}
}
impl<M, T> From<T> for Element<M>
where
M: Clone + 'static,
T: AsRef<str>,
{
fn from(value: T) -> Self {
Element::new(Span::new(value))
}
}
impl<M: Clone + 'static> From<Span> for Box<dyn Widget<M>> {
fn from(value: Span) -> Self {
Box::new(value)
}
}
impl From<Span> for Box<dyn Text> {
fn from(value: Span) -> Self {
Box::new(value)
}
}
impl<M: Clone + 'static> From<Span> for Element<M> {
fn from(value: Span) -> Self {
Element::new(value)
}
}