1use crate::{color::Color, unit::*, Style, StyleUpdater};
2use derive_rich::Rich;
3use std::{borrow::Cow, fmt};
4
5#[derive(Rich, Clone, Debug, PartialEq, Default)]
34pub struct Text {
35 #[rich(write, write(option))]
36 pub color: Option<Color>,
37 #[rich(write, write(option))]
38 pub direction: Option<Direction>,
39 #[rich(write, write(option))]
40 pub letter_spacing: Option<LetterSpacing>,
41 #[rich(write, write(option))]
42 pub word_spacing: Option<WordSpacing>,
43 #[rich(write, write(option))]
44 pub line_height: Option<LineHeight>,
45 #[rich(write, write(option))]
46 pub align: Option<TextAlign>,
47 #[rich(write, write(option))]
48 pub align_last: Option<TextAlignLast>,
49 #[rich(write, write(option))]
50 pub justify: Option<TextJustify>,
51 #[rich(write, write(option), write(style = compose))]
52 pub shadow: Option<TextShadow>,
53 #[rich(write, write(option))]
54 pub indent: Option<TextIndent>,
55 #[rich(write, write(option), write(style = compose))]
56 pub decoration: Option<TextDecoration>,
57 #[rich(write, write(option))]
58 pub white_space: Option<WhiteSpace>,
59 #[rich(write, write(option))]
60 pub unicode_bidi: Option<UnicodeBidi>,
61 #[rich(write, write(option))]
62 pub transform: Option<TextTransform>,
63 #[rich(write, write(option))]
64 pub overflow: Option<TextOverflow>,
65 #[rich(write, write(option))]
66 pub vertical_align: Option<VerticalAlign>,
68 #[rich(write, write(option))]
69 pub writing_mode: Option<WritingMode>,
70 #[rich(write, write(option))]
71 pub word_wrap: Option<WordWrap>,
72 #[rich(write, write(option))]
73 pub word_break: Option<WordBreak>,
74}
75
76impl StyleUpdater for Text {
77 fn update_style(self, style: Style) -> Style {
78 style
79 .try_insert("color", self.color)
80 .try_insert("direction", self.direction)
81 .try_insert("letter-spacing", self.letter_spacing)
82 .try_insert("word-spacing", self.word_spacing)
83 .try_insert("line-height", self.line_height)
84 .try_insert("text-align", self.align)
85 .try_insert("text-align-last", self.align_last)
86 .try_insert("text-justify", self.justify)
87 .try_merge(self.shadow)
88 .try_insert("text-indent", self.indent)
89 .try_insert("text-decoration", self.decoration)
90 .try_insert("white-space", self.white_space)
91 .try_insert("unicode-bidi", self.unicode_bidi)
92 .try_insert("text-transform", self.transform)
93 .try_insert("text-overflow", self.overflow.clone())
94 .try_insert("vertical-align", self.vertical_align)
95 .try_insert("writing-mode", self.writing_mode)
96 .try_insert("word-wrap", self.word_wrap)
97 .try_insert("word-break", self.word_break)
98 }
99}
100
101impl<T: Into<Color>> From<T> for Text {
102 fn from(source: T) -> Self {
103 Self::default().color(source.into())
104 }
105}
106
107#[derive(Clone, Debug, PartialEq, From, Display)]
108pub enum TextShadow {
109 One(Shadow),
110 #[display(
111 fmt = "{}",
112 "_0.iter().map(|s| s.to_string()).collect::<Vec<_>>().join(\", \")"
113 )]
114 Multiple(Vec<Shadow>),
115 #[display(fmt = "initial")]
116 Initial,
117 #[display(fmt = "inherit")]
118 Inherit,
119 #[display(fmt = "none")]
120 None,
121 #[display(fmt = "unset")]
122 Unset,
123}
124
125impl StyleUpdater for TextShadow {
126 fn update_style(self, style: Style) -> Style {
127 style.insert("text-shadow", self)
128 }
129}
130
131impl Default for TextShadow {
132 fn default() -> Self {
133 TextShadow::Multiple(vec![])
134 }
135}
136
137impl TextShadow {
138 fn shadow(mut self, conf: impl FnOnce(Shadow) -> Shadow) -> Self {
139 self = match self {
140 Self::One(shadow) => Self::One(conf(shadow)),
141 Self::Multiple(shadows) => {
142 Self::One(conf(shadows.into_iter().next().unwrap_or_default()))
143 }
144 _ => Self::One(conf(Shadow::default())),
145 };
146 self
147 }
148
149 pub fn new() -> Self {
150 TextShadow::default()
151 }
152
153 pub fn x(self, val: impl Into<Length>) -> Self {
154 self.shadow(|sh| sh.x(val))
155 }
156
157 pub fn y(self, val: impl Into<Length>) -> Self {
158 self.shadow(|sh| sh.y(val))
159 }
160
161 pub fn blur(self, val: impl Into<Length>) -> Self {
162 self.shadow(|sh| sh.blur(val))
163 }
164
165 pub fn try_blur(self, val: Option<impl Into<Length>>) -> Self {
166 self.shadow(|sh| sh.try_blur(val))
167 }
168
169 pub fn color(self, val: impl Into<Color>) -> Self {
170 self.shadow(|sh| sh.color(val))
171 }
172
173 pub fn try_color(self, val: Option<impl Into<Color>>) -> Self {
174 self.shadow(|sh| sh.try_color(val))
175 }
176
177 pub fn push(mut self, get_val: impl FnOnce(Shadow) -> Shadow) -> Self {
178 let val = get_val(Shadow::default());
179 self = match self {
180 Self::Multiple(mut vec) => {
181 vec.push(val);
182 Self::Multiple(vec)
183 }
184 _ => Self::Multiple(vec![val]),
185 };
186 self
187 }
188}
189
190#[derive(Rich, Clone, Debug, PartialEq)]
191pub struct Shadow {
192 #[rich(write)]
193 x: Length,
194 #[rich(write)]
195 y: Length,
196 #[rich(write, write(option))]
197 blur: Option<Length>,
198 #[rich(write, write(option))]
199 color: Option<Color>,
200}
201
202impl fmt::Display for Shadow {
203 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204 write!(f, "{} {}", self.x, self.y)?;
205 if let Some(blur) = self.blur.as_ref() {
206 write!(f, " {}", blur)?;
207 }
208 if let Some(color) = self.color.as_ref() {
209 write!(f, " {}", color)?;
210 }
211 Ok(())
212 }
213}
214
215impl Default for Shadow {
216 fn default() -> Self {
217 Self {
218 x: px(0),
219 y: px(0),
220 blur: None,
221 color: None,
222 }
223 }
224}
225
226#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
227pub enum Direction {
228 #[display(fmt = "ltr")]
229 Ltr,
230 #[display(fmt = "rtl")]
231 Rtl,
232 #[display(fmt = "initial")]
233 Initial,
234 #[display(fmt = "inherit")]
235 Inherit,
236}
237
238#[derive(Clone, Debug, PartialEq, Display, From)]
239pub enum Spacing {
240 #[display(fmt = "normal")]
241 Normal,
242 Length(Length),
243 #[display(fmt = "initial")]
244 Initial,
245 #[display(fmt = "inherit")]
246 Inherit,
247}
248
249pub type LetterSpacing = Spacing;
250pub type WordSpacing = Spacing;
251
252#[derive(Clone, Debug, PartialEq, Display, From)]
253pub enum LineHeight {
254 #[display(fmt = "normal")]
255 Normal,
256 Number(f32),
257 Length(Length),
258 Percent(Percent),
259 #[display(fmt = "initial")]
260 Initial,
261 #[display(fmt = "inherit")]
262 Inherit,
263}
264
265#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
266pub enum TextAlign {
267 #[display(fmt = "start")]
268 Start,
269 #[display(fmt = "end")]
270 End,
271 #[display(fmt = "left")]
272 Left,
273 #[display(fmt = "right")]
274 Right,
275 #[display(fmt = "center")]
276 Center,
277 #[display(fmt = "justify")]
278 Justify,
279 #[display(fmt = "initial")]
280 Initial,
281 #[display(fmt = "inherit")]
282 Inherit,
283}
284
285fn display_helper(value: &Option<impl ToString>) -> String {
286 value
287 .as_ref()
288 .map(|v| v.to_string() + " ")
289 .unwrap_or_else(|| "".into())
290}
291
292#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
293pub enum TextDecoration {
294 #[display(
295 fmt = "{}{}{}",
296 "display_helper(line)",
297 "display_helper(color)",
298 "display_helper(style).trim()"
299 )]
300 Decoration {
301 line: Option<TextDecorationLine>,
303 color: Option<TextDecorationColor>,
304 style: Option<TextDecorationStyle>,
305 },
306 #[display(fmt = "initial")]
307 Initial,
308 #[display(fmt = "inherit")]
309 Inherit,
310}
311
312impl Default for TextDecoration {
313 fn default() -> Self {
314 TextDecoration::Initial
315 }
316}
317
318impl TextDecoration {
319 pub fn line(mut self, value: impl Into<TextDecorationLine>) -> Self {
320 match self {
321 Self::Decoration { ref mut line, .. } => *line = Some(value.into()),
322 _ => {
323 self = Self::Decoration {
324 line: Some(value.into()),
325 color: None,
326 style: None,
327 }
328 }
329 };
330 self
331 }
332
333 pub fn line_none(self) -> Self {
334 self.line(TextDecorationLine::None)
335 }
336
337 pub fn line_underline(self) -> Self {
338 self.line(TextDecorationLine::Underline)
339 }
340
341 pub fn line_overline(self) -> Self {
342 self.line(TextDecorationLine::Overline)
343 }
344
345 pub fn line_line_through(self) -> Self {
346 self.line(TextDecorationLine::LineThrough)
347 }
348
349 pub fn color(mut self, value: impl Into<TextDecorationColor>) -> Self {
350 match self {
351 Self::Decoration { ref mut color, .. } => *color = Some(value.into()),
352 _ => {
353 self = Self::Decoration {
354 line: Some(TextDecorationLine::None),
355 color: Some(value.into()),
356 style: None,
357 }
358 }
359 };
360 self
361 }
362
363 pub fn style(mut self, value: impl Into<TextDecorationStyle>) -> Self {
364 match self {
365 Self::Decoration { ref mut style, .. } => *style = Some(value.into()),
366 _ => {
367 self = Self::Decoration {
368 line: Some(TextDecorationLine::None),
369 color: None,
370 style: Some(value.into()),
371 }
372 }
373 };
374 self
375 }
376
377 pub fn style_solid(self) -> Self {
378 self.style(TextDecorationStyle::Solid)
379 }
380
381 pub fn style_dashed(self) -> Self {
382 self.style(TextDecorationStyle::Dashed)
383 }
384
385 }
387
388#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
389pub enum TextDecorationLine {
390 #[display(fmt = "none")]
391 None,
392 #[display(fmt = "underline")]
393 Underline,
394 #[display(fmt = "overline")]
395 Overline,
396 #[display(fmt = "line-through")]
397 LineThrough,
398 #[display(fmt = "initial")]
399 Initial,
400 #[display(fmt = "inherit")]
401 Inherit,
402}
403
404#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
405pub enum TextDecorationColor {
406 Color(Color),
407 #[display(fmt = "initial")]
408 Initial,
409 #[display(fmt = "inherit")]
410 Inherit,
411}
412
413#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
414pub enum TextDecorationStyle {
415 #[display(fmt = "solid")]
416 Solid,
417 #[display(fmt = "double")]
418 Double,
419 #[display(fmt = "dotted")]
420 Dotted,
421 #[display(fmt = "dashed")]
422 Dashed,
423 #[display(fmt = "wavy")]
424 Wavy,
425 #[display(fmt = "initial")]
426 Initial,
427 #[display(fmt = "inherit")]
428 Inherit,
429}
430
431#[derive(Clone, Debug, PartialEq, Display, From)]
432pub enum TextIndent {
433 Length(Length),
434 Percent(Percent),
435 #[display(fmt = "initial")]
436 Initial,
437 #[display(fmt = "inherit")]
438 Inherit,
439}
440
441#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
442pub enum TextTransform {
443 #[display(fmt = "none")]
444 None,
445 #[display(fmt = "capitalize")]
446 Capitalize,
447 #[display(fmt = "uppercase")]
448 Uppercase,
449 #[display(fmt = "lowercase")]
450 Lowercase,
451 #[display(fmt = "initial")]
452 Initial,
453 #[display(fmt = "inherit")]
454 Inherit,
455}
456
457#[derive(Clone, Debug, PartialEq, Display, From)]
458pub enum TextOverflow {
459 #[display(fmt = "clip")]
460 Clip,
461 #[display(fmt = "ellipsis")]
462 Ellipsis,
463 String(Cow<'static, str>),
464 #[display(fmt = "initial")]
465 Initial,
466 #[display(fmt = "inherit")]
467 Inherit,
468}
469
470#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
471pub enum UnicodeBidi {
472 #[display(fmt = "normal")]
473 Normal,
474 #[display(fmt = "embed")]
475 Embed,
476 #[display(fmt = "bidi-override")]
477 BidiOverride,
478 #[display(fmt = "isolate")]
479 Isolate,
480 #[display(fmt = "isolate-override")]
481 IsolateOverride,
482 #[display(fmt = "plaintext")]
483 Plaintext,
484 #[display(fmt = "initial")]
485 Initial,
486 #[display(fmt = "inherit")]
487 Inherit,
488}
489
490#[derive(Clone, Debug, PartialEq, Display, From)]
491pub enum VerticalAlign {
492 #[display(fmt = "baseline")]
493 Baseline,
494 #[display(fmt = "sub")]
495 Sub,
496 #[display(fmt = "super")]
497 Super,
498 #[display(fmt = "top")]
499 Top,
500 #[display(fmt = "text-top")]
501 TextTop,
502 #[display(fmt = "middle")]
503 Middle,
504 #[display(fmt = "bottom")]
505 Bottom,
506 #[display(fmt = "text-bottom")]
507 TextBottom,
508 Length(Length),
509 Percent(Percent),
510 #[display(fmt = "initial")]
511 Initial,
512 #[display(fmt = "inherit")]
513 Inherit,
514}
515
516#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
517pub enum WhiteSpace {
518 #[display(fmt = "normal")]
519 Normal,
520 #[display(fmt = "nowrap")]
521 Nowrap,
522 #[display(fmt = "pre")]
523 Pre,
524 #[display(fmt = "pre-line")]
525 PreLine,
526 #[display(fmt = "pre-wrap")]
527 PreWrap,
528 #[display(fmt = "initial")]
529 Initial,
530 #[display(fmt = "inherit")]
531 Inherit,
532}
533
534#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
535pub enum TextAlignLast {
536 #[display(fmt = "auto")]
537 Auto,
538 #[display(fmt = "left")]
539 Left,
540 #[display(fmt = "right")]
541 Right,
542 #[display(fmt = "center")]
543 Center,
544 #[display(fmt = "justify")]
545 Justify,
546 #[display(fmt = "start")]
547 Start,
548 #[display(fmt = "end")]
549 End,
550 #[display(fmt = "initial")]
551 Initial,
552 #[display(fmt = "inherit")]
553 Inherit,
554}
555
556#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
557pub enum TextJustify {
558 #[display(fmt = "auto")]
559 Auto,
560 #[display(fmt = "inter-word")]
561 InterWord,
562 #[display(fmt = "inter-character")]
563 InterCharacter,
564 #[display(fmt = "none")]
565 None,
566 #[display(fmt = "initial")]
567 Initial,
568 #[display(fmt = "inherit")]
569 Inherit,
570}
571
572#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
573pub enum WordBreak {
574 #[display(fmt = "normal")]
575 Normal,
576 #[display(fmt = "break-all")]
577 BreakAll,
578 #[display(fmt = "keep-all")]
579 KeepAll,
580 #[display(fmt = "break-word")]
581 BreakWord,
582 #[display(fmt = "initial")]
583 Initial,
584 #[display(fmt = "inherit")]
585 Inherit,
586}
587
588#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
589pub enum WordWrap {
590 #[display(fmt = "normal")]
591 Normal,
592 #[display(fmt = "break-word")]
593 BreakWord,
594 #[display(fmt = "initial")]
595 Initial,
596 #[display(fmt = "inherit")]
597 Inherit,
598}
599
600#[derive(Clone, Copy, Debug, PartialEq, Display, From)]
601pub enum WritingMode {
602 #[display(fmt = "horizontal-tb")]
603 HorizontalTb,
604 #[display(fmt = "vertical-rl")]
605 VerticalRl,
606 #[display(fmt = "vertical-lr")]
607 VerticalLr,
608}