use crate::{color::Color, css, unit::*, Style, StyleUpdater};
use derive_rich::Rich;
use std::borrow::Cow;
#[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("line-height", self.line_height)
.try_insert("text-align", self.align)
.try_insert("text-decoration", self.decoration.clone())
.try_insert("text-indent", self.indent)
.try_merge(self.shadow)
.try_insert("text-transform", self.transform)
.try_insert("text-overflow", self.overflow.clone())
.try_insert("unicode-bidi", self.unicode_bidi)
.try_insert("vertical-align", self.vertical_align)
.try_insert("white-space", self.white_space)
.try_insert("word-spacing", self.word_spacing)
}
}
impl<T: Into<Color>> From<T> for Text {
fn from(source: T) -> Self {
Self::default().color(source.into())
}
}
#[derive(Clone, Debug, PartialEq, From)]
pub enum TextShadow {
One(Shadow),
Multiple(Vec<Shadow>),
Initial(css::Initial),
Inherit(css::Inherit),
None(css::None),
Unset(css::Unset),
}
impl StyleUpdater for TextShadow {
fn update_style(self, style: Style) -> Style {
let to_string = |shadow: Shadow| {
let mut vals = vec![];
vals.push(shadow.x.to_string());
vals.push(shadow.y.to_string());
if let Some(blur) = shadow.blur {
vals.push(blur.to_string());
}
if let Some(color) = shadow.color {
vals.push(color.to_string());
}
vals.join(" ")
};
let val = match self {
Self::Initial(val) => val.to_string(),
Self::Inherit(val) => val.to_string(),
Self::None(val) => val.to_string(),
Self::Unset(val) => val.to_string(),
Self::One(shadow) => to_string(shadow),
Self::Multiple(vec) => {
let val = vec
.into_iter()
.map(to_string)
.collect::<Vec<_>>()
.join(", ");
if val.is_empty() {
return style;
}
val
}
};
style.insert("text-shadow", val)
}
}
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_else(Shadow::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 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 {
Ltr(css::Ltr),
Rtl(css::Rtl),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Spacing {
Normal(css::Normal),
Length(Length),
Initial(css::Initial),
Inherit(css::Inherit),
}
pub type LetterSpacing = Spacing;
pub type WordSpacing = Spacing;
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum LineHeight {
Normal(css::Normal),
Number(f32),
Length(Length),
Percent(Percent),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextAlign {
Start(css::Start),
End(css::End),
Left(css::Left),
Right(css::Right),
Center(css::Center),
Justify(css::Justify),
Initial(css::Initial),
Inherit(css::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>,
},
#[from]
Initial(css::Initial),
#[from]
Inherit(css::Inherit),
}
impl Default for TextDecoration {
fn default() -> Self {
css::Initial.into()
}
}
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 color(mut self, value: impl Into<TextDecorationColor>) -> Self {
match self {
Self::Decoration { ref mut color, .. } => *color = Some(value.into()),
_ => {
self = Self::Decoration {
line: Some(css::None.into()),
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(css::None.into()),
color: None,
style: Some(value.into()),
}
}
};
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecorationLine {
None(css::None),
Underline(css::Underline),
Overline(css::Overline),
LineThrough(css::LineThrough),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecorationColor {
Color(Color),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecorationStyle {
Solid(css::Solid),
Double(css::Double),
Dotted(css::Dotted),
Dashed(css::Dashed),
Wavy(css::Wavy),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum TextIndent {
Length(Length),
Percent(Percent),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextTransform {
None(css::None),
Capitalize(css::Capitalize),
Uppercase(css::Uppercase),
Lowercase(css::Lowercase),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum TextOverflow {
Clip(css::Clip),
Ellipsis(css::Ellipsis),
String(Cow<'static, str>),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum UnicodeBidi {
Normal(css::Normal),
Embed(css::Embed),
BidiOverride(css::BidiOverride),
Isolate(css::Isolate),
IsolateOverride(css::IsolateOverride),
Plaintext(css::Plaintext),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum VerticalAlign {
Baseline(css::Baseline),
Sub(css::Sub),
Super(css::Super),
Top(css::Top),
TextTop(css::TextTop),
Middle(css::Middle),
Bottom(css::Bottom),
TextBottom(css::TextBottom),
Length(Length),
Percent(Percent),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WhiteSpace {
Normal(css::Normal),
Nowrap(css::Nowrap),
Pre(css::Pre),
PreLine(css::PreLine),
PreWrap(css::PreWrap),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextAlignLast {
Auto(css::Auto),
Left(css::Left),
Right(css::Right),
Center(css::Center),
Justify(css::Justify),
Start(css::Start),
End(css::End),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextJustify {
Auto(css::Auto),
InterWord(css::InterWord),
InterCharacter(css::InterCharacter),
None(css::None),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WordBreak {
Normal(css::Normal),
BreakAll(css::BreakAll),
KeepAll(css::KeepAll),
BreakWord(css::BreakWord),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WordWrap {
Normal(css::Normal),
BreakWord(css::BreakWord),
Initial(css::Initial),
Inherit(css::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WritingMode {
HorizontalTb(css::HorizontalTb),
VerticalRl(css::VerticalRl),
VerticalLr(css::VerticalLr),
}