Skip to main content

azul_layout/solver3/
counters.rs

1//! CSS Counter Support
2//!
3//! Implements CSS counters for ordered lists and generated content as per CSS spec.
4//! Counters are cached per-node in the LayoutCache and computed during layout traversal.
5
6use alloc::string::String;
7
8use azul_css::props::style::lists::StyleListStyleType;
9
10/// Formats a counter value into a string based on the list style type.
11///
12/// Implements CSS counter styles for various numbering systems.
13pub fn format_counter(value: i32, style: StyleListStyleType) -> String {
14    match style {
15        StyleListStyleType::None => String::new(),
16        StyleListStyleType::Disc => "•".to_string(),
17        StyleListStyleType::Circle => "◦".to_string(),
18        StyleListStyleType::Square => "▪".to_string(),
19        StyleListStyleType::Decimal => value.to_string(),
20        StyleListStyleType::DecimalLeadingZero => format!("{:02}", value),
21        StyleListStyleType::LowerAlpha => to_alphabetic(value as u32, false),
22        StyleListStyleType::UpperAlpha => to_alphabetic(value as u32, true),
23        StyleListStyleType::LowerRoman => to_roman(value as u32, false),
24        StyleListStyleType::UpperRoman => to_roman(value as u32, true),
25        StyleListStyleType::LowerGreek => to_greek(value as u32, false),
26        StyleListStyleType::UpperGreek => to_greek(value as u32, true),
27    }
28}
29
30// --- Formatting Helpers ---
31
32/// Converts a number to alphabetic representation (a, b, c, ..., z, aa, ab, ...).
33///
34/// This implements the CSS `lower-alpha` and `upper-alpha` counter styles.
35fn to_alphabetic(mut num: u32, uppercase: bool) -> String {
36    if num == 0 {
37        return String::new();
38    }
39
40    let mut result = String::new();
41    let base = if uppercase { b'A' } else { b'a' };
42
43    while num > 0 {
44        let remainder = ((num - 1) % 26) as u8;
45        result.insert(0, (base + remainder) as char);
46        num = (num - 1) / 26;
47    }
48
49    result
50}
51
52/// Converts a number to Roman numeral representation.
53///
54/// This implements the CSS `lower-roman` and `upper-roman` counter styles.
55fn to_roman(mut num: u32, uppercase: bool) -> String {
56    if num == 0 {
57        return "0".to_string();
58    }
59    if num > 3999 {
60        // Roman numerals traditionally don't go beyond 3999
61        return num.to_string();
62    }
63
64    let values = [
65        (1000, "M", "m"),
66        (900, "CM", "cm"),
67        (500, "D", "d"),
68        (400, "CD", "cd"),
69        (100, "C", "c"),
70        (90, "XC", "xc"),
71        (50, "L", "l"),
72        (40, "XL", "xl"),
73        (10, "X", "x"),
74        (9, "IX", "ix"),
75        (5, "V", "v"),
76        (4, "IV", "iv"),
77        (1, "I", "i"),
78    ];
79
80    let mut result = String::new();
81    for (value, upper, lower) in &values {
82        while num >= *value {
83            result.push_str(if uppercase { upper } else { lower });
84            num -= *value;
85        }
86    }
87
88    result
89}
90
91/// Converts a number to Greek letter representation.
92///
93/// This implements the CSS `lower-greek` and `upper-greek` counter styles.
94/// Supports α, β, γ, ... (24 letters of Greek alphabet).
95fn to_greek(num: u32, uppercase: bool) -> String {
96    if num == 0 {
97        return String::new();
98    }
99
100    // Greek lowercase letters α-ω (24 letters, omitting archaic letters)
101    let greek_lower = [
102        'α', 'β', 'γ', 'δ', 'ε', 'ζ', 'η', 'θ', 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 'σ',
103        'τ', 'υ', 'φ', 'χ', 'ψ', 'ω',
104    ];
105
106    let greek_upper = [
107        'Α', 'Β', 'Γ', 'Δ', 'Ε', 'Ζ', 'Η', 'Θ', 'Ι', 'Κ', 'Λ', 'Μ', 'Ν', 'Ξ', 'Ο', 'Π', 'Ρ', 'Σ',
108        'Τ', 'Υ', 'Φ', 'Χ', 'Ψ', 'Ω',
109    ];
110
111    let letters = if uppercase {
112        &greek_upper
113    } else {
114        &greek_lower
115    };
116
117    if num <= 24 {
118        letters[(num - 1) as usize].to_string()
119    } else {
120        // For numbers > 24, fall back to decimal
121        num.to_string()
122    }
123}