// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial //! Make sure that the top level element of the component is always a Window use crate::expression_tree::{BindingExpression, BuiltinFunction, Expression}; use crate::langtype::Type; use crate::namedreference::NamedReference; use crate::object_tree::{Component, Element}; use crate::typeregister::TypeRegister; use std::cell::RefCell; use std::collections::HashSet; use std::rc::Rc; pub fn ensure_window( component: &Rc, type_register: &TypeRegister, style_metrics: &Rc, ) { if component.root_element.borrow().builtin_type().map_or(true, |b| { matches!(b.name.as_str(), "Window" | "Dialog" | "WindowItem" | "PopupWindow") }) { return; // already a window, nothing to do } let window_type = type_register.lookup_builtin_element("Window").unwrap(); let win_elem = component.root_element.clone(); // the old_root becomes the Window let mut win_elem_mut = win_elem.borrow_mut(); let new_root = Element { id: std::mem::replace(&mut win_elem_mut.id, "root_window".into()), base_type: std::mem::replace(&mut win_elem_mut.base_type, window_type), bindings: Default::default(), is_component_placeholder: false, property_analysis: Default::default(), children: std::mem::take(&mut win_elem_mut.children), enclosing_component: win_elem_mut.enclosing_component.clone(), property_declarations: Default::default(), named_references: Default::default(), repeated: Default::default(), states: Default::default(), transitions: Default::default(), child_of_layout: false, has_popup_child: false, layout_info_prop: Default::default(), default_fill_parent: Default::default(), accessibility_props: Default::default(), geometry_props: Default::default(), is_flickable_viewport: false, item_index: Default::default(), item_index_of_first_children: Default::default(), debug: std::mem::take(&mut win_elem_mut.debug), inline_depth: 0, is_legacy_syntax: false, }; let new_root = new_root.make_rc(); win_elem_mut.children.push(new_root.clone()); drop(win_elem_mut); let make_two_way = |name: &str| { new_root.borrow_mut().bindings.insert( name.into(), RefCell::new(BindingExpression::new_two_way(NamedReference::new(&win_elem, name))), ); }; make_two_way("width"); make_two_way("height"); let mut must_update = HashSet::new(); let mut base_props: HashSet = new_root.borrow().base_type.property_list().into_iter().map(|x| x.0).collect(); base_props.extend(win_elem.borrow().bindings.keys().cloned()); for prop in base_props { if prop == "width" || prop == "height" { continue; } if win_elem.borrow().property_declarations.contains_key(&prop) { continue; } must_update.insert(NamedReference::new(&win_elem, &prop)); if let Some(b) = win_elem.borrow_mut().bindings.remove(&prop) { new_root.borrow_mut().bindings.insert(prop.clone(), b); } if let Some(a) = win_elem.borrow().property_analysis.borrow_mut().remove(&prop) { new_root.borrow().property_analysis.borrow_mut().insert(prop.clone(), a); } } crate::object_tree::visit_all_named_references(component, &mut |nr| { if must_update.contains(nr) { *nr = NamedReference::new(&new_root, nr.name()); } }); // Fix up any ElementReferences for builtin member function calls, to not refer to the WindowItem, // as we swapped out the base_type. let fixup_element_reference = |expr: &mut Expression| match expr { Expression::FunctionCall { function, arguments, .. } if matches!( function.as_ref(), Expression::BuiltinFunctionReference(BuiltinFunction::ItemMemberFunction(..), ..) ) => { for arg in arguments.iter_mut() { if matches!(arg, Expression::ElementReference(elr) if elr.upgrade().map_or(false, |elemrc| Rc::ptr_eq(&elemrc, &win_elem)) ) { *arg = Expression::ElementReference(Rc::downgrade(&new_root)) } } } _ => {} }; crate::object_tree::visit_all_expressions(component, |expr, _| { expr.visit_recursive_mut(&mut |expr| fixup_element_reference(expr)); fixup_element_reference(expr) }); component.root_element.borrow_mut().set_binding_if_not_set("background".into(), || { Expression::Cast { from: Expression::PropertyReference(NamedReference::new( &style_metrics.root_element, "window-background", )) .into(), to: Type::Brush, } }); }