atmos/os_lib/layout/
boxmodel.rs1use super::*;
4
5pub(crate) fn margin_is_h_auto(node: &StyledNode) -> bool {
7 if node
8 .value_ref("margin-left")
9 .map(|v| v.trim() == "auto")
10 .unwrap_or(false)
11 {
12 return true;
13 }
14 if let Some(m) = node.value_ref("margin") {
15 let mut it = m.split_whitespace();
16 let first = it.next();
17 let second = it.next();
18 let lr = second.or(first);
19 return lr.map(|s| s == "auto").unwrap_or(false);
20 }
21 false
22}
23
24pub(crate) fn box_main_auto_margins(b: &LayoutBox, is_row: bool) -> (bool, bool) {
27 fn side_auto(b: &LayoutBox, side: &str, short_idx_4: usize) -> bool {
28 if let Some(v) = box_style_value(b, side) {
29 return v.trim() == "auto";
30 }
31 if let Some(m) = box_style_value(b, "margin") {
32 let mut toks = [""; 4];
33 let mut count = 0;
34 for tok in m.split_whitespace().take(4) {
35 toks[count] = tok;
36 count += 1;
37 }
38 let val = match count {
40 1 => Some(toks[0]),
41 2 => {
42 if short_idx_4 == 0 || short_idx_4 == 2 {
44 Some(toks[0])
45 } else {
46 Some(toks[1])
47 }
48 }
49 3 => match short_idx_4 {
50 0 => Some(toks[0]),
51 2 => Some(toks[2]),
52 _ => Some(toks[1]), },
54 4 => Some(toks[short_idx_4]),
55 _ => None,
56 };
57 return val.map(|s| s == "auto").unwrap_or(false);
58 }
59 false
60 }
61 if is_row {
62 (
64 side_auto(b, "margin-left", 3),
65 side_auto(b, "margin-right", 1),
66 )
67 } else {
68 (
69 side_auto(b, "margin-top", 0),
70 side_auto(b, "margin-bottom", 2),
71 )
72 }
73}
74
75pub(crate) fn box_position(b: &LayoutBox) -> String {
77 box_style_value(b, "position")
78 .map(|v| v.trim().to_lowercase())
79 .unwrap_or_else(|| String::from("static"))
80}
81
82pub(crate) fn box_is_rtl(b: &LayoutBox) -> bool {
86 box_style_value(b, "direction")
87 .map(|v| v.trim().eq_ignore_ascii_case("rtl"))
88 .unwrap_or(false)
89}
90
91pub(crate) fn box_float(b: &LayoutBox) -> &'static str {
95 let rtl = box_is_rtl(b);
96 match box_style_value(b, "float").map(|v| v.trim().to_lowercase()) {
97 Some(ref v) if v == "left" => "left",
98 Some(ref v) if v == "right" => "right",
99 Some(ref v) if v == "inline-start" => {
100 if rtl {
101 "right"
102 } else {
103 "left"
104 }
105 }
106 Some(ref v) if v == "inline-end" => {
107 if rtl {
108 "left"
109 } else {
110 "right"
111 }
112 }
113 _ => "none",
114 }
115}
116
117pub(crate) fn box_clear(b: &LayoutBox) -> &'static str {
120 let rtl = box_is_rtl(b);
121 match box_style_value(b, "clear").map(|v| v.trim().to_lowercase()) {
122 Some(ref v) if v == "left" => "left",
123 Some(ref v) if v == "right" => "right",
124 Some(ref v) if v == "both" => "both",
125 Some(ref v) if v == "inline-start" => {
126 if rtl {
127 "right"
128 } else {
129 "left"
130 }
131 }
132 Some(ref v) if v == "inline-end" => {
133 if rtl {
134 "left"
135 } else {
136 "right"
137 }
138 }
139 _ => "none",
140 }
141}
142
143pub(crate) fn box_len(b: &LayoutBox, prop: &str, base: i32) -> Option<i32> {
145 parse_length_value(box_style_value(b, prop)).and_then(|lv| resolve_length_value(lv, base))
146}
147
148pub(crate) fn box_inset_len(b: &LayoutBox, side: &str, base: i32) -> Option<i32> {
152 if let Some(v) = box_len(b, side, base) {
153 return Some(v);
154 }
155 let side_idx = match side {
156 "top" => 0,
157 "right" => 1,
158 "bottom" => 2,
159 "left" => 3,
160 _ => return None,
161 };
162 let inset = box_style_value(b, "inset")?;
163 let mut toks = [""; 4];
164 let mut count = 0;
165 for t in inset.split_whitespace().take(4) {
166 toks[count] = t;
167 count += 1;
168 }
169 let tok = match count {
170 1 => Some(toks[0]),
171 2 => match side_idx {
172 0 | 2 => Some(toks[0]),
173 _ => Some(toks[1]),
174 },
175 3 => match side_idx {
176 0 => Some(toks[0]),
177 2 => Some(toks[2]),
178 _ => Some(toks[1]),
179 },
180 4 => Some(toks[side_idx]),
181 _ => None,
182 }?;
183 parse_length_value(Some(String::from(tok))).and_then(|lv| resolve_length_value(lv, base))
184}
185
186pub(crate) fn extract_fn_args(s: &str, name: &str) -> Option<String> {
188 let needle = alloc::format!("{}(", name);
189 let pos = s.find(&needle)?;
190 let after = s.get(pos + needle.len()..)?;
191 let end = after.find(')')?;
192 after.get(..end).map(String::from)
193}
194
195pub(crate) fn resolve_translate_len(tok: &str, base: i32) -> i32 {
207 let t = tok.trim();
208 if let Some(p) = t.strip_suffix('%') {
209 return p
210 .trim()
211 .parse::<f32>()
212 .map(|v| (v / 100.0 * base as f32) as i32)
213 .unwrap_or(0);
214 }
215 if let Some(p) = t.strip_suffix("rem") {
216 return p.trim().parse::<f32>().map(|v| (v * 16.0) as i32).unwrap_or(0);
217 }
218 if let Some(p) = t.strip_suffix("vh") {
219 let h = super::values::VIEWPORT_HINT_H.load(core::sync::atomic::Ordering::Relaxed);
220 return p
221 .trim()
222 .parse::<f32>()
223 .map(|v| (v / 100.0 * h as f32) as i32)
224 .unwrap_or(0);
225 }
226 if let Some(p) = t.strip_suffix("vw") {
227 let w = super::values::VIEWPORT_HINT_W.load(core::sync::atomic::Ordering::Relaxed);
228 return p
229 .trim()
230 .parse::<f32>()
231 .map(|v| (v / 100.0 * w as f32) as i32)
232 .unwrap_or(0);
233 }
234 let n = t.strip_suffix("px").unwrap_or(t).trim();
235 n.parse::<f32>().map(|v| v as i32).unwrap_or(0)
236}
237
238pub(crate) fn box_translate(b: &LayoutBox) -> (i32, i32) {
241 let t = match box_style_value(b, "transform") {
242 Some(v) => v,
243 None => return (0, 0),
244 };
245 let w = b.dimensions.content.width;
246 let h = b.dimensions.content.height;
247 if let Some(args) = extract_fn_args(&t, "translate") {
248 let mut parts = args.split(',');
249 let dx = parts
250 .next()
251 .map(|a| resolve_translate_len(a, w))
252 .unwrap_or(0);
253 let dy = parts
254 .next()
255 .map(|a| resolve_translate_len(a, h))
256 .unwrap_or(0);
257 return (dx, dy);
258 }
259 let dx = extract_fn_args(&t, "translateX")
260 .map(|a| resolve_translate_len(&a, w))
261 .unwrap_or(0);
262 let dy = extract_fn_args(&t, "translateY")
263 .map(|a| resolve_translate_len(&a, h))
264 .unwrap_or(0);
265 (dx, dy)
266}
267
268pub(crate) fn occ_is(occ: &[Vec<bool>], r: usize, c: usize) -> bool {
270 occ.get(r)
271 .and_then(|row| row.get(c))
272 .copied()
273 .unwrap_or(false)
274}
275pub(crate) fn occ_mark(occ: &mut Vec<Vec<bool>>, r: usize, c: usize) {
276 while occ.len() <= r {
277 occ.push(Vec::new());
278 }
279 let row = &mut occ[r];
280 while row.len() <= c {
281 row.push(false);
282 }
283 row[c] = true;
284}
285
286