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) -> &'static str {
79 let Some(v) = box_style_value_ref(b, "position") else { return "static" };
80 let v = v.trim();
81 if v.eq_ignore_ascii_case("relative") {
82 "relative"
83 } else if v.eq_ignore_ascii_case("absolute") {
84 "absolute"
85 } else if v.eq_ignore_ascii_case("fixed") {
86 "fixed"
87 } else if v.eq_ignore_ascii_case("sticky") {
88 "sticky"
89 } else {
90 "static"
91 }
92}
93
94pub(crate) fn box_is_rtl(b: &LayoutBox) -> bool {
98 box_style_value(b, "direction")
99 .map(|v| v.trim().eq_ignore_ascii_case("rtl"))
100 .unwrap_or(false)
101}
102
103pub(crate) fn box_float(b: &LayoutBox) -> &'static str {
107 let rtl = box_is_rtl(b);
108 match box_style_value(b, "float").map(|v| v.trim().to_lowercase()) {
109 Some(ref v) if v == "left" => "left",
110 Some(ref v) if v == "right" => "right",
111 Some(ref v) if v == "inline-start" => {
112 if rtl {
113 "right"
114 } else {
115 "left"
116 }
117 }
118 Some(ref v) if v == "inline-end" => {
119 if rtl {
120 "left"
121 } else {
122 "right"
123 }
124 }
125 _ => "none",
126 }
127}
128
129pub(crate) fn box_clear(b: &LayoutBox) -> &'static str {
132 let rtl = box_is_rtl(b);
133 match box_style_value(b, "clear").map(|v| v.trim().to_lowercase()) {
134 Some(ref v) if v == "left" => "left",
135 Some(ref v) if v == "right" => "right",
136 Some(ref v) if v == "both" => "both",
137 Some(ref v) if v == "inline-start" => {
138 if rtl {
139 "right"
140 } else {
141 "left"
142 }
143 }
144 Some(ref v) if v == "inline-end" => {
145 if rtl {
146 "left"
147 } else {
148 "right"
149 }
150 }
151 _ => "none",
152 }
153}
154
155pub(crate) fn box_len(b: &LayoutBox, prop: &str, base: i32) -> Option<i32> {
157 parse_length_value(box_style_value(b, prop)).and_then(|lv| resolve_length_value(lv, base))
158}
159
160pub(crate) fn box_inset_len(b: &LayoutBox, side: &str, base: i32) -> Option<i32> {
164 if let Some(v) = box_len(b, side, base) {
165 return Some(v);
166 }
167 let side_idx = match side {
168 "top" => 0,
169 "right" => 1,
170 "bottom" => 2,
171 "left" => 3,
172 _ => return None,
173 };
174 let inset = box_style_value(b, "inset")?;
175 let mut toks = [""; 4];
176 let mut count = 0;
177 for t in inset.split_whitespace().take(4) {
178 toks[count] = t;
179 count += 1;
180 }
181 let tok = match count {
182 1 => Some(toks[0]),
183 2 => match side_idx {
184 0 | 2 => Some(toks[0]),
185 _ => Some(toks[1]),
186 },
187 3 => match side_idx {
188 0 => Some(toks[0]),
189 2 => Some(toks[2]),
190 _ => Some(toks[1]),
191 },
192 4 => Some(toks[side_idx]),
193 _ => None,
194 }?;
195 parse_length_value(Some(String::from(tok))).and_then(|lv| resolve_length_value(lv, base))
196}
197
198pub(crate) fn extract_fn_args(s: &str, name: &str) -> Option<String> {
200 let needle = alloc::format!("{}(", name);
201 let pos = s.find(&needle)?;
202 let after = s.get(pos + needle.len()..)?;
203 let end = after.find(')')?;
204 after.get(..end).map(String::from)
205}
206
207pub(crate) fn resolve_translate_len(tok: &str, base: i32) -> i32 {
219 let t = tok.trim();
220 if let Some(p) = t.strip_suffix('%') {
221 return p
222 .trim()
223 .parse::<f32>()
224 .map(|v| (v / 100.0 * base as f32) as i32)
225 .unwrap_or(0);
226 }
227 if let Some(p) = t.strip_suffix("rem") {
228 return p.trim().parse::<f32>().map(|v| (v * 16.0) as i32).unwrap_or(0);
229 }
230 if let Some(p) = t.strip_suffix("vh") {
231 let h = super::values::VIEWPORT_HINT_H.load(core::sync::atomic::Ordering::Relaxed);
232 return p
233 .trim()
234 .parse::<f32>()
235 .map(|v| (v / 100.0 * h as f32) as i32)
236 .unwrap_or(0);
237 }
238 if let Some(p) = t.strip_suffix("vw") {
239 let w = super::values::VIEWPORT_HINT_W.load(core::sync::atomic::Ordering::Relaxed);
240 return p
241 .trim()
242 .parse::<f32>()
243 .map(|v| (v / 100.0 * w as f32) as i32)
244 .unwrap_or(0);
245 }
246 let n = t.strip_suffix("px").unwrap_or(t).trim();
247 n.parse::<f32>().map(|v| v as i32).unwrap_or(0)
248}
249
250pub(crate) fn box_translate(b: &LayoutBox) -> (i32, i32) {
253 let t = match box_style_value(b, "transform") {
254 Some(v) => v,
255 None => return (0, 0),
256 };
257 let w = b.dimensions.content.width;
258 let h = b.dimensions.content.height;
259 if let Some(args) = extract_fn_args(&t, "translate") {
260 let mut parts = args.split(',');
261 let dx = parts
262 .next()
263 .map(|a| resolve_translate_len(a, w))
264 .unwrap_or(0);
265 let dy = parts
266 .next()
267 .map(|a| resolve_translate_len(a, h))
268 .unwrap_or(0);
269 return (dx, dy);
270 }
271 let dx = extract_fn_args(&t, "translateX")
272 .map(|a| resolve_translate_len(&a, w))
273 .unwrap_or(0);
274 let dy = extract_fn_args(&t, "translateY")
275 .map(|a| resolve_translate_len(&a, h))
276 .unwrap_or(0);
277 (dx, dy)
278}
279
280pub(crate) fn occ_is(occ: &[Vec<bool>], r: usize, c: usize) -> bool {
282 occ.get(r)
283 .and_then(|row| row.get(c))
284 .copied()
285 .unwrap_or(false)
286}
287pub(crate) fn occ_mark(occ: &mut Vec<Vec<bool>>, r: usize, c: usize) {
288 while occ.len() <= r {
289 occ.push(Vec::new());
290 }
291 let row = &mut occ[r];
292 while row.len() <= c {
293 row.push(false);
294 }
295 row[c] = true;
296}
297
298