i_slint_compiler/passes/
clip.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//! Pass that lowers synthetic `clip` properties to Clip element
5
6use smol_str::{format_smolstr, SmolStr};
7use std::cell::RefCell;
8use std::rc::Rc;
9
10use crate::diagnostics::{BuildDiagnostics, Spanned};
11use crate::expression_tree::{BindingExpression, Expression, NamedReference};
12use crate::langtype::NativeClass;
13use crate::object_tree::{Component, Element, ElementRc};
14use crate::typeregister::TypeRegister;
15
16pub fn handle_clip(
17    component: &Rc<Component>,
18    type_register: &TypeRegister,
19    diag: &mut BuildDiagnostics,
20) {
21    let native_clip =
22        type_register.lookup_builtin_element("Clip").unwrap().as_builtin().native_class.clone();
23
24    crate::object_tree::recurse_elem_including_sub_components(
25        component,
26        &(),
27        &mut |elem_rc: &ElementRc, _| {
28            let elem = elem_rc.borrow();
29            if elem.native_class().is_some_and(|n| Rc::ptr_eq(&n, &native_clip)) {
30                return;
31            }
32            if elem.bindings.contains_key("clip")
33                || elem
34                    .property_analysis
35                    .borrow()
36                    .get("clip")
37                    .is_some_and(|a| a.is_set || a.is_linked)
38            {
39                match elem.builtin_type().as_ref().map(|ty| ty.name.as_str()) {
40                    Some("Rectangle") => {}
41                    Some("Path") => {
42                        // it's an actual property, so keep it as is
43                        return;
44                    }
45                    _ => {
46                        diag.push_error(
47                            "The 'clip' property can only be applied to a Rectangle or a Path for now".into(),
48                            &elem.bindings.get("clip").and_then(|x| x.borrow().span.clone()).unwrap_or_else(|| elem.to_source_location()),
49                        );
50                        return;
51                    }
52                }
53                drop(elem);
54                create_clip_element(elem_rc, &native_clip);
55            }
56        },
57    );
58}
59
60fn create_clip_element(parent_elem: &ElementRc, native_clip: &Rc<NativeClass>) {
61    let mut parent = parent_elem.borrow_mut();
62    let clip = Element::make_rc(Element {
63        id: format_smolstr!("{}-clip", parent.id),
64        base_type: crate::langtype::ElementType::Native(native_clip.clone()),
65        children: std::mem::take(&mut parent.children),
66        enclosing_component: parent.enclosing_component.clone(),
67        ..Element::default()
68    });
69
70    parent.children.push(clip.clone());
71    drop(parent); // NamedReference::new will borrow() the parent, so we can't hold a mutable ref
72    clip.borrow_mut().bindings = ["width", "height"]
73        .iter()
74        .map(|prop| {
75            (
76                SmolStr::new_static(prop),
77                RefCell::new(
78                    Expression::PropertyReference(NamedReference::new(
79                        parent_elem,
80                        SmolStr::new_static(prop),
81                    ))
82                    .into(),
83                ),
84            )
85        })
86        .collect();
87
88    copy_optional_binding(parent_elem, "border-width", &clip);
89    if super::border_radius::BORDER_RADIUS_PROPERTIES
90        .iter()
91        .any(|property_name| parent_elem.borrow().is_binding_set(property_name, true))
92    {
93        for optional_binding in super::border_radius::BORDER_RADIUS_PROPERTIES.iter() {
94            copy_optional_binding(parent_elem, optional_binding, &clip);
95        }
96    } else if parent_elem.borrow().bindings.contains_key("border-radius") {
97        for prop in super::border_radius::BORDER_RADIUS_PROPERTIES.iter() {
98            clip.borrow_mut().bindings.insert(
99                SmolStr::new(prop),
100                RefCell::new(
101                    Expression::PropertyReference(NamedReference::new(
102                        parent_elem,
103                        SmolStr::new_static("border-radius"),
104                    ))
105                    .into(),
106                ),
107            );
108        }
109    }
110    clip.borrow_mut().bindings.insert(
111        SmolStr::new_static("clip"),
112        BindingExpression::new_two_way(NamedReference::new(
113            parent_elem,
114            SmolStr::new_static("clip"),
115        ))
116        .into(),
117    );
118}
119
120fn copy_optional_binding(
121    parent_elem: &ElementRc,
122    optional_binding: &'static str,
123    clip: &ElementRc,
124) {
125    if parent_elem.borrow().bindings.contains_key(optional_binding) {
126        clip.borrow_mut().bindings.insert(
127            optional_binding.into(),
128            RefCell::new(
129                Expression::PropertyReference(NamedReference::new(
130                    parent_elem,
131                    SmolStr::new_static(optional_binding),
132                ))
133                .into(),
134            ),
135        );
136    }
137}