Skip to main content

i_slint_compiler/object_tree/
forward_inherited_expression.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
4use super::{BindingExpression, ElementRc, ElementType, PropertyDeclaration};
5use crate::expression_tree::{Callable, Expression};
6use crate::langtype::Type;
7use crate::namedreference::NamedReference;
8use crate::symbol_counters::SymbolCounters;
9use std::collections::HashMap;
10use std::rc::{Rc, Weak};
11
12#[derive(Default)]
13pub(crate) struct ForwardedReferenceCache {
14    forwarded_references: HashMap<NamedReference, NamedReference>,
15}
16
17pub(crate) enum InheritedExpression {
18    Expression(Expression),
19    TwoWayBinding,
20    Unbound,
21}
22
23pub(crate) fn forward_inherited_expression(
24    element: &ElementRc,
25    property_name: &str,
26    symbol_counters: &SymbolCounters,
27    forwarded_references: &mut ForwardedReferenceCache,
28) -> InheritedExpression {
29    let ElementType::Component(base_component) = &element.borrow().base_type else {
30        return InheritedExpression::Unbound;
31    };
32    let mut current_base_root = base_component.root_element.clone();
33
34    loop {
35        let binding = current_base_root
36            .borrow()
37            .binding(property_name)
38            .map(|binding| (!binding.two_way_bindings.is_empty(), binding.expression.clone()));
39        if let Some((is_two_way_binding, mut expression)) = binding {
40            if is_two_way_binding {
41                return InheritedExpression::TwoWayBinding;
42            }
43            if !matches!(expression, Expression::Invalid) {
44                rebase_expression_to_instance(
45                    &mut expression,
46                    &current_base_root,
47                    element,
48                    symbol_counters,
49                    forwarded_references,
50                );
51                return InheritedExpression::Expression(expression);
52            }
53        }
54
55        let next_source_root = {
56            let source_root = current_base_root.borrow();
57            let ElementType::Component(base_component) = &source_root.base_type else {
58                return InheritedExpression::Unbound;
59            };
60            base_component.root_element.clone()
61        };
62        current_base_root = next_source_root;
63    }
64}
65
66fn rebase_expression_to_instance(
67    expression: &mut Expression,
68    base_root_element: &ElementRc,
69    target_instance: &ElementRc,
70    symbol_counters: &SymbolCounters,
71    forwarded_references: &mut ForwardedReferenceCache,
72) {
73    expression.visit_recursive_mut(&mut |expression| match expression {
74        Expression::PropertyReference(named_reference)
75        | Expression::FunctionCall {
76            function: Callable::Callback(named_reference) | Callable::Function(named_reference),
77            ..
78        } => {
79            let referenced_element = named_reference.element();
80            if Rc::ptr_eq(&referenced_element, base_root_element) {
81                *named_reference =
82                    NamedReference::new(target_instance, named_reference.name().clone());
83            } else if Weak::ptr_eq(
84                &referenced_element.borrow().enclosing_component,
85                &base_root_element.borrow().enclosing_component,
86            ) {
87                let forwarded_reference = forward_reference(
88                    named_reference,
89                    base_root_element,
90                    symbol_counters,
91                    forwarded_references,
92                );
93                *named_reference =
94                    NamedReference::new(target_instance, forwarded_reference.name().clone());
95            }
96        }
97        _ => (),
98    });
99}
100
101fn forward_reference(
102    original: &NamedReference,
103    base_root: &ElementRc,
104    symbol_counters: &SymbolCounters,
105    forwarded_references: &mut ForwardedReferenceCache,
106) -> NamedReference {
107    if let Some(existing) = forwarded_references.forwarded_references.get(original) {
108        return existing.clone();
109    }
110
111    let property_type = original.ty();
112    let property_name = symbol_counters.generate_name("forward_reference_");
113    let binding = match &property_type {
114        Type::Callback(function) | Type::Function(function) => {
115            let arguments = function
116                .args
117                .iter()
118                .enumerate()
119                .map(|(index, argument_type)| Expression::FunctionParameterReference {
120                    index,
121                    ty: argument_type.clone(),
122                })
123                .collect();
124            let function = if matches!(property_type, Type::Callback(_)) {
125                Callable::Callback(original.clone())
126            } else {
127                Callable::Function(original.clone())
128            };
129            Expression::FunctionCall { function, arguments, source_location: None }
130        }
131        _ => Expression::PropertyReference(original.clone()),
132    };
133
134    base_root.borrow_mut().property_declarations.insert(
135        property_name.clone(),
136        PropertyDeclaration { property_type, ..PropertyDeclaration::default() },
137    );
138    base_root.borrow_mut().set_binding(property_name.clone(), BindingExpression::from(binding));
139
140    let forwarded_reference = NamedReference::new(base_root, property_name);
141    forwarded_references.forwarded_references.insert(original.clone(), forwarded_reference.clone());
142    forwarded_reference
143}