1use 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::collections::HashSet;
15use std::rc::Rc;
16
17pub fn ensure_window(
18 component: &Rc<Component>,
19 type_register: &TypeRegister,
20 style_metrics: &Rc<Component>,
21 diag: &mut BuildDiagnostics,
22) {
23 if component.inherits_popup_window.get() {
24 diag.push_error(
25 "PopupWindow cannot be the top level".into(),
26 &*component.root_element.borrow(),
27 );
28 }
29
30 if inherits_window(component) {
31 return; }
33
34 let window_type = type_register.lookup_builtin_element("Window").unwrap();
35
36 let win_elem = component.root_element.clone();
37
38 let mut win_elem_mut = win_elem.borrow_mut();
40 let new_root = Element {
41 id: std::mem::replace(&mut win_elem_mut.id, "root_window".into()),
42 base_type: std::mem::replace(&mut win_elem_mut.base_type, window_type),
43 bindings: Default::default(),
44 change_callbacks: Default::default(),
45 is_component_placeholder: false,
46 is_injected_wrapper_element: 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 shadowing_members: Default::default(),
52 named_references: Default::default(),
53 repeated: Default::default(),
54 states: Default::default(),
55 transitions: Default::default(),
56 match_elements: Default::default(),
57 child_of_layout: false,
58 child_of_flexbox: false,
59 parent_box_layout_orientation: None,
60 has_popup_child: false,
61 layout_info_prop: Default::default(),
62 layout_info_v_with_constraint: Default::default(),
63 layout_info_h_at_own_height: Default::default(),
64 height_is_literal: Default::default(),
65 default_fill_parent: Default::default(),
66 accessibility_props: Default::default(),
67 geometry_props: Default::default(),
68 is_flickable_content: false,
69 is_tooltip: false,
70 z_order: None,
71 item_index: Default::default(),
72 item_index_of_first_children: Default::default(),
73 grid_layout_cell: None,
74 debug: std::mem::take(&mut win_elem_mut.debug),
75
76 inline_depth: 0,
77 is_legacy_syntax: false,
78 slot_target: None,
79 forwarded_slots: Vec::new(),
80 };
81 let new_root = new_root.make_rc();
82 win_elem_mut.children.push(new_root.clone());
83 drop(win_elem_mut);
84
85 let make_two_way = |name: &'static str| {
86 new_root.borrow_mut().set_binding(
87 name.into(),
88 BindingExpression::new_two_way(
89 NamedReference::new(&win_elem, SmolStr::new_static(name)).into(),
90 ),
91 );
92 };
93 make_two_way("width");
94 make_two_way("height");
95
96 let mut must_update = HashSet::new();
97
98 let mut base_props: HashSet<SmolStr> =
99 new_root.borrow().base_type.property_list().into_iter().map(|x| x.0).collect();
100 base_props.extend(win_elem.borrow().real_bindings().map(|(name, _)| name.clone()));
101 for prop in base_props {
102 if prop == "width" || prop == "height" {
103 continue;
104 }
105
106 if win_elem.borrow().property_declarations.contains_key(&prop) {
107 continue;
108 }
109
110 must_update.insert(NamedReference::new(&win_elem, prop.clone()));
111
112 if let Some(b) = win_elem.borrow_mut().take_binding_including_synthetic(&prop) {
113 new_root.borrow_mut().set_binding(prop.clone(), b);
114 }
115 if let Some(a) = win_elem.borrow().property_analysis.borrow_mut().remove(&prop) {
116 new_root.borrow().property_analysis.borrow_mut().insert(prop.clone(), a);
117 }
118 }
119
120 crate::object_tree::visit_all_named_references(component, &mut |nr| {
121 if must_update.contains(nr) {
122 *nr = NamedReference::new(&new_root, nr.name().clone());
123 }
124 });
125
126 let fixup_element_reference = |expr: &mut Expression| {
129 if let Expression::FunctionCall { arguments, .. } = expr {
130 for arg in arguments.iter_mut() {
131 if matches!(arg, Expression::ElementReference(elr) if elr.upgrade().is_some_and(|elemrc| Rc::ptr_eq(&elemrc, &win_elem)))
132 {
133 *arg = Expression::ElementReference(Rc::downgrade(&new_root))
134 }
135 }
136 }
137 };
138
139 crate::object_tree::visit_all_expressions(component, |expr, _| {
140 expr.visit_recursive_mut(&mut |expr| fixup_element_reference(expr));
141 fixup_element_reference(expr)
142 });
143
144 component.root_element.borrow_mut().set_binding_if_not_set("background".into(), || {
145 Expression::Cast {
146 from: Expression::PropertyReference(NamedReference::new(
147 &style_metrics.root_element,
148 SmolStr::new_static("window-background"),
149 ))
150 .into(),
151 to: Type::Brush,
152 }
153 });
154
155 crate::object_tree::apply_default_type_properties(&mut component.root_element.borrow_mut());
158}
159
160pub fn inherits_window(component: &Rc<Component>) -> bool {
161 component.root_element.borrow().builtin_type().is_none_or(|b| {
162 matches!(
163 b.name.as_str(),
164 "Window" | "Dialog" | "WindowItem" | "PopupWindow" | "SystemTrayIcon"
165 )
166 })
167}
168
169#[cfg(feature = "slint-sc")]
173fn literal_alpha(expr: &Expression) -> Option<u8> {
174 match expr.ignore_debug_hooks() {
175 Expression::Cast { from, to: Type::Color | Type::Brush } => literal_alpha(from),
176 Expression::NumberLiteral(value, _) => Some((*value as u32 >> 24) as u8),
178 _ => None,
179 }
180}
181
182#[cfg(feature = "slint-sc")]
189pub fn check_sc_window_background(component: &Rc<Component>, diag: &mut BuildDiagnostics) {
190 if !inherits_window(component) {
192 return;
193 }
194 let root = component.root_element.borrow();
195 let Some(binding) = root.binding_cell_including_synthetic("background") else { return };
197 let binding = binding.borrow();
198 match literal_alpha(&binding.expression) {
199 Some(0xff) => {}
200 Some(_) => diag.slint_sc_error("A Window background that isn't fully opaque is", &*binding),
201 None => diag.slint_sc_error("A Window background that isn't a color literal is", &*binding),
202 }
203}
204
205pub fn warn_about_child_windows(doc: &crate::object_tree::Document, diag: &mut BuildDiagnostics) {
207 for component in &doc.inner_components {
208 crate::object_tree::recurse_elem_including_sub_components(
209 component,
210 &(),
211 &mut |elem, _| {
212 if Rc::ptr_eq(&component.root_element, elem) {
214 return;
215 }
216
217 let elem = elem.borrow();
218
219 let Some(builtin) = elem.builtin_type() else {
220 return;
221 };
222 let inheritance_hint = if let ElementType::Component(component) = &elem.base_type {
223 format!("\n(Note: {} inherits {})", component.id, builtin.name)
224 } else {
225 "".to_owned()
226 };
227 if matches!(builtin.name.as_str(), "Window" | "WindowItem") {
228 #[cfg(feature = "slint-sc")]
231 if diag.slint_sc {
232 diag.push_error(
233 format!(
234 "Instantiating Window as an element is not supported in Slint SC\
235 {inheritance_hint}"
236 ),
237 &*elem,
238 );
239 return;
240 }
241 diag.push_warning(
242 format!(
243 "Window elements as children do not create separate windows (this may change in the future)\n\
244 Consider using a PopupWindow instead\
245 {inheritance_hint}"
246 ),
247 &*elem,
248 );
249 } else if builtin.name.as_str() == "SystemTrayIcon" {
250 diag.push_error(
255 format!(
256 "SystemTrayIcon must be the root of an exported component, not a child element\
257 {inheritance_hint}"
258 ),
259 &*elem,
260 );
261 }
262 },
263 );
264 }
265}