i_slint_compiler/passes/
optimize_useless_rectangles.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! Remove the rectangles that serves no purposes
5//!
6//! Rectangles which do not draw anything and have no x or y don't need to be in
7//! the item tree, we can just remove them.
8
9use crate::langtype::ElementType;
10use crate::object_tree::*;
11use std::rc::Rc;
12
13pub fn optimize_useless_rectangles(root_component: &Rc<Component>) {
14    recurse_elem_including_sub_components(root_component, &(), &mut |parent_, _| {
15        let mut parent = parent_.borrow_mut();
16        let children = std::mem::take(&mut parent.children);
17
18        for elem in children {
19            if !can_optimize(&elem) {
20                parent.children.push(elem);
21                continue;
22            }
23
24            parent.children.extend(std::mem::take(&mut elem.borrow_mut().children));
25            if let Some(last) = parent.debug.last_mut() {
26                last.element_boundary = true;
27            }
28            parent.debug.extend(std::mem::take(&mut elem.borrow_mut().debug));
29
30            let enclosing = parent.enclosing_component.upgrade().unwrap();
31
32            for popup in enclosing.popup_windows.borrow_mut().iter_mut() {
33                if Rc::ptr_eq(&popup.parent_element, &elem) {
34                    // parent element is use for x/y, and the position of the removed element is 0,0
35                    popup.parent_element = parent_.clone();
36                }
37            }
38
39            enclosing.optimized_elements.borrow_mut().push(elem);
40        }
41    });
42}
43
44/// Check that this is a element we can optimize
45fn can_optimize(elem: &ElementRc) -> bool {
46    let e = elem.borrow();
47    if e.is_flickable_viewport || e.has_popup_child || e.is_component_placeholder {
48        return false;
49    };
50
51    if e.child_of_layout {
52        // The `LayoutItem` still has reference to this component, so we cannot remove it
53        return false;
54    }
55
56    let base_type = match &e.base_type {
57        ElementType::Builtin(base_type) if base_type.name == "Rectangle" => base_type,
58        ElementType::Builtin(base_type) if base_type.native_class.class_name == "Empty" => {
59            base_type
60        }
61        _ => return false,
62    };
63
64    let analysis = e.property_analysis.borrow();
65    for coord in ["x", "y"] {
66        if e.bindings.contains_key(coord) || analysis.get(coord).is_some_and(|a| a.is_set) {
67            return false;
68        }
69    }
70    if analysis.get("absolute-position").is_some_and(|a| a.is_read) {
71        return false;
72    }
73
74    // Check that no Rectangle property are set
75    !e.bindings.keys().chain(analysis.iter().filter(|(_, v)| v.is_set).map(|(k, _)| k)).any(|k| {
76        !e.property_declarations.contains_key(k.as_str())
77            && base_type.properties.contains_key(k.as_str())
78    }) && e.accessibility_props.0.is_empty()
79}