Skip to main content

azul_css/props/layout/
display.rs

1//! CSS properties for `display` and `float`.
2
3use alloc::string::{String, ToString};
4
5use crate::props::formatter::PrintAsCssValue;
6
7/// Represents a `display` CSS property value
8#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
9#[repr(C)]
10pub enum LayoutDisplay {
11    // Basic display types
12    None,
13    #[default]
14    Block,
15    Inline,
16    InlineBlock,
17
18    // Flex layout
19    Flex,
20    InlineFlex,
21
22    // Table layout
23    Table,
24    InlineTable,
25    TableRowGroup,
26    TableHeaderGroup,
27    TableFooterGroup,
28    TableRow,
29    TableColumnGroup,
30    TableColumn,
31    TableCell,
32    TableCaption,
33
34    FlowRoot,
35
36    // List layout
37    ListItem,
38
39    // Special displays
40    RunIn,
41    Marker,
42
43    // CSS3 additions
44    Grid,
45    InlineGrid,
46}
47
48impl LayoutDisplay {
49    pub fn creates_block_context(&self) -> bool {
50        matches!(
51            self,
52            LayoutDisplay::Block
53                | LayoutDisplay::Flex
54                | LayoutDisplay::Grid
55                | LayoutDisplay::Table
56                | LayoutDisplay::ListItem
57        )
58    }
59
60    pub fn creates_flex_context(&self) -> bool {
61        matches!(self, LayoutDisplay::Flex | LayoutDisplay::InlineFlex)
62    }
63
64    pub fn creates_table_context(&self) -> bool {
65        matches!(self, LayoutDisplay::Table | LayoutDisplay::InlineTable)
66    }
67
68    pub fn is_inline_level(&self) -> bool {
69        matches!(
70            self,
71            LayoutDisplay::Inline
72                | LayoutDisplay::InlineBlock
73                | LayoutDisplay::InlineFlex
74                | LayoutDisplay::InlineTable
75                | LayoutDisplay::InlineGrid
76        )
77    }
78}
79
80impl PrintAsCssValue for LayoutDisplay {
81    fn print_as_css_value(&self) -> String {
82        String::from(match self {
83            LayoutDisplay::None => "none",
84            LayoutDisplay::Block => "block",
85            LayoutDisplay::Inline => "inline",
86            LayoutDisplay::InlineBlock => "inline-block",
87            LayoutDisplay::Flex => "flex",
88            LayoutDisplay::InlineFlex => "inline-flex",
89            LayoutDisplay::Table => "table",
90            LayoutDisplay::InlineTable => "inline-table",
91            LayoutDisplay::TableRowGroup => "table-row-group",
92            LayoutDisplay::TableHeaderGroup => "table-header-group",
93            LayoutDisplay::TableFooterGroup => "table-footer-group",
94            LayoutDisplay::TableRow => "table-row",
95            LayoutDisplay::TableColumnGroup => "table-column-group",
96            LayoutDisplay::TableColumn => "table-column",
97            LayoutDisplay::TableCell => "table-cell",
98            LayoutDisplay::TableCaption => "table-caption",
99            LayoutDisplay::ListItem => "list-item",
100            LayoutDisplay::RunIn => "run-in",
101            LayoutDisplay::Marker => "marker",
102            LayoutDisplay::FlowRoot => "flow-root",
103            LayoutDisplay::Grid => "grid",
104            LayoutDisplay::InlineGrid => "inline-grid",
105        })
106    }
107}
108
109/// Represents a `float` attribute
110#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
111#[repr(C)]
112pub enum LayoutFloat {
113    Left,
114    Right,
115    None,
116}
117
118impl Default for LayoutFloat {
119    fn default() -> Self {
120        LayoutFloat::None
121    }
122}
123
124impl PrintAsCssValue for LayoutFloat {
125    fn print_as_css_value(&self) -> String {
126        String::from(match self {
127            LayoutFloat::Left => "left",
128            LayoutFloat::Right => "right",
129            LayoutFloat::None => "none",
130        })
131    }
132}
133
134// --- PARSERS ---
135
136#[cfg(feature = "parser")]
137#[derive(Clone, PartialEq)]
138pub enum LayoutDisplayParseError<'a> {
139    InvalidValue(&'a str),
140}
141
142#[cfg(feature = "parser")]
143impl_debug_as_display!(LayoutDisplayParseError<'a>);
144
145#[cfg(feature = "parser")]
146impl_display! { LayoutDisplayParseError<'a>, {
147    InvalidValue(val) => format!("Invalid display value: \"{}\"", val),
148}}
149
150#[cfg(feature = "parser")]
151#[derive(Debug, Clone, PartialEq)]
152pub enum LayoutDisplayParseErrorOwned {
153    InvalidValue(String),
154}
155
156#[cfg(feature = "parser")]
157impl<'a> LayoutDisplayParseError<'a> {
158    pub fn to_contained(&self) -> LayoutDisplayParseErrorOwned {
159        match self {
160            Self::InvalidValue(s) => LayoutDisplayParseErrorOwned::InvalidValue(s.to_string()),
161        }
162    }
163}
164
165#[cfg(feature = "parser")]
166impl LayoutDisplayParseErrorOwned {
167    pub fn to_shared<'a>(&'a self) -> LayoutDisplayParseError<'a> {
168        match self {
169            Self::InvalidValue(s) => LayoutDisplayParseError::InvalidValue(s.as_str()),
170        }
171    }
172}
173
174#[cfg(feature = "parser")]
175pub fn parse_layout_display<'a>(
176    input: &'a str,
177) -> Result<LayoutDisplay, LayoutDisplayParseError<'a>> {
178    let input = input.trim();
179    match input {
180        "none" => Ok(LayoutDisplay::None),
181        "block" => Ok(LayoutDisplay::Block),
182        "inline" => Ok(LayoutDisplay::Inline),
183        "inline-block" => Ok(LayoutDisplay::InlineBlock),
184        "flex" => Ok(LayoutDisplay::Flex),
185        "inline-flex" => Ok(LayoutDisplay::InlineFlex),
186        "table" => Ok(LayoutDisplay::Table),
187        "inline-table" => Ok(LayoutDisplay::InlineTable),
188        "table-row-group" => Ok(LayoutDisplay::TableRowGroup),
189        "table-header-group" => Ok(LayoutDisplay::TableHeaderGroup),
190        "table-footer-group" => Ok(LayoutDisplay::TableFooterGroup),
191        "table-row" => Ok(LayoutDisplay::TableRow),
192        "table-column-group" => Ok(LayoutDisplay::TableColumnGroup),
193        "table-column" => Ok(LayoutDisplay::TableColumn),
194        "table-cell" => Ok(LayoutDisplay::TableCell),
195        "table-caption" => Ok(LayoutDisplay::TableCaption),
196        "list-item" => Ok(LayoutDisplay::ListItem),
197        "run-in" => Ok(LayoutDisplay::RunIn),
198        "marker" => Ok(LayoutDisplay::Marker),
199        "grid" => Ok(LayoutDisplay::Grid),
200        "inline-grid" => Ok(LayoutDisplay::InlineGrid),
201        "flow-root" => Ok(LayoutDisplay::FlowRoot),
202        _ => Err(LayoutDisplayParseError::InvalidValue(input)),
203    }
204}
205
206#[cfg(feature = "parser")]
207#[derive(Clone, PartialEq)]
208pub enum LayoutFloatParseError<'a> {
209    InvalidValue(&'a str),
210}
211
212#[cfg(feature = "parser")]
213impl_debug_as_display!(LayoutFloatParseError<'a>);
214
215#[cfg(feature = "parser")]
216impl_display! { LayoutFloatParseError<'a>, {
217    InvalidValue(val) => format!("Invalid float value: \"{}\"", val),
218}}
219
220#[cfg(feature = "parser")]
221#[derive(Debug, Clone, PartialEq)]
222pub enum LayoutFloatParseErrorOwned {
223    InvalidValue(String),
224}
225
226#[cfg(feature = "parser")]
227impl<'a> LayoutFloatParseError<'a> {
228    pub fn to_contained(&self) -> LayoutFloatParseErrorOwned {
229        match self {
230            Self::InvalidValue(s) => LayoutFloatParseErrorOwned::InvalidValue(s.to_string()),
231        }
232    }
233}
234
235#[cfg(feature = "parser")]
236impl LayoutFloatParseErrorOwned {
237    pub fn to_shared<'a>(&'a self) -> LayoutFloatParseError<'a> {
238        match self {
239            Self::InvalidValue(s) => LayoutFloatParseError::InvalidValue(s.as_str()),
240        }
241    }
242}
243
244#[cfg(feature = "parser")]
245pub fn parse_layout_float<'a>(input: &'a str) -> Result<LayoutFloat, LayoutFloatParseError<'a>> {
246    let input = input.trim();
247    match input {
248        "left" => Ok(LayoutFloat::Left),
249        "right" => Ok(LayoutFloat::Right),
250        "none" => Ok(LayoutFloat::None),
251        _ => Err(LayoutFloatParseError::InvalidValue(input)),
252    }
253}
254
255#[cfg(all(test, feature = "parser"))]
256mod tests {
257    use super::*;
258
259    #[test]
260    fn test_parse_layout_display() {
261        assert_eq!(parse_layout_display("block").unwrap(), LayoutDisplay::Block);
262        assert_eq!(
263            parse_layout_display("inline").unwrap(),
264            LayoutDisplay::Inline
265        );
266        assert_eq!(
267            parse_layout_display("inline-block").unwrap(),
268            LayoutDisplay::InlineBlock
269        );
270        assert_eq!(parse_layout_display("flex").unwrap(), LayoutDisplay::Flex);
271        assert_eq!(
272            parse_layout_display("inline-flex").unwrap(),
273            LayoutDisplay::InlineFlex
274        );
275        assert_eq!(parse_layout_display("grid").unwrap(), LayoutDisplay::Grid);
276        assert_eq!(
277            parse_layout_display("inline-grid").unwrap(),
278            LayoutDisplay::InlineGrid
279        );
280        assert_eq!(parse_layout_display("none").unwrap(), LayoutDisplay::None);
281        assert_eq!(
282            parse_layout_display("flow-root").unwrap(),
283            LayoutDisplay::FlowRoot
284        );
285        assert_eq!(
286            parse_layout_display("list-item").unwrap(),
287            LayoutDisplay::ListItem
288        );
289        // Note: 'inherit' and 'initial' are handled by the CSS cascade system,
290        // not as enum variants
291        assert!(parse_layout_display("inherit").is_err());
292        assert!(parse_layout_display("initial").is_err());
293
294        // Table values
295        assert_eq!(parse_layout_display("table").unwrap(), LayoutDisplay::Table);
296        assert_eq!(
297            parse_layout_display("inline-table").unwrap(),
298            LayoutDisplay::InlineTable
299        );
300        assert_eq!(
301            parse_layout_display("table-row").unwrap(),
302            LayoutDisplay::TableRow
303        );
304        assert_eq!(
305            parse_layout_display("table-cell").unwrap(),
306            LayoutDisplay::TableCell
307        );
308        assert_eq!(
309            parse_layout_display("table-caption").unwrap(),
310            LayoutDisplay::TableCaption
311        );
312        assert_eq!(
313            parse_layout_display("table-column-group").unwrap(),
314            LayoutDisplay::TableColumnGroup
315        );
316        assert_eq!(
317            parse_layout_display("table-header-group").unwrap(),
318            LayoutDisplay::TableHeaderGroup
319        );
320        assert_eq!(
321            parse_layout_display("table-footer-group").unwrap(),
322            LayoutDisplay::TableFooterGroup
323        );
324        assert_eq!(
325            parse_layout_display("table-row-group").unwrap(),
326            LayoutDisplay::TableRowGroup
327        );
328
329        // Whitespace
330        assert_eq!(
331            parse_layout_display("  inline-flex  ").unwrap(),
332            LayoutDisplay::InlineFlex
333        );
334
335        // Invalid values
336        assert!(parse_layout_display("invalid-value").is_err());
337        assert!(parse_layout_display("").is_err());
338        assert!(parse_layout_display("display").is_err());
339    }
340
341    #[test]
342    fn test_parse_layout_float() {
343        assert_eq!(parse_layout_float("left").unwrap(), LayoutFloat::Left);
344        assert_eq!(parse_layout_float("right").unwrap(), LayoutFloat::Right);
345        assert_eq!(parse_layout_float("none").unwrap(), LayoutFloat::None);
346
347        // Whitespace
348        assert_eq!(parse_layout_float("  right  ").unwrap(), LayoutFloat::Right);
349
350        // Invalid values
351        assert!(parse_layout_float("center").is_err());
352        assert!(parse_layout_float("").is_err());
353        assert!(parse_layout_float("float-left").is_err());
354    }
355}