use crate::{color::Color, unit::*, values as val, St, StyleValues, UpdateStyleValues};
use derive_rich::Rich;
use savory::prelude::DeclarativeConfig;
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 DeclarativeConfig for Text {}
impl UpdateStyleValues for Text {
fn update_style_values(self, values: StyleValues) -> StyleValues {
values
.try_add(St::Color, self.color)
.try_add(St::Direction, self.direction)
.try_add(St::LetterSpacing, self.letter_spacing)
.try_add(St::LineHeight, self.line_height)
.try_add(St::TextAlign, self.align)
.try_add(St::TextDecoration, self.decoration.clone())
.try_add(St::TextIndent, self.indent)
.try_merge(self.shadow)
.try_add(St::TextTransform, self.transform)
.try_add(St::TextOverflow, self.overflow.clone())
.try_add(St::UnicodeBidi, self.unicode_bidi)
.try_add(St::VerticalAlign, self.vertical_align)
.try_add(St::WhiteSpace, self.white_space)
.try_add(St::WordSpacing, 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(val::Initial),
Inherit(val::Inherit),
None(val::None),
Unset(val::Unset),
}
impl UpdateStyleValues for TextShadow {
fn update_style_values(self, values: StyleValues) -> StyleValues {
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 values;
}
val
}
};
values.add(St::TextShadow, 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))
}
#[allow(clippy::should_implement_trait)]
pub fn add(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(val::Ltr),
Rtl(val::Rtl),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum Spacing {
Normal(val::Normal),
Length(Length),
Initial(val::Initial),
Inherit(val::Inherit),
}
pub type LetterSpacing = Spacing;
pub type WordSpacing = Spacing;
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum LineHeight {
Normal(val::Normal),
Number(f32),
Length(Length),
Percent(Percent),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextAlign {
Start(val::Start),
End(val::End),
Left(val::Left),
Right(val::Right),
Center(val::Center),
Justify(val::Justify),
Initial(val::Initial),
Inherit(val::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(val::Initial),
#[from]
Inherit(val::Inherit),
}
impl Default for TextDecoration {
fn default() -> Self {
val::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(val::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(val::None.into()),
color: None,
style: Some(value.into()),
}
}
};
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecorationLine {
None(val::None),
Underline(val::Underline),
Overline(val::Overline),
LineThrough(val::LineThrough),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecorationColor {
Color(Color),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextDecorationStyle {
Solid(val::Solid),
Double(val::Double),
Dotted(val::Dotted),
Dashed(val::Dashed),
Wavy(val::Wavy),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum TextIndent {
Length(Length),
Percent(Percent),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextTransform {
None(val::None),
Capitalize(val::Capitalize),
Uppercase(val::Uppercase),
Lowercase(val::Lowercase),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum TextOverflow {
Clip(val::Clip),
Ellipsis(val::Ellipsis),
String(Cow<'static, str>),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum UnicodeBidi {
Normal(val::Normal),
Embed(val::Embed),
BidiOverride(val::BidiOverride),
Isolate(val::Isolate),
IsolateOverride(val::IsolateOverride),
Plaintext(val::Plaintext),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Debug, PartialEq, Display, From)]
pub enum VerticalAlign {
Baseline(val::Baseline),
Sub(val::Sub),
Super(val::Super),
Top(val::Top),
TextTop(val::TextTop),
Middle(val::Middle),
Bottom(val::Bottom),
TextBottom(val::TextBottom),
Length(Length),
Percent(Percent),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WhiteSpace {
Normal(val::Normal),
Nowrap(val::Nowrap),
Pre(val::Pre),
PreLine(val::PreLine),
PreWrap(val::PreWrap),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextAlignLast {
Auto(val::Auto),
Left(val::Left),
Right(val::Right),
Center(val::Center),
Justify(val::Justify),
Start(val::Start),
End(val::End),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum TextJustify {
Auto(val::Auto),
InterWord(val::InterWord),
InterCharacter(val::InterCharacter),
None(val::None),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WordBreak {
Normal(val::Normal),
BreakAll(val::BreakAll),
KeepAll(val::KeepAll),
BreakWord(val::BreakWord),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WordWrap {
Normal(val::Normal),
BreakWord(val::BreakWord),
Initial(val::Initial),
Inherit(val::Inherit),
}
#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
pub enum WritingMode {
HorizontalTb(val::HorizontalTb),
VerticalRl(val::VerticalRl),
VerticalLr(val::VerticalLr),
}