use crate::{color::Color, unit::*, Style, StyleUpdater};
use derive_rich::Rich;
use std::{borrow::Cow, fmt};
#[derive(Rich, Clone, Debug, PartialEq, Default)]
pub struct Text {
#[rich(write, write(option))]
pub color: Option<Color>,
#[rich(write, write(option))]
pub direction: Option<Direction>,
#[rich(write, write(option))]
pub letter_spacing: Option<LetterSpacing>,
#[rich(write, write(option))]
pub word_spacing: Option<WordSpacing>,
#[rich(write, write(option))]
pub line_height: Option<LineHeight>,
#[rich(write, write(option))]
pub align: Option<TextAlign>,
#[rich(write, write(option))]
pub align_last: Option<TextAlignLast>,
#[rich(write, write(option))]
pub justify: Option<TextJustify>,
#[rich(write, write(option), write(style = compose))]
pub shadow: Option<TextShadow>,
#[rich(write, write(option))]
pub indent: Option<TextIndent>,
#[rich(write, write(option), write(style = compose))]
pub decoration: Option<TextDecoration>,
#[rich(write, write(option))]
pub white_space: Option<WhiteSpace>,
#[rich(write, write(option))]
pub unicode_bidi: Option<UnicodeBidi>,
#[rich(write, write(option))]
pub transform: Option<TextTransform>,
#[rich(write, write(option))]
pub overflow: Option<TextOverflow>,
#[rich(write, write(option))]
pub vertical_align: Option<VerticalAlign>,
#[rich(write, write(option))]
pub writing_mode: Option<WritingMode>,
#[rich(write, write(option))]
pub word_wrap: Option<WordWrap>,
#[rich(write, write(option))]
pub word_break: Option<WordBreak>,
}
impl StyleUpdater for Text {
fn update_style(self, style: Style) -> Style {
style
.try_insert("color", self.color)
.try_insert("direction", self.direction)
.try_insert("letter-spacing", self.letter_spacing)
.try_insert("word-spacing", self.word_spacing)
.try_insert("line-height", self.line_height)
.try_insert("text-align", self.align)
.try_insert("text-align-last", self.align_last)
.try_insert("text-justify", self.justify)
.try_merge(self.shadow)
.try_insert("text-indent", self.indent)
.try_insert("text-decoration", self.decoration)
.try_insert("white-space", self.white_space)
.try_insert("unicode-bidi", self.unicode_bidi)
.try_insert("text-transform", self.transform)
.try_insert("text-overflow", self.overflow.clone())
.try_insert("vertical-align", self.vertical_align)
.try_insert("writing-mode", self.writing_mode)
.try_insert("word-wrap", self.word_wrap)
.try_insert("word-break", self.word_break)
}
}
impl<T: Into<Color>> From<T> for Text {
fn from(source: T) -> Self {
Self::default().color(source.into())
}
}
#[derive(Clone, Debug, PartialEq, From, Display)]
pub enum TextShadow {
One(Shadow),
#[display(
fmt = "{}",
"_0.iter().map(|s| s.to_string()).collect::<Vec<_>>().join(\", \")"
)]
Multiple(Vec<Shadow>),
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
#[display(fmt = "none")]
None,
#[display(fmt = "unset")]
Unset,
}
impl StyleUpdater for TextShadow {
fn update_style(self, style: Style) -> Style {
style.insert("text-shadow", self)
}
}
impl Default for TextShadow {
fn default() -> Self {
TextShadow::Multiple(vec![])
}
}
impl TextShadow {
fn shadow(mut self, conf: impl FnOnce(Shadow) -> Shadow) -> Self {
self = match self {
Self::One(shadow) => Self::One(conf(shadow)),
Self::Multiple(shadows) => {
Self::One(conf(shadows.into_iter().next().unwrap_or_default()))
}
_ => Self::One(conf(Shadow::default())),
};
self
}
pub fn new() -> Self {
TextShadow::default()
}
pub fn x(self, val: impl Into<Length>) -> Self {
self.shadow(|sh| sh.x(val))
}
pub fn y(self, val: impl Into<Length>) -> Self {
self.shadow(|sh| sh.y(val))
}
pub fn blur(self, val: impl Into<Length>) -> Self {
self.shadow(|sh| sh.blur(val))
}
pub fn try_blur(self, val: Option<impl Into<Length>>) -> Self {
self.shadow(|sh| sh.try_blur(val))
}
pub fn color(self, val: impl Into<Color>) -> Self {
self.shadow(|sh| sh.color(val))
}
pub fn try_color(self, val: Option<impl Into<Color>>) -> Self {
self.shadow(|sh| sh.try_color(val))
}
pub fn push(mut self, get_val: impl FnOnce(Shadow) -> Shadow) -> Self {
let val = get_val(Shadow::default());
self = match self {
Self::Multiple(mut vec) => {
vec.push(val);
Self::Multiple(vec)
}
_ => Self::Multiple(vec![val]),
};
self
}
}
#[derive(Rich, Clone, Debug, PartialEq)]
pub struct Shadow {
#[rich(write)]
x: Length,
#[rich(write)]
y: Length,
#[rich(write, write(option))]
blur: Option<Length>,
#[rich(write, write(option))]
color: Option<Color>,
}
impl fmt::Display for Shadow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.x, self.y)?;
if let Some(blur) = self.blur.as_ref() {
write!(f, " {}", blur)?;
}
if let Some(color) = self.color.as_ref() {
write!(f, " {}", color)?;
}
Ok(())
}
}
impl Default for Shadow {
fn default() -> Self {
Self {
x: px(0),
y: px(0),
blur: None,
color: None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum Direction {
#[display(fmt = "ltr")]
Ltr,
#[display(fmt = "rtl")]
Rtl,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Spacing {
#[display(fmt = "normal")]
Normal,
Length(Length),
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
pub type LetterSpacing = Spacing;
pub type WordSpacing = Spacing;
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum LineHeight {
#[display(fmt = "normal")]
Normal,
Number(f32),
Length(Length),
Percent(Percent),
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextAlign {
#[display(fmt = "start")]
Start,
#[display(fmt = "end")]
End,
#[display(fmt = "left")]
Left,
#[display(fmt = "right")]
Right,
#[display(fmt = "center")]
Center,
#[display(fmt = "justify")]
Justify,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
fn display_helper(value: &Option<impl ToString>) -> String {
value
.as_ref()
.map(|v| v.to_string() + " ")
.unwrap_or_else(|| "".into())
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecoration {
#[display(
fmt = "{}{}{}",
"display_helper(line)",
"display_helper(color)",
"display_helper(style).trim()"
)]
Decoration {
line: Option<TextDecorationLine>,
color: Option<TextDecorationColor>,
style: Option<TextDecorationStyle>,
},
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
impl Default for TextDecoration {
fn default() -> Self {
TextDecoration::Initial
}
}
impl TextDecoration {
pub fn line(mut self, value: impl Into<TextDecorationLine>) -> Self {
match self {
Self::Decoration { ref mut line, .. } => *line = Some(value.into()),
_ => {
self = Self::Decoration {
line: Some(value.into()),
color: None,
style: None,
}
}
};
self
}
pub fn line_none(self) -> Self {
self.line(TextDecorationLine::None)
}
pub fn line_underline(self) -> Self {
self.line(TextDecorationLine::Underline)
}
pub fn line_overline(self) -> Self {
self.line(TextDecorationLine::Overline)
}
pub fn line_line_through(self) -> Self {
self.line(TextDecorationLine::LineThrough)
}
pub fn color(mut self, value: impl Into<TextDecorationColor>) -> Self {
match self {
Self::Decoration { ref mut color, .. } => *color = Some(value.into()),
_ => {
self = Self::Decoration {
line: Some(TextDecorationLine::None),
color: Some(value.into()),
style: None,
}
}
};
self
}
pub fn style(mut self, value: impl Into<TextDecorationStyle>) -> Self {
match self {
Self::Decoration { ref mut style, .. } => *style = Some(value.into()),
_ => {
self = Self::Decoration {
line: Some(TextDecorationLine::None),
color: None,
style: Some(value.into()),
}
}
};
self
}
pub fn style_solid(self) -> Self {
self.style(TextDecorationStyle::Solid)
}
pub fn style_dashed(self) -> Self {
self.style(TextDecorationStyle::Dashed)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecorationLine {
#[display(fmt = "none")]
None,
#[display(fmt = "underline")]
Underline,
#[display(fmt = "overline")]
Overline,
#[display(fmt = "line-through")]
LineThrough,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecorationColor {
Color(Color),
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecorationStyle {
#[display(fmt = "solid")]
Solid,
#[display(fmt = "double")]
Double,
#[display(fmt = "dotted")]
Dotted,
#[display(fmt = "dashed")]
Dashed,
#[display(fmt = "wavy")]
Wavy,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum TextIndent {
Length(Length),
Percent(Percent),
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextTransform {
#[display(fmt = "none")]
None,
#[display(fmt = "capitalize")]
Capitalize,
#[display(fmt = "uppercase")]
Uppercase,
#[display(fmt = "lowercase")]
Lowercase,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum TextOverflow {
#[display(fmt = "clip")]
Clip,
#[display(fmt = "ellipsis")]
Ellipsis,
String(Cow<'static, str>),
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum UnicodeBidi {
#[display(fmt = "normal")]
Normal,
#[display(fmt = "embed")]
Embed,
#[display(fmt = "bidi-override")]
BidiOverride,
#[display(fmt = "isolate")]
Isolate,
#[display(fmt = "isolate-override")]
IsolateOverride,
#[display(fmt = "plaintext")]
Plaintext,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum VerticalAlign {
#[display(fmt = "baseline")]
Baseline,
#[display(fmt = "sub")]
Sub,
#[display(fmt = "super")]
Super,
#[display(fmt = "top")]
Top,
#[display(fmt = "text-top")]
TextTop,
#[display(fmt = "middle")]
Middle,
#[display(fmt = "bottom")]
Bottom,
#[display(fmt = "text-bottom")]
TextBottom,
Length(Length),
Percent(Percent),
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WhiteSpace {
#[display(fmt = "normal")]
Normal,
#[display(fmt = "nowrap")]
Nowrap,
#[display(fmt = "pre")]
Pre,
#[display(fmt = "pre-line")]
PreLine,
#[display(fmt = "pre-wrap")]
PreWrap,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextAlignLast {
#[display(fmt = "auto")]
Auto,
#[display(fmt = "left")]
Left,
#[display(fmt = "right")]
Right,
#[display(fmt = "center")]
Center,
#[display(fmt = "justify")]
Justify,
#[display(fmt = "start")]
Start,
#[display(fmt = "end")]
End,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextJustify {
#[display(fmt = "auto")]
Auto,
#[display(fmt = "inter-word")]
InterWord,
#[display(fmt = "inter-character")]
InterCharacter,
#[display(fmt = "none")]
None,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WordBreak {
#[display(fmt = "normal")]
Normal,
#[display(fmt = "break-all")]
BreakAll,
#[display(fmt = "keep-all")]
KeepAll,
#[display(fmt = "break-word")]
BreakWord,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WordWrap {
#[display(fmt = "normal")]
Normal,
#[display(fmt = "break-word")]
BreakWord,
#[display(fmt = "initial")]
Initial,
#[display(fmt = "inherit")]
Inherit,
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WritingMode {
#[display(fmt = "horizontal-tb")]
HorizontalTb,
#[display(fmt = "vertical-rl")]
VerticalRl,
#[display(fmt = "vertical-lr")]
VerticalLr,
}