atmos/os_lib/layout/
pseudo_box.rs1extern crate alloc;
14use alloc::collections::BTreeMap;
15use alloc::string::String;
16
17const INHERITED: &[&str] = &["color", "font-family", "font-size", "font-weight", "font-style", "line-height", "visibility"];
20
21pub fn pseudo_values(parent: &BTreeMap<String, String>, which: &str) -> Option<BTreeMap<String, String>> {
24 let prefix = alloc::format!("x-{}-", which);
25 let content = parent.get(&alloc::format!("{}content", prefix))?;
26 if content.trim() == "none" || content.trim() == "normal" {
27 return None;
28 }
29 let mut out = BTreeMap::new();
30 for key in INHERITED {
31 if let Some(v) = parent.get(*key) {
32 out.insert(String::from(*key), v.clone());
33 }
34 }
35 for (k, v) in parent.range(prefix.clone()..) {
36 let Some(name) = k.strip_prefix(prefix.as_str()) else { break };
37 out.insert(String::from(name), v.clone());
38 }
39 if out.get("display").map(|d| d.trim()) == Some("none") {
40 return None;
41 }
42 match out.get("position").map(|p| p.trim()) {
43 Some("absolute") | Some("fixed") => {}
44 _ => return None,
45 }
46 out.remove("content");
48 out.insert(String::from("display"), String::from("block"));
49 Some(out)
50}
51
52fn len(v: Option<&String>, base: i32) -> Option<i32> {
53 let v = v?.trim();
54 if v.is_empty() || v == "auto" {
55 return None;
56 }
57 super::values::resolve_len_or_pct(v, base)
58}
59
60pub fn abs_rect(cb: (i32, i32, i32, i32), v: &BTreeMap<String, String>) -> (i32, i32, i32, i32) {
64 let (cx, cy, cw, ch) = cb;
65 let mut sides: [Option<String>; 4] = [
67 v.get("top").cloned(),
68 v.get("right").cloned(),
69 v.get("bottom").cloned(),
70 v.get("left").cloned(),
71 ];
72 if let Some(inset) = v.get("inset") {
73 let p: alloc::vec::Vec<&str> = inset.split_whitespace().collect();
74 let pick = |i: usize| -> String {
75 String::from(match p.len() {
76 1 => p[0],
77 2 => p[i % 2],
78 3 => [p[0], p[1], p[2], p[1]][i],
79 _ => p.get(i).copied().unwrap_or("auto"),
80 })
81 };
82 if !p.is_empty() {
83 for (i, s) in sides.iter_mut().enumerate() {
84 if s.is_none() {
85 *s = Some(pick(i));
86 }
87 }
88 }
89 }
90 let top = len(sides[0].as_ref(), ch);
91 let right = len(sides[1].as_ref(), cw);
92 let bottom = len(sides[2].as_ref(), ch);
93 let left = len(sides[3].as_ref(), cw);
94 let w = len(v.get("width"), cw)
95 .or_else(|| Some(cw - left? - right?))
96 .unwrap_or(0)
97 .max(0);
98 let h = len(v.get("height"), ch)
99 .or_else(|| Some(ch - top? - bottom?))
100 .unwrap_or(0)
101 .max(0);
102 let x = match (left, right) {
103 (Some(l), _) => cx + l,
104 (None, Some(r)) => cx + cw - r - w,
105 _ => cx,
106 };
107 let y = match (top, bottom) {
108 (Some(t), _) => cy + t,
109 (None, Some(b)) => cy + ch - b - h,
110 _ => cy,
111 };
112 (x, y, w, h)
113}
114
115pub fn border_px(v: &BTreeMap<String, String>) -> i32 {
118 let src = v.get("border-width").or_else(|| v.get("border"));
119 let Some(src) = src else { return 0 };
120 if src.split_whitespace().any(|t| t == "none" || t == "hidden") {
121 return 0;
122 }
123 src.split_whitespace()
124 .find_map(|t| match t {
125 "thin" => Some(1),
126 "medium" => Some(3),
127 "thick" => Some(5),
128 _ => super::values::resolve_len_or_pct(t, 0).filter(|_| !t.ends_with('%')),
129 })
130 .unwrap_or(0)
131 .max(0)
132}