Skip to main content

takumi_css/style/properties/
text_wrap.rs

1use cssparser::{Parser, match_ignore_ascii_case};
2use typed_builder::TypedBuilder;
3
4use crate::style::{
5  CssDescriptorKind, CssToken, FromCss, MakeComputed, ParseResult, declare_enum_from_css_impl,
6  tw::TailwindPropertyParser,
7};
8
9/// Controls how text should be wrapped.
10/// Construct with [`TextWrap::builder`].
11#[derive(Debug, Clone, Copy, PartialEq, Default, TypedBuilder)]
12#[builder(field_defaults(default))]
13#[non_exhaustive]
14pub struct TextWrap {
15  /// Controls whether text should be wrapped.
16  pub mode: TextWrapMode,
17  /// Controls the style of text wrapping.
18  pub style: TextWrapStyle,
19}
20
21impl MakeComputed for TextWrap {}
22
23impl TailwindPropertyParser for TextWrap {
24  fn parse_tw(token: &str) -> Option<Self> {
25    match_ignore_ascii_case! {token,
26      "wrap" => Some(TextWrap {
27        mode: TextWrapMode::Wrap,
28        style: TextWrapStyle::default(),
29      }),
30      "nowrap" => Some(TextWrap {
31        mode: TextWrapMode::NoWrap,
32        style: TextWrapStyle::default(),
33      }),
34      "balance" => Some(TextWrap {
35        mode: TextWrapMode::default(),
36        style: TextWrapStyle::Balance,
37      }),
38      "pretty" => Some(TextWrap {
39        mode: TextWrapMode::default(),
40        style: TextWrapStyle::Pretty,
41      }),
42      _ => None,
43    }
44  }
45}
46
47impl<'i> FromCss<'i> for TextWrap {
48  fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
49    let mut mode = None;
50    let mut style = TextWrapStyle::default();
51
52    while !input.is_exhausted() {
53      if let Ok(parsed) = input.try_parse(TextWrapMode::from_css) {
54        mode = Some(parsed);
55        continue;
56      }
57
58      if let Ok(parsed) = input.try_parse(TextWrapStyle::from_css) {
59        style = parsed;
60        continue;
61      }
62
63      return Err(input.new_error_for_next_token());
64    }
65
66    Ok(TextWrap {
67      mode: mode.unwrap_or_default(),
68      style,
69    })
70  }
71
72  const VALID_TOKENS: &'static [CssToken] = &[
73    CssToken::Descriptor(CssDescriptorKind::TextWrapMode),
74    CssToken::Descriptor(CssDescriptorKind::TextWrapStyle),
75  ];
76}
77
78/// Controls whether text should be wrapped.
79#[derive(Debug, Clone, Copy, PartialEq, Default)]
80#[non_exhaustive]
81pub enum TextWrapMode {
82  /// Text is wrapped across lines at appropriate characters to minimize overflow.
83  #[default]
84  Wrap,
85  /// Text does not wrap across lines. It will overflow its containing element rather than breaking onto a new line.
86  NoWrap,
87}
88
89impl From<TextWrapMode> for parley::TextWrapMode {
90  fn from(value: TextWrapMode) -> Self {
91    match value {
92      TextWrapMode::Wrap => parley::TextWrapMode::Wrap,
93      TextWrapMode::NoWrap => parley::TextWrapMode::NoWrap,
94    }
95  }
96}
97
98declare_enum_from_css_impl!(
99  TextWrapMode,
100  "wrap" => TextWrapMode::Wrap,
101  "nowrap" => TextWrapMode::NoWrap,
102);
103
104/// Controls the style of text wrapping.
105#[derive(Debug, Clone, Copy, PartialEq, Default)]
106#[non_exhaustive]
107pub enum TextWrapStyle {
108  /// Text is wrapped in the default way.
109  #[default]
110  Auto,
111  /// Use binary search to find the minimum width that maintains the same number of lines.
112  Balance,
113  /// Try to avoid orphans (single short words on the last line) by adjusting line breaks.
114  Pretty,
115}
116
117declare_enum_from_css_impl!(
118  TextWrapStyle,
119  "auto" => TextWrapStyle::Auto,
120  "balance" => TextWrapStyle::Balance,
121  "pretty" => TextWrapStyle::Pretty,
122);