i_slint_compiler/passes/
z_order.rs1use std::cell::RefCell;
10use std::rc::Rc;
11
12use crate::diagnostics::Spanned;
13use crate::expression_tree::{BindingExpression, Expression, Unit};
14use crate::langtype::ElementType;
15use crate::object_tree::{Component, ElementRc};
16
17pub fn reorder_by_z_order(root_component: &Rc<Component>) {
18 crate::object_tree::recurse_elem_including_sub_components(
19 root_component,
20 &(),
21 &mut |elem: &ElementRc, _| {
22 reorder_children_by_zorder(elem);
23 },
24 )
25}
26
27fn reorder_children_by_zorder(elem: &Rc<RefCell<crate::object_tree::Element>>) {
28 let mut has_any_z = false;
29 let mut has_dynamic_z = false;
30
31 for child_elm in elem.borrow().children.iter() {
32 if mark_per_instance_z(child_elm) {
33 has_any_z = true;
34 has_dynamic_z = true;
35 } else if let Some(value) = z_binding_value(child_elm) {
36 has_any_z = true;
37 has_dynamic_z |= value.is_none();
38 }
39 }
40
41 if !has_any_z {
42 return;
43 }
44
45 if has_dynamic_z {
46 setup_dynamic_z_order(elem);
47 } else {
48 reorder_static_z(elem);
49 }
50}
51
52fn mark_per_instance_z(child_elm: &ElementRc) -> bool {
54 use crate::namedreference::NamedReference;
55
56 let child = child_elm.borrow();
57 if child.repeated.is_none() {
58 return false;
59 }
60 let ElementType::Component(c) = &child.base_type else { return false };
61 let c = c.clone();
62 drop(child);
63
64 {
65 let root = c.root_element.borrow();
66 let Some(b) = root.binding("z") else { return false };
67 if try_eval_const_expr(&b.expression).is_some() {
68 return false;
70 }
71 }
72
73 child_elm.borrow_mut().z_order = Some(crate::object_tree::ZOrder::PerInstance(
74 NamedReference::new(&c.root_element, smol_str::SmolStr::new_static("z")),
75 ));
76 c.root_element
78 .borrow()
79 .property_analysis
80 .borrow_mut()
81 .entry(smol_str::SmolStr::new_static("z"))
82 .or_default()
83 .is_read = true;
84 true
85}
86
87fn reorder_static_z(elem: &Rc<RefCell<crate::object_tree::Element>>) {
91 let take_constant_z = |elem: &ElementRc| -> Option<f64> {
92 elem.borrow().binding("z")?;
95 let e = elem.borrow_mut().take_binding("z")?;
96 let z = try_eval_const_expr(&e.expression);
97 debug_assert!(z.is_some(), "non-constant z on the static path");
98 Some(z.unwrap_or(0.))
99 };
100 let mut children_z_order = Vec::new();
101 for (idx, child_elm) in elem.borrow().children.iter().enumerate() {
102 let z = take_constant_z(child_elm);
103 let z = z.or_else(|| {
104 child_elm.borrow().repeated.as_ref()?;
105 if let ElementType::Component(c) = &child_elm.borrow().base_type {
106 take_constant_z(&c.root_element)
107 } else {
108 None
109 }
110 });
111
112 if let Some(z) = z {
113 if children_z_order.is_empty() {
114 for i in 0..idx {
115 children_z_order.push((i, 0.));
116 }
117 }
118 children_z_order.push((idx, z));
119 } else if !children_z_order.is_empty() {
120 children_z_order.push((idx, 0.));
121 }
122 }
123
124 if !children_z_order.is_empty() {
125 children_z_order.sort_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap());
126
127 let new_children = children_z_order
128 .into_iter()
129 .map(|(idx, _)| elem.borrow().children[idx].clone())
130 .collect();
131 elem.borrow_mut().children = new_children;
132 }
133}
134
135fn setup_dynamic_z_order(elem: &Rc<RefCell<crate::object_tree::Element>>) {
139 use crate::namedreference::NamedReference;
140 use crate::object_tree::ZOrder;
141
142 for child_elm in elem.borrow().children.iter() {
143 if child_elm.borrow().z_order.is_some() {
144 continue;
147 }
148 let z_order = if child_elm.borrow().repeated.is_some() {
149 let mut z_val = 0.;
153 if let ElementType::Component(c) = &child_elm.borrow().base_type {
154 let has_binding = c.root_element.borrow().binding("z").is_some();
156 if has_binding && let Some(e) = c.root_element.borrow_mut().take_binding("z") {
157 let z = try_eval_const_expr(&e.expression);
158 debug_assert!(z.is_some(), "handled by mark_per_instance_z otherwise");
159 z_val = z.unwrap_or(0.);
160 }
161 }
162 ZOrder::Constant(z_val as f32)
163 } else if let Some(z_val) = constant_z(child_elm) {
164 child_elm.borrow_mut().take_binding("z");
165 ZOrder::Constant(z_val as f32)
166 } else {
167 if child_elm.borrow().binding_cell_including_synthetic("z").is_none() {
170 let span = child_elm.borrow().to_source_location();
171 child_elm.borrow_mut().set_binding(
172 smol_str::SmolStr::new_static("z"),
173 BindingExpression::new_with_span(
174 Expression::NumberLiteral(0., Unit::None),
175 span,
176 ),
177 );
178 }
179 ZOrder::Dynamic(NamedReference::new(child_elm, smol_str::SmolStr::new_static("z")))
180 };
181 child_elm.borrow_mut().z_order = Some(z_order);
182 }
183}
184
185fn z_binding_value(child_elm: &ElementRc) -> Option<Option<f64>> {
189 let child = child_elm.borrow();
190 if let Some(b) = child.binding("z") {
191 return Some(try_eval_const_expr(&b.expression));
192 }
193 if child.repeated.is_some()
194 && let ElementType::Component(c) = &child.base_type
195 && let Some(b) = c.root_element.borrow().binding("z")
196 {
197 return Some(try_eval_const_expr(&b.expression));
198 }
199 None
200}
201
202fn constant_z(child_elm: &ElementRc) -> Option<f64> {
205 let child = child_elm.borrow();
206 if child.property_analysis.borrow().get("z").is_some_and(|a| a.is_set || a.is_linked) {
207 return None;
208 }
209 match child.binding("z") {
210 None => Some(0.),
211 Some(b) => {
212 if b.two_way_bindings.is_empty() && b.animation.is_none() {
213 try_eval_const_expr(&b.expression)
214 } else {
215 None
216 }
217 }
218 }
219}
220
221fn try_eval_const_expr(expression: &Expression) -> Option<f64> {
222 match expression.ignore_debug_hooks() {
223 Expression::NumberLiteral(v, Unit::None) => Some(*v),
224 Expression::Cast { from, .. } => try_eval_const_expr(from),
225 Expression::UnaryOp { sub, op: '-' } => try_eval_const_expr(sub).map(|v| -v),
226 Expression::UnaryOp { sub, op: '+' } => try_eval_const_expr(sub),
227 _ => None,
228 }
229}