i_slint_compiler/passes/
windows.rs1use crate::diagnostics::BuildDiagnostics;
8use crate::expression_tree::{BindingExpression, Expression};
9use crate::langtype::{ElementType, Type};
10use crate::namedreference::NamedReference;
11use crate::object_tree::{Component, Element};
12use crate::typeregister::TypeRegister;
13use smol_str::SmolStr;
14use std::cell::RefCell;
15use std::collections::HashSet;
16use std::rc::Rc;
17
18pub fn ensure_window(
19 component: &Rc<Component>,
20 type_register: &TypeRegister,
21 style_metrics: &Rc<Component>,
22 diag: &mut BuildDiagnostics,
23) {
24 if component.inherits_popup_window.get() {
25 diag.push_error(
26 "PopupWindow cannot be the top level".into(),
27 &*component.root_element.borrow(),
28 );
29 }
30
31 if inherits_window(component) {
32 return; }
34
35 let window_type = type_register.lookup_builtin_element("Window").unwrap();
36
37 let win_elem = component.root_element.clone();
38
39 let mut win_elem_mut = win_elem.borrow_mut();
41 let new_root = Element {
42 id: std::mem::replace(&mut win_elem_mut.id, "root_window".into()),
43 base_type: std::mem::replace(&mut win_elem_mut.base_type, window_type),
44 bindings: Default::default(),
45 change_callbacks: Default::default(),
46 is_component_placeholder: false,
47 property_analysis: Default::default(),
48 children: std::mem::take(&mut win_elem_mut.children),
49 enclosing_component: win_elem_mut.enclosing_component.clone(),
50 property_declarations: Default::default(),
51 named_references: Default::default(),
52 repeated: Default::default(),
53 states: Default::default(),
54 transitions: Default::default(),
55 child_of_layout: false,
56 has_popup_child: false,
57 layout_info_prop: Default::default(),
58 layout_info_v_with_constraint: Default::default(),
59 layout_info_h_with_constraint: Default::default(),
60 default_fill_parent: Default::default(),
61 accessibility_props: Default::default(),
62 geometry_props: Default::default(),
63 is_flickable_viewport: false,
64 is_tooltip: false,
65 item_index: Default::default(),
66 item_index_of_first_children: Default::default(),
67 grid_layout_cell: None,
68 debug: std::mem::take(&mut win_elem_mut.debug),
69
70 inline_depth: 0,
71 is_legacy_syntax: false,
72 };
73 let new_root = new_root.make_rc();
74 win_elem_mut.children.push(new_root.clone());
75 drop(win_elem_mut);
76
77 let make_two_way = |name: &'static str| {
78 new_root.borrow_mut().bindings.insert(
79 name.into(),
80 RefCell::new(BindingExpression::new_two_way(
81 NamedReference::new(&win_elem, SmolStr::new_static(name)).into(),
82 )),
83 );
84 };
85 make_two_way("width");
86 make_two_way("height");
87
88 let mut must_update = HashSet::new();
89
90 let mut base_props: HashSet<SmolStr> =
91 new_root.borrow().base_type.property_list().into_iter().map(|x| x.0).collect();
92 base_props.extend(win_elem.borrow().bindings.keys().cloned());
93 for prop in base_props {
94 if prop == "width" || prop == "height" {
95 continue;
96 }
97
98 if win_elem.borrow().property_declarations.contains_key(&prop) {
99 continue;
100 }
101
102 must_update.insert(NamedReference::new(&win_elem, prop.clone()));
103
104 if let Some(b) = win_elem.borrow_mut().bindings.remove(&prop) {
105 new_root.borrow_mut().bindings.insert(prop.clone(), b);
106 }
107 if let Some(a) = win_elem.borrow().property_analysis.borrow_mut().remove(&prop) {
108 new_root.borrow().property_analysis.borrow_mut().insert(prop.clone(), a);
109 }
110 }
111
112 crate::object_tree::visit_all_named_references(component, &mut |nr| {
113 if must_update.contains(nr) {
114 *nr = NamedReference::new(&new_root, nr.name().clone());
115 }
116 });
117
118 let fixup_element_reference = |expr: &mut Expression| {
121 if let Expression::FunctionCall { arguments, .. } = expr {
122 for arg in arguments.iter_mut() {
123 if matches!(arg, Expression::ElementReference(elr) if elr.upgrade().is_some_and(|elemrc| Rc::ptr_eq(&elemrc, &win_elem)))
124 {
125 *arg = Expression::ElementReference(Rc::downgrade(&new_root))
126 }
127 }
128 }
129 };
130
131 crate::object_tree::visit_all_expressions(component, |expr, _| {
132 expr.visit_recursive_mut(&mut |expr| fixup_element_reference(expr));
133 fixup_element_reference(expr)
134 });
135
136 component.root_element.borrow_mut().set_binding_if_not_set("background".into(), || {
137 Expression::Cast {
138 from: Expression::PropertyReference(NamedReference::new(
139 &style_metrics.root_element,
140 SmolStr::new_static("window-background"),
141 ))
142 .into(),
143 to: Type::Brush,
144 }
145 });
146}
147
148pub fn inherits_window(component: &Rc<Component>) -> bool {
149 component.root_element.borrow().builtin_type().is_none_or(|b| {
150 matches!(
151 b.name.as_str(),
152 "Window" | "Dialog" | "WindowItem" | "PopupWindow" | "SystemTrayIcon"
153 )
154 })
155}
156
157pub fn warn_about_child_windows(doc: &crate::object_tree::Document, diag: &mut BuildDiagnostics) {
159 for component in &doc.inner_components {
160 crate::object_tree::recurse_elem_including_sub_components(
161 component,
162 &(),
163 &mut |elem, _| {
164 if Rc::ptr_eq(&component.root_element, elem) {
166 return;
167 }
168
169 let elem = elem.borrow();
170
171 let Some(builtin) = elem.builtin_type() else {
172 return;
173 };
174 let inheritance_hint = if let ElementType::Component(component) = &elem.base_type {
175 format!("\n(Note: {} inherits {})", component.id, builtin.name)
176 } else {
177 "".to_owned()
178 };
179 if matches!(builtin.name.as_str(), "Window" | "WindowItem") {
180 diag.push_warning(
181 format!(
182 "Window elements as children do not create separate windows (this may change in the future)\n\
183 Consider using a PopupWindow instead\
184 {inheritance_hint}"
185 ),
186 &*elem,
187 );
188 } else if builtin.name.as_str() == "SystemTrayIcon" {
189 diag.push_error(
194 format!(
195 "SystemTrayIcon must be the root of an exported component, not a child element\
196 {inheritance_hint}"
197 ),
198 &*elem,
199 );
200 }
201 },
202 );
203 }
204}