Skip to main content

slint_interpreter/
eval.rs

1// Copyright © SixtyFPS GmbH <[email protected]>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use crate::api::{SetPropertyError, Struct, Value};
5use crate::dynamic_item_tree::{CallbackHandler, InstanceRef};
6use core::pin::Pin;
7use corelib::graphics::{
8    ConicGradientBrush, GradientStop, LinearGradientBrush, PathElement, RadialGradientBrush,
9};
10use corelib::input::FocusReason;
11use corelib::items::{ColorScheme, ItemRc, ItemRef, PropertyAnimation, WindowItem};
12use corelib::menus::{Menu, MenuFromItemTree};
13use corelib::model::{Model, ModelExt, ModelRc, VecModel};
14use corelib::rtti::AnimatedBindingKind;
15use corelib::window::WindowInner;
16use corelib::{Brush, Color, PathData, SharedString, SharedVector};
17use i_slint_compiler::expression_tree::{
18    BuiltinFunction, Callable, EasingCurve, Expression, MinMaxOp, Path as ExprPath,
19    PathElement as ExprPathElement,
20};
21use i_slint_compiler::langtype::Type;
22use i_slint_compiler::namedreference::NamedReference;
23use i_slint_compiler::object_tree::ElementRc;
24use i_slint_core as corelib;
25use smol_str::SmolStr;
26use std::collections::HashMap;
27use std::rc::Rc;
28
29pub trait ErasedPropertyInfo {
30    fn get(&self, item: Pin<ItemRef>) -> Value;
31    fn set(
32        &self,
33        item: Pin<ItemRef>,
34        value: Value,
35        animation: Option<PropertyAnimation>,
36    ) -> Result<(), ()>;
37    fn set_binding(
38        &self,
39        item: Pin<ItemRef>,
40        binding: Box<dyn Fn() -> Value>,
41        animation: AnimatedBindingKind,
42    );
43    fn offset(&self) -> usize;
44
45    /// Safety: Property2 must be a (pinned) pointer to a `Property<T>`
46    /// where T is the same T as the one represented by this property.
47    unsafe fn link_two_ways(&self, item: Pin<ItemRef>, property2: *const ());
48
49    fn prepare_for_two_way_binding(&self, item: Pin<ItemRef>) -> Pin<Rc<corelib::Property<Value>>>;
50
51    fn link_two_way_with_map(
52        &self,
53        item: Pin<ItemRef>,
54        property2: Pin<Rc<corelib::Property<Value>>>,
55        map: Option<Rc<dyn corelib::rtti::TwoWayBindingMapping<Value>>>,
56    );
57}
58
59impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedPropertyInfo
60    for &'static dyn corelib::rtti::PropertyInfo<Item, Value>
61{
62    fn get(&self, item: Pin<ItemRef>) -> Value {
63        (*self).get(ItemRef::downcast_pin(item).unwrap()).unwrap()
64    }
65    fn set(
66        &self,
67        item: Pin<ItemRef>,
68        value: Value,
69        animation: Option<PropertyAnimation>,
70    ) -> Result<(), ()> {
71        (*self).set(ItemRef::downcast_pin(item).unwrap(), value, animation)
72    }
73    fn set_binding(
74        &self,
75        item: Pin<ItemRef>,
76        binding: Box<dyn Fn() -> Value>,
77        animation: AnimatedBindingKind,
78    ) {
79        (*self).set_binding(ItemRef::downcast_pin(item).unwrap(), binding, animation).unwrap();
80    }
81    fn offset(&self) -> usize {
82        (*self).offset()
83    }
84    unsafe fn link_two_ways(&self, item: Pin<ItemRef>, property2: *const ()) {
85        // Safety: ErasedPropertyInfo::link_two_ways and PropertyInfo::link_two_ways have the same safety requirement
86        unsafe { (*self).link_two_ways(ItemRef::downcast_pin(item).unwrap(), property2) }
87    }
88
89    fn prepare_for_two_way_binding(&self, item: Pin<ItemRef>) -> Pin<Rc<corelib::Property<Value>>> {
90        (*self).prepare_for_two_way_binding(ItemRef::downcast_pin(item).unwrap())
91    }
92
93    fn link_two_way_with_map(
94        &self,
95        item: Pin<ItemRef>,
96        property2: Pin<Rc<corelib::Property<Value>>>,
97        map: Option<Rc<dyn corelib::rtti::TwoWayBindingMapping<Value>>>,
98    ) {
99        (*self).link_two_way_with_map(ItemRef::downcast_pin(item).unwrap(), property2, map)
100    }
101}
102
103pub trait ErasedCallbackInfo {
104    fn call(&self, item: Pin<ItemRef>, args: &[Value]) -> Value;
105    fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>);
106}
107
108impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedCallbackInfo
109    for &'static dyn corelib::rtti::CallbackInfo<Item, Value>
110{
111    fn call(&self, item: Pin<ItemRef>, args: &[Value]) -> Value {
112        (*self).call(ItemRef::downcast_pin(item).unwrap(), args).unwrap()
113    }
114
115    fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>) {
116        (*self).set_handler(ItemRef::downcast_pin(item).unwrap(), handler).unwrap()
117    }
118}
119
120impl corelib::rtti::ValueType for Value {}
121
122#[derive(Clone)]
123pub(crate) enum ComponentInstance<'a, 'id> {
124    InstanceRef(InstanceRef<'a, 'id>),
125    GlobalComponent(Pin<Rc<dyn crate::global_component::GlobalComponent>>),
126}
127
128/// The local variable needed for binding evaluation
129pub struct EvalLocalContext<'a, 'id> {
130    local_variables: HashMap<SmolStr, Value>,
131    function_arguments: Vec<Value>,
132    pub(crate) component_instance: InstanceRef<'a, 'id>,
133    /// When Some, a return statement was executed and one must stop evaluating
134    return_value: Option<Value>,
135}
136
137impl<'a, 'id> EvalLocalContext<'a, 'id> {
138    pub fn from_component_instance(component: InstanceRef<'a, 'id>) -> Self {
139        Self {
140            local_variables: Default::default(),
141            function_arguments: Default::default(),
142            component_instance: component,
143            return_value: None,
144        }
145    }
146
147    /// Create a context for a function and passing the arguments
148    pub fn from_function_arguments(
149        component: InstanceRef<'a, 'id>,
150        function_arguments: Vec<Value>,
151    ) -> Self {
152        Self {
153            component_instance: component,
154            function_arguments,
155            local_variables: Default::default(),
156            return_value: None,
157        }
158    }
159}
160
161/// Evaluate an expression and return a Value as the result of this expression
162pub fn eval_expression(expression: &Expression, local_context: &mut EvalLocalContext) -> Value {
163    if let Some(r) = &local_context.return_value {
164        return r.clone();
165    }
166    match expression {
167        Expression::Invalid => panic!("invalid expression while evaluating"),
168        Expression::Uncompiled(_) => panic!("uncompiled expression while evaluating"),
169        Expression::StringLiteral(s) => Value::String(s.as_str().into()),
170        Expression::NumberLiteral(n, unit) => Value::Number(unit.normalize(*n)),
171        Expression::BoolLiteral(b) => Value::Bool(*b),
172        Expression::ElementReference(_) => todo!(
173            "Element references are only supported in the context of built-in function calls at the moment"
174        ),
175        Expression::PropertyReference(nr) => load_property_helper(
176            &ComponentInstance::InstanceRef(local_context.component_instance),
177            &nr.element(),
178            nr.name(),
179        )
180        .unwrap(),
181        Expression::RepeaterIndexReference { element } => load_property_helper(
182            &ComponentInstance::InstanceRef(local_context.component_instance),
183            &element.upgrade().unwrap().borrow().base_type.as_component().root_element,
184            crate::dynamic_item_tree::SPECIAL_PROPERTY_INDEX,
185        )
186        .unwrap(),
187        Expression::RepeaterModelReference { element } => {
188            let value = load_property_helper(
189                &ComponentInstance::InstanceRef(local_context.component_instance),
190                &element.upgrade().unwrap().borrow().base_type.as_component().root_element,
191                crate::dynamic_item_tree::SPECIAL_PROPERTY_MODEL_DATA,
192            )
193            .unwrap();
194            if matches!(value, Value::Void) {
195                // Uninitialized model data (because the model returned None) should still be initialized to the default value of the type
196                default_value_for_type(&expression.ty())
197            } else {
198                value
199            }
200        }
201        Expression::FunctionParameterReference { index, .. } => {
202            local_context.function_arguments[*index].clone()
203        }
204        Expression::StructFieldAccess { base, name } => {
205            if let Value::Struct(o) = eval_expression(base, local_context) {
206                o.get_field(name).cloned().unwrap_or(Value::Void)
207            } else {
208                Value::Void
209            }
210        }
211        Expression::ArrayIndex { array, index } => {
212            let array = eval_expression(array, local_context);
213            let index = eval_expression(index, local_context);
214            match (array, index) {
215                (Value::Model(model), Value::Number(index)) => model
216                    .row_data_tracked(index as isize as usize)
217                    .unwrap_or_else(|| default_value_for_type(&expression.ty())),
218                _ => Value::Void,
219            }
220        }
221        Expression::Cast { from, to } => {
222            let v = eval_expression(from, local_context);
223            match (v, to) {
224                (Value::Number(n), Type::Int32) => Value::Number(n.trunc()),
225                (Value::Number(n), Type::String) => {
226                    Value::String(i_slint_core::string::shared_string_from_number(n))
227                }
228                (Value::Number(n), Type::Color) => Color::from_argb_encoded(n as u32).into(),
229                (Value::Brush(brush), Type::Color) => brush.color().into(),
230                (Value::EnumerationValue(_, val), Type::String) => Value::String(val.into()),
231                (v, _) => v,
232            }
233        }
234        Expression::CodeBlock(sub) => {
235            let mut v = Value::Void;
236            for e in sub {
237                v = eval_expression(e, local_context);
238                if let Some(r) = &local_context.return_value {
239                    return r.clone();
240                }
241            }
242            v
243        }
244        Expression::FunctionCall { function, arguments, source_location } => match &function {
245            Callable::Function(nr) => {
246                let is_item_member = nr
247                    .element()
248                    .borrow()
249                    .native_class()
250                    .is_some_and(|n| n.properties.contains_key(nr.name()));
251                if is_item_member {
252                    call_item_member_function(nr, local_context)
253                } else {
254                    let args = arguments
255                        .iter()
256                        .map(|e| eval_expression(e, local_context))
257                        .collect::<Vec<_>>();
258                    call_function(
259                        &ComponentInstance::InstanceRef(local_context.component_instance),
260                        &nr.element(),
261                        nr.name(),
262                        args,
263                    )
264                    .unwrap()
265                }
266            }
267            Callable::Callback(nr) => {
268                let args =
269                    arguments.iter().map(|e| eval_expression(e, local_context)).collect::<Vec<_>>();
270                invoke_callback(
271                    &ComponentInstance::InstanceRef(local_context.component_instance),
272                    &nr.element(),
273                    nr.name(),
274                    &args,
275                )
276                .unwrap()
277            }
278            Callable::Builtin(f) => {
279                call_builtin_function(f.clone(), arguments, local_context, source_location)
280            }
281        },
282        Expression::SelfAssignment { lhs, rhs, op, .. } => {
283            let rhs = eval_expression(rhs, local_context);
284            eval_assignment(lhs, *op, rhs, local_context);
285            Value::Void
286        }
287        Expression::BinaryExpression { lhs, rhs, op } => {
288            let lhs = eval_expression(lhs, local_context);
289            let rhs = eval_expression(rhs, local_context);
290
291            match (op, lhs, rhs) {
292                ('+', Value::String(mut a), Value::String(b)) => {
293                    a.push_str(b.as_str());
294                    Value::String(a)
295                }
296                ('+', Value::Number(a), Value::Number(b)) => Value::Number(a + b),
297                ('+', a @ Value::Struct(_), b @ Value::Struct(_)) => {
298                    let a: Option<corelib::layout::LayoutInfo> = a.try_into().ok();
299                    let b: Option<corelib::layout::LayoutInfo> = b.try_into().ok();
300                    if let (Some(a), Some(b)) = (a, b) {
301                        a.merge(&b).into()
302                    } else {
303                        panic!("unsupported {a:?} {op} {b:?}");
304                    }
305                }
306                ('-', Value::Number(a), Value::Number(b)) => Value::Number(a - b),
307                ('/', Value::Number(a), Value::Number(b)) => Value::Number(a / b),
308                ('*', Value::Number(a), Value::Number(b)) => Value::Number(a * b),
309                ('<', Value::Number(a), Value::Number(b)) => Value::Bool(a < b),
310                ('>', Value::Number(a), Value::Number(b)) => Value::Bool(a > b),
311                ('≤', Value::Number(a), Value::Number(b)) => Value::Bool(a <= b),
312                ('≥', Value::Number(a), Value::Number(b)) => Value::Bool(a >= b),
313                ('<', Value::String(a), Value::String(b)) => Value::Bool(a < b),
314                ('>', Value::String(a), Value::String(b)) => Value::Bool(a > b),
315                ('≤', Value::String(a), Value::String(b)) => Value::Bool(a <= b),
316                ('≥', Value::String(a), Value::String(b)) => Value::Bool(a >= b),
317                ('=', a, b) => Value::Bool(a == b),
318                ('!', a, b) => Value::Bool(a != b),
319                ('&', Value::Bool(a), Value::Bool(b)) => Value::Bool(a && b),
320                ('|', Value::Bool(a), Value::Bool(b)) => Value::Bool(a || b),
321                (op, lhs, rhs) => panic!("unsupported {lhs:?} {op} {rhs:?}"),
322            }
323        }
324        Expression::UnaryOp { sub, op } => {
325            let sub = eval_expression(sub, local_context);
326            match (sub, op) {
327                (Value::Number(a), '+') => Value::Number(a),
328                (Value::Number(a), '-') => Value::Number(-a),
329                (Value::Bool(a), '!') => Value::Bool(!a),
330                (sub, op) => panic!("unsupported {op} {sub:?}"),
331            }
332        }
333        Expression::ImageReference { resource_ref, nine_slice, .. } => {
334            let mut image = match resource_ref {
335                i_slint_compiler::expression_tree::ImageReference::None => Ok(Default::default()),
336                i_slint_compiler::expression_tree::ImageReference::AbsolutePath(path) => {
337                    let path = std::path::Path::new(path);
338                    if path.starts_with("builtin:/") {
339                        i_slint_compiler::fileaccess::load_file(path)
340                            .and_then(|virtual_file| virtual_file.builtin_contents)
341                            .map(|virtual_file| {
342                                let extension = path.extension().unwrap().to_str().unwrap();
343                                corelib::graphics::load_image_from_embedded_data(
344                                    corelib::slice::Slice::from_slice(virtual_file),
345                                    corelib::slice::Slice::from_slice(extension.as_bytes()),
346                                )
347                            })
348                            .ok_or_else(Default::default)
349                    } else {
350                        corelib::graphics::Image::load_from_path(path)
351                    }
352                }
353                i_slint_compiler::expression_tree::ImageReference::EmbeddedData { .. } => {
354                    todo!()
355                }
356                i_slint_compiler::expression_tree::ImageReference::EmbeddedTexture { .. } => {
357                    todo!()
358                }
359            }
360            .unwrap_or_else(|_| {
361                eprintln!("Could not load image {resource_ref:?}");
362                Default::default()
363            });
364            if let Some(n) = nine_slice {
365                image.set_nine_slice_edges(n[0], n[1], n[2], n[3]);
366            }
367            Value::Image(image)
368        }
369        Expression::Condition { condition, true_expr, false_expr } => {
370            match eval_expression(condition, local_context).try_into() as Result<bool, _> {
371                Ok(true) => eval_expression(true_expr, local_context),
372                Ok(false) => eval_expression(false_expr, local_context),
373                _ => local_context
374                    .return_value
375                    .clone()
376                    .expect("conditional expression did not evaluate to boolean"),
377            }
378        }
379        Expression::Array { values, .. } => {
380            Value::Model(ModelRc::new(corelib::model::SharedVectorModel::from(
381                values
382                    .iter()
383                    .map(|e| eval_expression(e, local_context))
384                    .collect::<SharedVector<_>>(),
385            )))
386        }
387        Expression::Struct { values, .. } => Value::Struct(
388            values
389                .iter()
390                .map(|(k, v)| (k.to_string(), eval_expression(v, local_context)))
391                .collect(),
392        ),
393        Expression::PathData(data) => Value::PathData(convert_path(data, local_context)),
394        Expression::StoreLocalVariable { name, value } => {
395            let value = eval_expression(value, local_context);
396            local_context.local_variables.insert(name.clone(), value);
397            Value::Void
398        }
399        Expression::ReadLocalVariable { name, .. } => {
400            local_context.local_variables.get(name).unwrap().clone()
401        }
402        Expression::EasingCurve(curve) => Value::EasingCurve(match curve {
403            EasingCurve::Linear => corelib::animations::EasingCurve::Linear,
404            EasingCurve::EaseInElastic => corelib::animations::EasingCurve::EaseInElastic,
405            EasingCurve::EaseOutElastic => corelib::animations::EasingCurve::EaseOutElastic,
406            EasingCurve::EaseInOutElastic => corelib::animations::EasingCurve::EaseInOutElastic,
407            EasingCurve::EaseInBounce => corelib::animations::EasingCurve::EaseInBounce,
408            EasingCurve::EaseOutBounce => corelib::animations::EasingCurve::EaseOutBounce,
409            EasingCurve::EaseInOutBounce => corelib::animations::EasingCurve::EaseInOutBounce,
410            EasingCurve::CubicBezier(a, b, c, d) => {
411                corelib::animations::EasingCurve::CubicBezier([*a, *b, *c, *d])
412            }
413        }),
414        Expression::LinearGradient { angle, stops } => {
415            let angle = eval_expression(angle, local_context);
416            Value::Brush(Brush::LinearGradient(LinearGradientBrush::new(
417                angle.try_into().unwrap(),
418                stops.iter().map(|(color, stop)| {
419                    let color = eval_expression(color, local_context).try_into().unwrap();
420                    let position = eval_expression(stop, local_context).try_into().unwrap();
421                    GradientStop { color, position }
422                }),
423            )))
424        }
425        Expression::RadialGradient { stops } => Value::Brush(Brush::RadialGradient(
426            RadialGradientBrush::new_circle(stops.iter().map(|(color, stop)| {
427                let color = eval_expression(color, local_context).try_into().unwrap();
428                let position = eval_expression(stop, local_context).try_into().unwrap();
429                GradientStop { color, position }
430            })),
431        )),
432        Expression::ConicGradient { from_angle, stops } => {
433            let from_angle: f32 = eval_expression(from_angle, local_context).try_into().unwrap();
434            Value::Brush(Brush::ConicGradient(ConicGradientBrush::new(
435                from_angle,
436                stops.iter().map(|(color, stop)| {
437                    let color = eval_expression(color, local_context).try_into().unwrap();
438                    let position = eval_expression(stop, local_context).try_into().unwrap();
439                    GradientStop { color, position }
440                }),
441            )))
442        }
443        Expression::EnumerationValue(value) => {
444            Value::EnumerationValue(value.enumeration.name.to_string(), value.to_string())
445        }
446        Expression::ReturnStatement(x) => {
447            let val = x.as_ref().map_or(Value::Void, |x| eval_expression(x, local_context));
448            if local_context.return_value.is_none() {
449                local_context.return_value = Some(val);
450            }
451            local_context.return_value.clone().unwrap()
452        }
453        Expression::LayoutCacheAccess {
454            layout_cache_prop,
455            index,
456            repeater_index,
457            entries_per_item,
458        } => {
459            let cache = load_property_helper(
460                &ComponentInstance::InstanceRef(local_context.component_instance),
461                &layout_cache_prop.element(),
462                layout_cache_prop.name(),
463            )
464            .unwrap();
465            if let Value::LayoutCache(cache) = cache {
466                if let Some(ri) = repeater_index {
467                    let offset: usize = eval_expression(ri, local_context).try_into().unwrap();
468                    Value::Number(
469                        cache
470                            .get((cache[*index] as usize) + offset * entries_per_item)
471                            .copied()
472                            .unwrap_or(0.)
473                            .into(),
474                    )
475                } else {
476                    Value::Number(cache[*index].into())
477                }
478            } else if let Value::ArrayOfU16(cache) = cache {
479                if let Some(ri) = repeater_index {
480                    let offset: usize = eval_expression(ri, local_context).try_into().unwrap();
481                    Value::Number(
482                        cache
483                            .get((cache[*index] as usize) + offset * entries_per_item)
484                            .copied()
485                            .unwrap_or(0)
486                            .into(),
487                    )
488                } else {
489                    Value::Number(cache[*index].into())
490                }
491            } else {
492                panic!("invalid layout cache")
493            }
494        }
495        Expression::ComputeLayoutInfo(lay, o) => {
496            crate::eval_layout::compute_layout_info(lay, *o, local_context)
497        }
498        Expression::ComputeGridLayoutInfo { layout_organized_data_prop, layout, orientation } => {
499            let cache = load_property_helper(
500                &ComponentInstance::InstanceRef(local_context.component_instance),
501                &layout_organized_data_prop.element(),
502                layout_organized_data_prop.name(),
503            )
504            .unwrap();
505            if let Value::ArrayOfU16(organized_data) = cache {
506                crate::eval_layout::compute_grid_layout_info(
507                    layout,
508                    &organized_data,
509                    *orientation,
510                    local_context,
511                )
512            } else {
513                panic!("invalid layout organized data cache")
514            }
515        }
516        Expression::OrganizeGridLayout(lay) => {
517            crate::eval_layout::organize_grid_layout(lay, local_context)
518        }
519        Expression::SolveLayout(lay, o) => crate::eval_layout::solve_layout(lay, *o, local_context),
520        Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } => {
521            let cache = load_property_helper(
522                &ComponentInstance::InstanceRef(local_context.component_instance),
523                &layout_organized_data_prop.element(),
524                layout_organized_data_prop.name(),
525            )
526            .unwrap();
527            if let Value::ArrayOfU16(organized_data) = cache {
528                crate::eval_layout::solve_grid_layout(
529                    &organized_data,
530                    layout,
531                    *orientation,
532                    local_context,
533                )
534            } else {
535                panic!("invalid layout organized data cache")
536            }
537        }
538        Expression::MinMax { ty: _, op, lhs, rhs } => {
539            let Value::Number(lhs) = eval_expression(lhs, local_context) else {
540                return local_context
541                    .return_value
542                    .clone()
543                    .expect("minmax lhs expression did not evaluate to number");
544            };
545            let Value::Number(rhs) = eval_expression(rhs, local_context) else {
546                return local_context
547                    .return_value
548                    .clone()
549                    .expect("minmax rhs expression did not evaluate to number");
550            };
551            match op {
552                MinMaxOp::Min => Value::Number(lhs.min(rhs)),
553                MinMaxOp::Max => Value::Number(lhs.max(rhs)),
554            }
555        }
556        Expression::EmptyComponentFactory => Value::ComponentFactory(Default::default()),
557        Expression::DebugHook { expression, .. } => eval_expression(expression, local_context),
558    }
559}
560
561fn call_builtin_function(
562    f: BuiltinFunction,
563    arguments: &[Expression],
564    local_context: &mut EvalLocalContext,
565    source_location: &Option<i_slint_compiler::diagnostics::SourceLocation>,
566) -> Value {
567    match f {
568        BuiltinFunction::GetWindowScaleFactor => Value::Number(
569            local_context.component_instance.access_window(|window| window.scale_factor()) as _,
570        ),
571        BuiltinFunction::GetWindowDefaultFontSize => Value::Number({
572            let component = local_context.component_instance;
573            let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
574            WindowItem::resolved_default_font_size(vtable::VRc::into_dyn(item_comp)).get() as _
575        }),
576        BuiltinFunction::AnimationTick => {
577            Value::Number(i_slint_core::animations::animation_tick() as f64)
578        }
579        BuiltinFunction::Debug => {
580            let to_print: SharedString =
581                eval_expression(&arguments[0], local_context).try_into().unwrap();
582            local_context.component_instance.description.debug_handler.borrow()(
583                source_location.as_ref(),
584                &to_print,
585            );
586            Value::Void
587        }
588        BuiltinFunction::Mod => {
589            let mut to_num = |e| -> f64 { eval_expression(e, local_context).try_into().unwrap() };
590            Value::Number(to_num(&arguments[0]).rem_euclid(to_num(&arguments[1])))
591        }
592        BuiltinFunction::Round => {
593            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
594            Value::Number(x.round())
595        }
596        BuiltinFunction::Ceil => {
597            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
598            Value::Number(x.ceil())
599        }
600        BuiltinFunction::Floor => {
601            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
602            Value::Number(x.floor())
603        }
604        BuiltinFunction::Sqrt => {
605            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
606            Value::Number(x.sqrt())
607        }
608        BuiltinFunction::Abs => {
609            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
610            Value::Number(x.abs())
611        }
612        BuiltinFunction::Sin => {
613            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
614            Value::Number(x.to_radians().sin())
615        }
616        BuiltinFunction::Cos => {
617            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
618            Value::Number(x.to_radians().cos())
619        }
620        BuiltinFunction::Tan => {
621            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
622            Value::Number(x.to_radians().tan())
623        }
624        BuiltinFunction::ASin => {
625            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
626            Value::Number(x.asin().to_degrees())
627        }
628        BuiltinFunction::ACos => {
629            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
630            Value::Number(x.acos().to_degrees())
631        }
632        BuiltinFunction::ATan => {
633            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
634            Value::Number(x.atan().to_degrees())
635        }
636        BuiltinFunction::ATan2 => {
637            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
638            let y: f64 = eval_expression(&arguments[1], local_context).try_into().unwrap();
639            Value::Number(x.atan2(y).to_degrees())
640        }
641        BuiltinFunction::Log => {
642            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
643            let y: f64 = eval_expression(&arguments[1], local_context).try_into().unwrap();
644            Value::Number(x.log(y))
645        }
646        BuiltinFunction::Ln => {
647            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
648            Value::Number(x.ln())
649        }
650        BuiltinFunction::Pow => {
651            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
652            let y: f64 = eval_expression(&arguments[1], local_context).try_into().unwrap();
653            Value::Number(x.powf(y))
654        }
655        BuiltinFunction::Exp => {
656            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
657            Value::Number(x.exp())
658        }
659        BuiltinFunction::ToFixed => {
660            let n: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
661            let digits: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
662            let digits: usize = digits.max(0) as usize;
663            Value::String(i_slint_core::string::shared_string_from_number_fixed(n, digits))
664        }
665        BuiltinFunction::ToPrecision => {
666            let n: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
667            let precision: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
668            let precision: usize = precision.max(0) as usize;
669            Value::String(i_slint_core::string::shared_string_from_number_precision(n, precision))
670        }
671        BuiltinFunction::SetFocusItem => {
672            if arguments.len() != 1 {
673                panic!("internal error: incorrect argument count to SetFocusItem")
674            }
675            let component = local_context.component_instance;
676            if let Expression::ElementReference(focus_item) = &arguments[0] {
677                generativity::make_guard!(guard);
678
679                let focus_item = focus_item.upgrade().unwrap();
680                let enclosing_component =
681                    enclosing_component_for_element(&focus_item, component, guard);
682                let description = enclosing_component.description;
683
684                let item_info = &description.items[focus_item.borrow().id.as_str()];
685
686                let focus_item_comp =
687                    enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
688
689                component.access_window(|window| {
690                    window.set_focus_item(
691                        &corelib::items::ItemRc::new(
692                            vtable::VRc::into_dyn(focus_item_comp),
693                            item_info.item_index(),
694                        ),
695                        true,
696                        FocusReason::Programmatic,
697                    )
698                });
699                Value::Void
700            } else {
701                panic!("internal error: argument to SetFocusItem must be an element")
702            }
703        }
704        BuiltinFunction::ClearFocusItem => {
705            if arguments.len() != 1 {
706                panic!("internal error: incorrect argument count to SetFocusItem")
707            }
708            let component = local_context.component_instance;
709            if let Expression::ElementReference(focus_item) = &arguments[0] {
710                generativity::make_guard!(guard);
711
712                let focus_item = focus_item.upgrade().unwrap();
713                let enclosing_component =
714                    enclosing_component_for_element(&focus_item, component, guard);
715                let description = enclosing_component.description;
716
717                let item_info = &description.items[focus_item.borrow().id.as_str()];
718
719                let focus_item_comp =
720                    enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
721
722                component.access_window(|window| {
723                    window.set_focus_item(
724                        &corelib::items::ItemRc::new(
725                            vtable::VRc::into_dyn(focus_item_comp),
726                            item_info.item_index(),
727                        ),
728                        false,
729                        FocusReason::Programmatic,
730                    )
731                });
732                Value::Void
733            } else {
734                panic!("internal error: argument to ClearFocusItem must be an element")
735            }
736        }
737        BuiltinFunction::ShowPopupWindow => {
738            if arguments.len() != 1 {
739                panic!("internal error: incorrect argument count to ShowPopupWindow")
740            }
741            let component = local_context.component_instance;
742            if let Expression::ElementReference(popup_window) = &arguments[0] {
743                let popup_window = popup_window.upgrade().unwrap();
744                let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
745                let parent_component = pop_comp
746                    .parent_element
747                    .upgrade()
748                    .unwrap()
749                    .borrow()
750                    .enclosing_component
751                    .upgrade()
752                    .unwrap();
753                let popup_list = parent_component.popup_windows.borrow();
754                let popup =
755                    popup_list.iter().find(|p| Rc::ptr_eq(&p.component, &pop_comp)).unwrap();
756
757                generativity::make_guard!(guard);
758                let enclosing_component =
759                    enclosing_component_for_element(&popup.parent_element, component, guard);
760                let parent_item_info = &enclosing_component.description.items
761                    [popup.parent_element.borrow().id.as_str()];
762                let parent_item_comp =
763                    enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
764                let parent_item = corelib::items::ItemRc::new(
765                    vtable::VRc::into_dyn(parent_item_comp),
766                    parent_item_info.item_index(),
767                );
768
769                let close_policy = Value::EnumerationValue(
770                    popup.close_policy.enumeration.name.to_string(),
771                    popup.close_policy.to_string(),
772                )
773                .try_into()
774                .expect("Invalid internal enumeration representation for close policy");
775
776                crate::dynamic_item_tree::show_popup(
777                    popup_window,
778                    enclosing_component,
779                    popup,
780                    |instance_ref| {
781                        let comp = ComponentInstance::InstanceRef(instance_ref);
782                        let x = load_property_helper(&comp, &popup.x.element(), popup.x.name())
783                            .unwrap();
784                        let y = load_property_helper(&comp, &popup.y.element(), popup.y.name())
785                            .unwrap();
786                        corelib::api::LogicalPosition::new(
787                            x.try_into().unwrap(),
788                            y.try_into().unwrap(),
789                        )
790                    },
791                    close_policy,
792                    enclosing_component.self_weak().get().unwrap().clone(),
793                    component.window_adapter(),
794                    &parent_item,
795                );
796                Value::Void
797            } else {
798                panic!("internal error: argument to ShowPopupWindow must be an element")
799            }
800        }
801        BuiltinFunction::ClosePopupWindow => {
802            let component = local_context.component_instance;
803            if let Expression::ElementReference(popup_window) = &arguments[0] {
804                let popup_window = popup_window.upgrade().unwrap();
805                let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
806                let parent_component = pop_comp
807                    .parent_element
808                    .upgrade()
809                    .unwrap()
810                    .borrow()
811                    .enclosing_component
812                    .upgrade()
813                    .unwrap();
814                let popup_list = parent_component.popup_windows.borrow();
815                let popup =
816                    popup_list.iter().find(|p| Rc::ptr_eq(&p.component, &pop_comp)).unwrap();
817
818                generativity::make_guard!(guard);
819                let enclosing_component =
820                    enclosing_component_for_element(&popup.parent_element, component, guard);
821                crate::dynamic_item_tree::close_popup(
822                    popup_window,
823                    enclosing_component,
824                    enclosing_component.window_adapter(),
825                );
826
827                Value::Void
828            } else {
829                panic!("internal error: argument to ClosePopupWindow must be an element")
830            }
831        }
832        BuiltinFunction::ShowPopupMenu | BuiltinFunction::ShowPopupMenuInternal => {
833            let [Expression::ElementReference(element), entries, position] = arguments else {
834                panic!("internal error: incorrect argument count to ShowPopupMenu")
835            };
836            let position = eval_expression(position, local_context)
837                .try_into()
838                .expect("internal error: popup menu position argument should be a point");
839
840            let component = local_context.component_instance;
841            let elem = element.upgrade().unwrap();
842            generativity::make_guard!(guard);
843            let enclosing_component = enclosing_component_for_element(&elem, component, guard);
844            let description = enclosing_component.description;
845            let item_info = &description.items[elem.borrow().id.as_str()];
846            let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
847            let item_tree = vtable::VRc::into_dyn(item_comp);
848            let item_rc = corelib::items::ItemRc::new(item_tree.clone(), item_info.item_index());
849
850            generativity::make_guard!(guard);
851            let compiled = enclosing_component.description.popup_menu_description.unerase(guard);
852            let extra_data = enclosing_component
853                .description
854                .extra_data_offset
855                .apply(enclosing_component.as_ref());
856            let inst = crate::dynamic_item_tree::instantiate(
857                compiled.clone(),
858                Some(enclosing_component.self_weak().get().unwrap().clone()),
859                None,
860                Some(&crate::dynamic_item_tree::WindowOptions::UseExistingWindow(
861                    component.window_adapter(),
862                )),
863                extra_data.globals.get().unwrap().clone(),
864            );
865
866            generativity::make_guard!(guard);
867            let inst_ref = inst.unerase(guard);
868            if let Expression::ElementReference(e) = entries {
869                let menu_item_tree =
870                    e.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
871                let menu_item_tree = crate::dynamic_item_tree::make_menu_item_tree(
872                    &menu_item_tree,
873                    &enclosing_component,
874                    None,
875                );
876
877                if component.access_window(|window| {
878                    window.show_native_popup_menu(
879                        vtable::VRc::into_dyn(menu_item_tree.clone()),
880                        position,
881                        &item_rc,
882                    )
883                }) {
884                    return Value::Void;
885                }
886
887                let (entries, sub_menu, activated) = menu_item_tree_properties(menu_item_tree);
888
889                compiled.set_binding(inst_ref.borrow(), "entries", entries).unwrap();
890                compiled.set_callback_handler(inst_ref.borrow(), "sub-menu", sub_menu).unwrap();
891                compiled.set_callback_handler(inst_ref.borrow(), "activated", activated).unwrap();
892            } else {
893                let entries = eval_expression(entries, local_context);
894                compiled.set_property(inst_ref.borrow(), "entries", entries).unwrap();
895                let item_weak = item_rc.downgrade();
896                compiled
897                    .set_callback_handler(
898                        inst_ref.borrow(),
899                        "sub-menu",
900                        Box::new(move |args: &[Value]| -> Value {
901                            item_weak
902                                .upgrade()
903                                .unwrap()
904                                .downcast::<corelib::items::ContextMenu>()
905                                .unwrap()
906                                .sub_menu
907                                .call(&(args[0].clone().try_into().unwrap(),))
908                                .into()
909                        }),
910                    )
911                    .unwrap();
912                let item_weak = item_rc.downgrade();
913                compiled
914                    .set_callback_handler(
915                        inst_ref.borrow(),
916                        "activated",
917                        Box::new(move |args: &[Value]| -> Value {
918                            item_weak
919                                .upgrade()
920                                .unwrap()
921                                .downcast::<corelib::items::ContextMenu>()
922                                .unwrap()
923                                .activated
924                                .call(&(args[0].clone().try_into().unwrap(),));
925                            Value::Void
926                        }),
927                    )
928                    .unwrap();
929            }
930            let item_weak = item_rc.downgrade();
931            compiled
932                .set_callback_handler(
933                    inst_ref.borrow(),
934                    "close",
935                    Box::new(move |_args: &[Value]| -> Value {
936                        let Some(item_rc) = item_weak.upgrade() else { return Value::Void };
937                        if let Some(id) = item_rc
938                            .downcast::<corelib::items::ContextMenu>()
939                            .unwrap()
940                            .popup_id
941                            .take()
942                        {
943                            WindowInner::from_pub(item_rc.window_adapter().unwrap().window())
944                                .close_popup(id);
945                        }
946                        Value::Void
947                    }),
948                )
949                .unwrap();
950            component.access_window(|window| {
951                let context_menu_elem = item_rc.downcast::<corelib::items::ContextMenu>().unwrap();
952                if let Some(old_id) = context_menu_elem.popup_id.take() {
953                    window.close_popup(old_id)
954                }
955                let id = window.show_popup(
956                    &vtable::VRc::into_dyn(inst.clone()),
957                    position,
958                    corelib::items::PopupClosePolicy::CloseOnClickOutside,
959                    &item_rc,
960                    true,
961                );
962                context_menu_elem.popup_id.set(Some(id));
963            });
964            inst.run_setup_code();
965            Value::Void
966        }
967        BuiltinFunction::SetSelectionOffsets => {
968            if arguments.len() != 3 {
969                panic!("internal error: incorrect argument count to select range function call")
970            }
971            let component = local_context.component_instance;
972            if let Expression::ElementReference(element) = &arguments[0] {
973                generativity::make_guard!(guard);
974
975                let elem = element.upgrade().unwrap();
976                let enclosing_component = enclosing_component_for_element(&elem, component, guard);
977                let description = enclosing_component.description;
978                let item_info = &description.items[elem.borrow().id.as_str()];
979                let item_ref =
980                    unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
981
982                let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
983                let item_rc = corelib::items::ItemRc::new(
984                    vtable::VRc::into_dyn(item_comp),
985                    item_info.item_index(),
986                );
987
988                let window_adapter = component.window_adapter();
989
990                // TODO: Make this generic through RTTI
991                if let Some(textinput) =
992                    ItemRef::downcast_pin::<corelib::items::TextInput>(item_ref)
993                {
994                    let start: i32 =
995                        eval_expression(&arguments[1], local_context).try_into().expect(
996                            "internal error: second argument to set-selection-offsets must be an integer",
997                        );
998                    let end: i32 = eval_expression(&arguments[2], local_context).try_into().expect(
999                        "internal error: third argument to set-selection-offsets must be an integer",
1000                    );
1001
1002                    textinput.set_selection_offsets(&window_adapter, &item_rc, start, end);
1003                } else {
1004                    panic!(
1005                        "internal error: member function called on element that doesn't have it: {}",
1006                        elem.borrow().original_name()
1007                    )
1008                }
1009
1010                Value::Void
1011            } else {
1012                panic!("internal error: first argument to set-selection-offsets must be an element")
1013            }
1014        }
1015        BuiltinFunction::ItemFontMetrics => {
1016            if arguments.len() != 1 {
1017                panic!(
1018                    "internal error: incorrect argument count to item font metrics function call"
1019                )
1020            }
1021            let component = local_context.component_instance;
1022            if let Expression::ElementReference(element) = &arguments[0] {
1023                generativity::make_guard!(guard);
1024
1025                let elem = element.upgrade().unwrap();
1026                let enclosing_component = enclosing_component_for_element(&elem, component, guard);
1027                let description = enclosing_component.description;
1028                let item_info = &description.items[elem.borrow().id.as_str()];
1029                let item_ref =
1030                    unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1031                let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1032                let item_rc = corelib::items::ItemRc::new(
1033                    vtable::VRc::into_dyn(item_comp),
1034                    item_info.item_index(),
1035                );
1036                let window_adapter = component.window_adapter();
1037                let metrics = i_slint_core::items::slint_text_item_fontmetrics(
1038                    &window_adapter,
1039                    item_ref,
1040                    &item_rc,
1041                );
1042                metrics.into()
1043            } else {
1044                panic!("internal error: argument to item-font-metrics must be an element")
1045            }
1046        }
1047        BuiltinFunction::StringIsFloat => {
1048            if arguments.len() != 1 {
1049                panic!("internal error: incorrect argument count to StringIsFloat")
1050            }
1051            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1052                Value::Bool(<f64 as core::str::FromStr>::from_str(s.as_str()).is_ok())
1053            } else {
1054                panic!("Argument not a string");
1055            }
1056        }
1057        BuiltinFunction::StringToFloat => {
1058            if arguments.len() != 1 {
1059                panic!("internal error: incorrect argument count to StringToFloat")
1060            }
1061            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1062                Value::Number(core::str::FromStr::from_str(s.as_str()).unwrap_or(0.))
1063            } else {
1064                panic!("Argument not a string");
1065            }
1066        }
1067        BuiltinFunction::StringIsEmpty => {
1068            if arguments.len() != 1 {
1069                panic!("internal error: incorrect argument count to StringIsEmpty")
1070            }
1071            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1072                Value::Bool(s.is_empty())
1073            } else {
1074                panic!("Argument not a string");
1075            }
1076        }
1077        BuiltinFunction::StringCharacterCount => {
1078            if arguments.len() != 1 {
1079                panic!("internal error: incorrect argument count to StringCharacterCount")
1080            }
1081            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1082                Value::Number(
1083                    unicode_segmentation::UnicodeSegmentation::graphemes(s.as_str(), true).count()
1084                        as f64,
1085                )
1086            } else {
1087                panic!("Argument not a string");
1088            }
1089        }
1090        BuiltinFunction::StringToLowercase => {
1091            if arguments.len() != 1 {
1092                panic!("internal error: incorrect argument count to StringToLowercase")
1093            }
1094            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1095                Value::String(s.to_lowercase().into())
1096            } else {
1097                panic!("Argument not a string");
1098            }
1099        }
1100        BuiltinFunction::StringToUppercase => {
1101            if arguments.len() != 1 {
1102                panic!("internal error: incorrect argument count to StringToUppercase")
1103            }
1104            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1105                Value::String(s.to_uppercase().into())
1106            } else {
1107                panic!("Argument not a string");
1108            }
1109        }
1110        BuiltinFunction::ColorRgbaStruct => {
1111            if arguments.len() != 1 {
1112                panic!("internal error: incorrect argument count to ColorRGBAComponents")
1113            }
1114            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1115                let color = brush.color();
1116                let values = IntoIterator::into_iter([
1117                    ("red".to_string(), Value::Number(color.red().into())),
1118                    ("green".to_string(), Value::Number(color.green().into())),
1119                    ("blue".to_string(), Value::Number(color.blue().into())),
1120                    ("alpha".to_string(), Value::Number(color.alpha().into())),
1121                ])
1122                .collect();
1123                Value::Struct(values)
1124            } else {
1125                panic!("First argument not a color");
1126            }
1127        }
1128        BuiltinFunction::ColorHsvaStruct => {
1129            if arguments.len() != 1 {
1130                panic!("internal error: incorrect argument count to ColorHSVAComponents")
1131            }
1132            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1133                let color = brush.color().to_hsva();
1134                let values = IntoIterator::into_iter([
1135                    ("hue".to_string(), Value::Number(color.hue.into())),
1136                    ("saturation".to_string(), Value::Number(color.saturation.into())),
1137                    ("value".to_string(), Value::Number(color.value.into())),
1138                    ("alpha".to_string(), Value::Number(color.alpha.into())),
1139                ])
1140                .collect();
1141                Value::Struct(values)
1142            } else {
1143                panic!("First argument not a color");
1144            }
1145        }
1146        BuiltinFunction::ColorOklchStruct => {
1147            if arguments.len() != 1 {
1148                panic!("internal error: incorrect argument count to ColorOklchStruct")
1149            }
1150            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1151                let color = brush.color().to_oklch();
1152                let values = IntoIterator::into_iter([
1153                    ("lightness".to_string(), Value::Number(color.lightness.into())),
1154                    ("chroma".to_string(), Value::Number(color.chroma.into())),
1155                    ("hue".to_string(), Value::Number(color.hue.into())),
1156                    ("alpha".to_string(), Value::Number(color.alpha.into())),
1157                ])
1158                .collect();
1159                Value::Struct(values)
1160            } else {
1161                panic!("First argument not a color");
1162            }
1163        }
1164        BuiltinFunction::ColorBrighter => {
1165            if arguments.len() != 2 {
1166                panic!("internal error: incorrect argument count to ColorBrighter")
1167            }
1168            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1169                if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1170                    brush.brighter(factor as _).into()
1171                } else {
1172                    panic!("Second argument not a number");
1173                }
1174            } else {
1175                panic!("First argument not a color");
1176            }
1177        }
1178        BuiltinFunction::ColorDarker => {
1179            if arguments.len() != 2 {
1180                panic!("internal error: incorrect argument count to ColorDarker")
1181            }
1182            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1183                if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1184                    brush.darker(factor as _).into()
1185                } else {
1186                    panic!("Second argument not a number");
1187                }
1188            } else {
1189                panic!("First argument not a color");
1190            }
1191        }
1192        BuiltinFunction::ColorTransparentize => {
1193            if arguments.len() != 2 {
1194                panic!("internal error: incorrect argument count to ColorFaded")
1195            }
1196            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1197                if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1198                    brush.transparentize(factor as _).into()
1199                } else {
1200                    panic!("Second argument not a number");
1201                }
1202            } else {
1203                panic!("First argument not a color");
1204            }
1205        }
1206        BuiltinFunction::ColorMix => {
1207            if arguments.len() != 3 {
1208                panic!("internal error: incorrect argument count to ColorMix")
1209            }
1210
1211            let arg0 = eval_expression(&arguments[0], local_context);
1212            let arg1 = eval_expression(&arguments[1], local_context);
1213            let arg2 = eval_expression(&arguments[2], local_context);
1214
1215            if !matches!(arg0, Value::Brush(Brush::SolidColor(_))) {
1216                panic!("First argument not a color");
1217            }
1218            if !matches!(arg1, Value::Brush(Brush::SolidColor(_))) {
1219                panic!("Second argument not a color");
1220            }
1221            if !matches!(arg2, Value::Number(_)) {
1222                panic!("Third argument not a number");
1223            }
1224
1225            let (
1226                Value::Brush(Brush::SolidColor(color_a)),
1227                Value::Brush(Brush::SolidColor(color_b)),
1228                Value::Number(factor),
1229            ) = (arg0, arg1, arg2)
1230            else {
1231                unreachable!()
1232            };
1233
1234            color_a.mix(&color_b, factor as _).into()
1235        }
1236        BuiltinFunction::ColorWithAlpha => {
1237            if arguments.len() != 2 {
1238                panic!("internal error: incorrect argument count to ColorWithAlpha")
1239            }
1240            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1241                if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1242                    brush.with_alpha(factor as _).into()
1243                } else {
1244                    panic!("Second argument not a number");
1245                }
1246            } else {
1247                panic!("First argument not a color");
1248            }
1249        }
1250        BuiltinFunction::ImageSize => {
1251            if arguments.len() != 1 {
1252                panic!("internal error: incorrect argument count to ImageSize")
1253            }
1254            if let Value::Image(img) = eval_expression(&arguments[0], local_context) {
1255                let size = img.size();
1256                let values = IntoIterator::into_iter([
1257                    ("width".to_string(), Value::Number(size.width as f64)),
1258                    ("height".to_string(), Value::Number(size.height as f64)),
1259                ])
1260                .collect();
1261                Value::Struct(values)
1262            } else {
1263                panic!("First argument not an image");
1264            }
1265        }
1266        BuiltinFunction::ArrayLength => {
1267            if arguments.len() != 1 {
1268                panic!("internal error: incorrect argument count to ArrayLength")
1269            }
1270            match eval_expression(&arguments[0], local_context) {
1271                Value::Model(model) => {
1272                    model.model_tracker().track_row_count_changes();
1273                    Value::Number(model.row_count() as f64)
1274                }
1275                _ => {
1276                    panic!("First argument not an array");
1277                }
1278            }
1279        }
1280        BuiltinFunction::Rgb => {
1281            let r: i32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1282            let g: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1283            let b: i32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1284            let a: f32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1285            let r: u8 = r.clamp(0, 255) as u8;
1286            let g: u8 = g.clamp(0, 255) as u8;
1287            let b: u8 = b.clamp(0, 255) as u8;
1288            let a: u8 = (255. * a).clamp(0., 255.) as u8;
1289            Value::Brush(Brush::SolidColor(Color::from_argb_u8(a, r, g, b)))
1290        }
1291        BuiltinFunction::Hsv => {
1292            let h: f32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1293            let s: f32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1294            let v: f32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1295            let a: f32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1296            let a = (1. * a).clamp(0., 1.);
1297            Value::Brush(Brush::SolidColor(Color::from_hsva(h, s, v, a)))
1298        }
1299        BuiltinFunction::Oklch => {
1300            let l: f32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1301            let c: f32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1302            let h: f32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1303            let a: f32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1304            let l = l.clamp(0., 1.);
1305            let c = c.max(0.);
1306            let a = a.clamp(0., 1.);
1307            Value::Brush(Brush::SolidColor(Color::from_oklch(l, c, h, a)))
1308        }
1309        BuiltinFunction::ColorScheme => local_context
1310            .component_instance
1311            .window_adapter()
1312            .internal(corelib::InternalToken)
1313            .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1314            .into(),
1315        BuiltinFunction::SupportsNativeMenuBar => local_context
1316            .component_instance
1317            .window_adapter()
1318            .internal(corelib::InternalToken)
1319            .is_some_and(|x| x.supports_native_menu_bar())
1320            .into(),
1321        BuiltinFunction::SetupMenuBar => {
1322            let component = local_context.component_instance;
1323            let [
1324                Expression::PropertyReference(entries_nr),
1325                Expression::PropertyReference(sub_menu_nr),
1326                Expression::PropertyReference(activated_nr),
1327                Expression::ElementReference(item_tree_root),
1328                Expression::BoolLiteral(no_native),
1329                rest @ ..,
1330            ] = arguments
1331            else {
1332                panic!("internal error: incorrect argument count to SetupMenuBar")
1333            };
1334
1335            let menu_item_tree =
1336                item_tree_root.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
1337            let menu_item_tree = crate::dynamic_item_tree::make_menu_item_tree(
1338                &menu_item_tree,
1339                &component,
1340                rest.first(),
1341            );
1342
1343            if let Some(w) = component.window_adapter().internal(i_slint_core::InternalToken)
1344                && !no_native
1345                && w.supports_native_menu_bar()
1346            {
1347                let menubar = vtable::VRc::into_dyn(menu_item_tree);
1348                w.setup_menubar(menubar);
1349                return Value::Void;
1350            }
1351
1352            let (entries, sub_menu, activated) = menu_item_tree_properties(menu_item_tree);
1353
1354            assert_eq!(
1355                entries_nr.element().borrow().id,
1356                component.description.original.root_element.borrow().id,
1357                "entries need to be in the main element"
1358            );
1359            local_context
1360                .component_instance
1361                .description
1362                .set_binding(component.borrow(), entries_nr.name(), entries)
1363                .unwrap();
1364            let i = &ComponentInstance::InstanceRef(local_context.component_instance);
1365            set_callback_handler(i, &sub_menu_nr.element(), sub_menu_nr.name(), sub_menu).unwrap();
1366            set_callback_handler(i, &activated_nr.element(), activated_nr.name(), activated)
1367                .unwrap();
1368
1369            Value::Void
1370        }
1371        BuiltinFunction::MonthDayCount => {
1372            let m: u32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1373            let y: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1374            Value::Number(i_slint_core::date_time::month_day_count(m, y).unwrap_or(0) as f64)
1375        }
1376        BuiltinFunction::MonthOffset => {
1377            let m: u32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1378            let y: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1379
1380            Value::Number(i_slint_core::date_time::month_offset(m, y) as f64)
1381        }
1382        BuiltinFunction::FormatDate => {
1383            let f: SharedString = eval_expression(&arguments[0], local_context).try_into().unwrap();
1384            let d: u32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1385            let m: u32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1386            let y: i32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1387
1388            Value::String(i_slint_core::date_time::format_date(&f, d, m, y))
1389        }
1390        BuiltinFunction::DateNow => Value::Model(ModelRc::new(VecModel::from(
1391            i_slint_core::date_time::date_now()
1392                .into_iter()
1393                .map(|x| Value::Number(x as f64))
1394                .collect::<Vec<_>>(),
1395        ))),
1396        BuiltinFunction::ValidDate => {
1397            let d: SharedString = eval_expression(&arguments[0], local_context).try_into().unwrap();
1398            let f: SharedString = eval_expression(&arguments[1], local_context).try_into().unwrap();
1399            Value::Bool(i_slint_core::date_time::parse_date(d.as_str(), f.as_str()).is_some())
1400        }
1401        BuiltinFunction::ParseDate => {
1402            let d: SharedString = eval_expression(&arguments[0], local_context).try_into().unwrap();
1403            let f: SharedString = eval_expression(&arguments[1], local_context).try_into().unwrap();
1404
1405            Value::Model(ModelRc::new(
1406                i_slint_core::date_time::parse_date(d.as_str(), f.as_str())
1407                    .map(|x| {
1408                        VecModel::from(
1409                            x.into_iter().map(|x| Value::Number(x as f64)).collect::<Vec<_>>(),
1410                        )
1411                    })
1412                    .unwrap_or_default(),
1413            ))
1414        }
1415        BuiltinFunction::TextInputFocused => Value::Bool(
1416            local_context.component_instance.access_window(|window| window.text_input_focused())
1417                as _,
1418        ),
1419        BuiltinFunction::SetTextInputFocused => {
1420            local_context.component_instance.access_window(|window| {
1421                window.set_text_input_focused(
1422                    eval_expression(&arguments[0], local_context).try_into().unwrap(),
1423                )
1424            });
1425            Value::Void
1426        }
1427        BuiltinFunction::ImplicitLayoutInfo(orient) => {
1428            let component = local_context.component_instance;
1429            if let [Expression::ElementReference(item)] = arguments {
1430                generativity::make_guard!(guard);
1431
1432                let item = item.upgrade().unwrap();
1433                let enclosing_component = enclosing_component_for_element(&item, component, guard);
1434                let description = enclosing_component.description;
1435                let item_info = &description.items[item.borrow().id.as_str()];
1436                let item_ref =
1437                    unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1438                let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1439                let window_adapter = component.window_adapter();
1440                item_ref
1441                    .as_ref()
1442                    .layout_info(
1443                        crate::eval_layout::to_runtime(orient),
1444                        &window_adapter,
1445                        &ItemRc::new(vtable::VRc::into_dyn(item_comp), item_info.item_index()),
1446                    )
1447                    .into()
1448            } else {
1449                panic!("internal error: incorrect arguments to ImplicitLayoutInfo {arguments:?}");
1450            }
1451        }
1452        BuiltinFunction::ItemAbsolutePosition => {
1453            if arguments.len() != 1 {
1454                panic!("internal error: incorrect argument count to ItemAbsolutePosition")
1455            }
1456
1457            let component = local_context.component_instance;
1458
1459            if let Expression::ElementReference(item) = &arguments[0] {
1460                generativity::make_guard!(guard);
1461
1462                let item = item.upgrade().unwrap();
1463                let enclosing_component = enclosing_component_for_element(&item, component, guard);
1464                let description = enclosing_component.description;
1465
1466                let item_info = &description.items[item.borrow().id.as_str()];
1467
1468                let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1469
1470                let item_rc = corelib::items::ItemRc::new(
1471                    vtable::VRc::into_dyn(item_comp),
1472                    item_info.item_index(),
1473                );
1474
1475                item_rc.map_to_window(Default::default()).to_untyped().into()
1476            } else {
1477                panic!("internal error: argument to SetFocusItem must be an element")
1478            }
1479        }
1480        BuiltinFunction::RegisterCustomFontByPath => {
1481            if arguments.len() != 1 {
1482                panic!("internal error: incorrect argument count to RegisterCustomFontByPath")
1483            }
1484            let component = local_context.component_instance;
1485            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1486                if let Some(err) = component
1487                    .window_adapter()
1488                    .renderer()
1489                    .register_font_from_path(&std::path::PathBuf::from(s.as_str()))
1490                    .err()
1491                {
1492                    corelib::debug_log!("Error loading custom font {}: {}", s.as_str(), err);
1493                }
1494                Value::Void
1495            } else {
1496                panic!("Argument not a string");
1497            }
1498        }
1499        BuiltinFunction::RegisterCustomFontByMemory | BuiltinFunction::RegisterBitmapFont => {
1500            unimplemented!()
1501        }
1502        BuiltinFunction::Translate => {
1503            let original: SharedString =
1504                eval_expression(&arguments[0], local_context).try_into().unwrap();
1505            let context: SharedString =
1506                eval_expression(&arguments[1], local_context).try_into().unwrap();
1507            let domain: SharedString =
1508                eval_expression(&arguments[2], local_context).try_into().unwrap();
1509            let args = eval_expression(&arguments[3], local_context);
1510            let Value::Model(args) = args else { panic!("Args to translate not a model {args:?}") };
1511            struct StringModelWrapper(ModelRc<Value>);
1512            impl corelib::translations::FormatArgs for StringModelWrapper {
1513                type Output<'a> = SharedString;
1514                fn from_index(&self, index: usize) -> Option<SharedString> {
1515                    self.0.row_data(index).map(|x| x.try_into().unwrap())
1516                }
1517            }
1518            Value::String(corelib::translations::translate(
1519                &original,
1520                &context,
1521                &domain,
1522                &StringModelWrapper(args),
1523                eval_expression(&arguments[4], local_context).try_into().unwrap(),
1524                &SharedString::try_from(eval_expression(&arguments[5], local_context)).unwrap(),
1525            ))
1526        }
1527        BuiltinFunction::Use24HourFormat => Value::Bool(corelib::date_time::use_24_hour_format()),
1528        BuiltinFunction::UpdateTimers => {
1529            crate::dynamic_item_tree::update_timers(local_context.component_instance);
1530            Value::Void
1531        }
1532        BuiltinFunction::DetectOperatingSystem => i_slint_core::detect_operating_system().into(),
1533        // start and stop are unreachable because they are lowered to simple assignment of running
1534        BuiltinFunction::StartTimer => unreachable!(),
1535        BuiltinFunction::StopTimer => unreachable!(),
1536        BuiltinFunction::RestartTimer => {
1537            if let [Expression::ElementReference(timer_element)] = arguments {
1538                crate::dynamic_item_tree::restart_timer(
1539                    timer_element.clone(),
1540                    local_context.component_instance,
1541                );
1542
1543                Value::Void
1544            } else {
1545                panic!("internal error: argument to RestartTimer must be an element")
1546            }
1547        }
1548        BuiltinFunction::EscapeMarkdown => {
1549            let text: SharedString =
1550                eval_expression(&arguments[0], local_context).try_into().unwrap();
1551            Value::String(corelib::styled_text::escape_markdown(&text).into())
1552        }
1553        BuiltinFunction::ParseMarkdown => {
1554            let text: SharedString =
1555                eval_expression(&arguments[0], local_context).try_into().unwrap();
1556            Value::StyledText(corelib::styled_text::parse_markdown(&text))
1557        }
1558    }
1559}
1560
1561fn call_item_member_function(nr: &NamedReference, local_context: &mut EvalLocalContext) -> Value {
1562    let component = local_context.component_instance;
1563    let elem = nr.element();
1564    let name = nr.name().as_str();
1565    generativity::make_guard!(guard);
1566    let enclosing_component = enclosing_component_for_element(&elem, component, guard);
1567    let description = enclosing_component.description;
1568    let item_info = &description.items[elem.borrow().id.as_str()];
1569    let item_ref = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1570
1571    let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1572    let item_rc =
1573        corelib::items::ItemRc::new(vtable::VRc::into_dyn(item_comp), item_info.item_index());
1574
1575    let window_adapter = component.window_adapter();
1576
1577    // TODO: Make this generic through RTTI
1578    if let Some(textinput) = ItemRef::downcast_pin::<corelib::items::TextInput>(item_ref) {
1579        match name {
1580            "select-all" => textinput.select_all(&window_adapter, &item_rc),
1581            "clear-selection" => textinput.clear_selection(&window_adapter, &item_rc),
1582            "cut" => textinput.cut(&window_adapter, &item_rc),
1583            "copy" => textinput.copy(&window_adapter, &item_rc),
1584            "paste" => textinput.paste(&window_adapter, &item_rc),
1585            _ => panic!("internal: Unknown member function {name} called on TextInput"),
1586        }
1587    } else if let Some(s) = ItemRef::downcast_pin::<corelib::items::SwipeGestureHandler>(item_ref) {
1588        match name {
1589            "cancel" => s.cancel(&window_adapter, &item_rc),
1590            _ => panic!("internal: Unknown member function {name} called on SwipeGestureHandler"),
1591        }
1592    } else if let Some(s) = ItemRef::downcast_pin::<corelib::items::ContextMenu>(item_ref) {
1593        match name {
1594            "close" => s.close(&window_adapter, &item_rc),
1595            "is-open" => return Value::Bool(s.is_open(&window_adapter, &item_rc)),
1596            _ => {
1597                panic!("internal: Unknown member function {name} called on ContextMenu")
1598            }
1599        }
1600    } else if let Some(s) = ItemRef::downcast_pin::<corelib::items::WindowItem>(item_ref) {
1601        match name {
1602            "hide" => s.hide(&window_adapter),
1603            _ => {
1604                panic!("internal: Unknown member function {name} called on WindowItem")
1605            }
1606        }
1607    } else {
1608        panic!(
1609            "internal error: member function {name} called on element that doesn't have it: {}",
1610            elem.borrow().original_name()
1611        )
1612    }
1613
1614    Value::Void
1615}
1616
1617fn eval_assignment(lhs: &Expression, op: char, rhs: Value, local_context: &mut EvalLocalContext) {
1618    let eval = |lhs| match (lhs, &rhs, op) {
1619        (Value::String(ref mut a), Value::String(b), '+') => {
1620            a.push_str(b.as_str());
1621            Value::String(a.clone())
1622        }
1623        (Value::Number(a), Value::Number(b), '+') => Value::Number(a + b),
1624        (Value::Number(a), Value::Number(b), '-') => Value::Number(a - b),
1625        (Value::Number(a), Value::Number(b), '/') => Value::Number(a / b),
1626        (Value::Number(a), Value::Number(b), '*') => Value::Number(a * b),
1627        (lhs, rhs, op) => panic!("unsupported {lhs:?} {op} {rhs:?}"),
1628    };
1629    match lhs {
1630        Expression::PropertyReference(nr) => {
1631            let element = nr.element();
1632            generativity::make_guard!(guard);
1633            let enclosing_component = enclosing_component_instance_for_element(
1634                &element,
1635                &ComponentInstance::InstanceRef(local_context.component_instance),
1636                guard,
1637            );
1638
1639            match enclosing_component {
1640                ComponentInstance::InstanceRef(enclosing_component) => {
1641                    if op == '=' {
1642                        store_property(enclosing_component, &element, nr.name(), rhs).unwrap();
1643                        return;
1644                    }
1645
1646                    let component = element.borrow().enclosing_component.upgrade().unwrap();
1647                    if element.borrow().id == component.root_element.borrow().id
1648                        && let Some(x) =
1649                            enclosing_component.description.custom_properties.get(nr.name())
1650                    {
1651                        unsafe {
1652                            let p =
1653                                Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset));
1654                            x.prop.set(p, eval(x.prop.get(p).unwrap()), None).unwrap();
1655                        }
1656                        return;
1657                    }
1658                    let item_info =
1659                        &enclosing_component.description.items[element.borrow().id.as_str()];
1660                    let item =
1661                        unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1662                    let p = &item_info.rtti.properties[nr.name().as_str()];
1663                    p.set(item, eval(p.get(item)), None).unwrap();
1664                }
1665                ComponentInstance::GlobalComponent(global) => {
1666                    let val = if op == '=' {
1667                        rhs
1668                    } else {
1669                        eval(global.as_ref().get_property(nr.name()).unwrap())
1670                    };
1671                    global.as_ref().set_property(nr.name(), val).unwrap();
1672                }
1673            }
1674        }
1675        Expression::StructFieldAccess { base, name } => {
1676            if let Value::Struct(mut o) = eval_expression(base, local_context) {
1677                let mut r = o.get_field(name).unwrap().clone();
1678                r = if op == '=' { rhs } else { eval(std::mem::take(&mut r)) };
1679                o.set_field(name.to_string(), r);
1680                eval_assignment(base, '=', Value::Struct(o), local_context)
1681            }
1682        }
1683        Expression::RepeaterModelReference { element } => {
1684            let element = element.upgrade().unwrap();
1685            let component_instance = local_context.component_instance;
1686            generativity::make_guard!(g1);
1687            let enclosing_component =
1688                enclosing_component_for_element(&element, component_instance, g1);
1689            // we need a 'static Repeater component in order to call model_set_row_data, so get it.
1690            // Safety: This is the only 'static Id in scope.
1691            let static_guard =
1692                unsafe { generativity::Guard::new(generativity::Id::<'static>::new()) };
1693            let repeater = crate::dynamic_item_tree::get_repeater_by_name(
1694                enclosing_component,
1695                element.borrow().id.as_str(),
1696                static_guard,
1697            );
1698            repeater.0.model_set_row_data(
1699                eval_expression(
1700                    &Expression::RepeaterIndexReference { element: Rc::downgrade(&element) },
1701                    local_context,
1702                )
1703                .try_into()
1704                .unwrap(),
1705                if op == '=' {
1706                    rhs
1707                } else {
1708                    eval(eval_expression(
1709                        &Expression::RepeaterModelReference { element: Rc::downgrade(&element) },
1710                        local_context,
1711                    ))
1712                },
1713            )
1714        }
1715        Expression::ArrayIndex { array, index } => {
1716            let array = eval_expression(array, local_context);
1717            let index = eval_expression(index, local_context);
1718            match (array, index) {
1719                (Value::Model(model), Value::Number(index)) => {
1720                    if index >= 0. && (index as usize) < model.row_count() {
1721                        let index = index as usize;
1722                        if op == '=' {
1723                            model.set_row_data(index, rhs);
1724                        } else {
1725                            model.set_row_data(
1726                                index,
1727                                eval(
1728                                    model
1729                                        .row_data(index)
1730                                        .unwrap_or_else(|| default_value_for_type(&lhs.ty())),
1731                                ),
1732                            );
1733                        }
1734                    }
1735                }
1736                _ => {
1737                    eprintln!("Attempting to write into an array that cannot be written");
1738                }
1739            }
1740        }
1741        _ => panic!("typechecking should make sure this was a PropertyReference"),
1742    }
1743}
1744
1745pub fn load_property(component: InstanceRef, element: &ElementRc, name: &str) -> Result<Value, ()> {
1746    load_property_helper(&ComponentInstance::InstanceRef(component), element, name)
1747}
1748
1749fn load_property_helper(
1750    component_instance: &ComponentInstance,
1751    element: &ElementRc,
1752    name: &str,
1753) -> Result<Value, ()> {
1754    generativity::make_guard!(guard);
1755    match enclosing_component_instance_for_element(element, component_instance, guard) {
1756        ComponentInstance::InstanceRef(enclosing_component) => {
1757            let element = element.borrow();
1758            if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1759            {
1760                if let Some(x) = enclosing_component.description.custom_properties.get(name) {
1761                    return unsafe {
1762                        x.prop.get(Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset)))
1763                    };
1764                } else if enclosing_component.description.original.is_global() {
1765                    return Err(());
1766                }
1767            };
1768            let item_info = enclosing_component
1769                .description
1770                .items
1771                .get(element.id.as_str())
1772                .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, name));
1773            core::mem::drop(element);
1774            let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1775            Ok(item_info.rtti.properties.get(name).ok_or(())?.get(item))
1776        }
1777        ComponentInstance::GlobalComponent(glob) => glob.as_ref().get_property(name),
1778    }
1779}
1780
1781pub fn store_property(
1782    component_instance: InstanceRef,
1783    element: &ElementRc,
1784    name: &str,
1785    mut value: Value,
1786) -> Result<(), SetPropertyError> {
1787    generativity::make_guard!(guard);
1788    match enclosing_component_instance_for_element(
1789        element,
1790        &ComponentInstance::InstanceRef(component_instance),
1791        guard,
1792    ) {
1793        ComponentInstance::InstanceRef(enclosing_component) => {
1794            let maybe_animation = match element.borrow().bindings.get(name) {
1795                Some(b) => crate::dynamic_item_tree::animation_for_property(
1796                    enclosing_component,
1797                    &b.borrow().animation,
1798                ),
1799                None => {
1800                    crate::dynamic_item_tree::animation_for_property(enclosing_component, &None)
1801                }
1802            };
1803
1804            let component = element.borrow().enclosing_component.upgrade().unwrap();
1805            if element.borrow().id == component.root_element.borrow().id {
1806                if let Some(x) = enclosing_component.description.custom_properties.get(name) {
1807                    if let Some(orig_decl) = enclosing_component
1808                        .description
1809                        .original
1810                        .root_element
1811                        .borrow()
1812                        .property_declarations
1813                        .get(name)
1814                    {
1815                        // Do an extra type checking because PropertyInfo::set won't do it for custom structures or array
1816                        if !check_value_type(&mut value, &orig_decl.property_type) {
1817                            return Err(SetPropertyError::WrongType);
1818                        }
1819                    }
1820                    unsafe {
1821                        let p = Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset));
1822                        return x
1823                            .prop
1824                            .set(p, value, maybe_animation.as_animation())
1825                            .map_err(|()| SetPropertyError::WrongType);
1826                    }
1827                } else if enclosing_component.description.original.is_global() {
1828                    return Err(SetPropertyError::NoSuchProperty);
1829                }
1830            };
1831            let item_info = &enclosing_component.description.items[element.borrow().id.as_str()];
1832            let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1833            let p = &item_info.rtti.properties.get(name).ok_or(SetPropertyError::NoSuchProperty)?;
1834            p.set(item, value, maybe_animation.as_animation())
1835                .map_err(|()| SetPropertyError::WrongType)?;
1836        }
1837        ComponentInstance::GlobalComponent(glob) => {
1838            glob.as_ref().set_property(name, value)?;
1839        }
1840    }
1841    Ok(())
1842}
1843
1844/// Return true if the Value can be used for a property of the given type
1845fn check_value_type(value: &mut Value, ty: &Type) -> bool {
1846    match ty {
1847        Type::Void => true,
1848        Type::Invalid
1849        | Type::InferredProperty
1850        | Type::InferredCallback
1851        | Type::Callback { .. }
1852        | Type::Function { .. }
1853        | Type::ElementReference => panic!("not valid property type"),
1854        Type::Float32 => matches!(value, Value::Number(_)),
1855        Type::Int32 => matches!(value, Value::Number(_)),
1856        Type::String => matches!(value, Value::String(_)),
1857        Type::Color => matches!(value, Value::Brush(_)),
1858        Type::UnitProduct(_)
1859        | Type::Duration
1860        | Type::PhysicalLength
1861        | Type::LogicalLength
1862        | Type::Rem
1863        | Type::Angle
1864        | Type::Percent => matches!(value, Value::Number(_)),
1865        Type::Image => matches!(value, Value::Image(_)),
1866        Type::Bool => matches!(value, Value::Bool(_)),
1867        Type::Model => {
1868            matches!(value, Value::Model(_) | Value::Bool(_) | Value::Number(_))
1869        }
1870        Type::PathData => matches!(value, Value::PathData(_)),
1871        Type::Easing => matches!(value, Value::EasingCurve(_)),
1872        Type::Brush => matches!(value, Value::Brush(_)),
1873        Type::Array(inner) => {
1874            matches!(value, Value::Model(m) if m.iter().all(|mut v| check_value_type(&mut v, inner)))
1875        }
1876        Type::Struct(s) => {
1877            let Value::Struct(str) = value else { return false };
1878            if !str
1879                .0
1880                .iter_mut()
1881                .all(|(k, v)| s.fields.get(k).is_some_and(|ty| check_value_type(v, ty)))
1882            {
1883                return false;
1884            }
1885            for (k, v) in &s.fields {
1886                str.0.entry(k.clone()).or_insert_with(|| default_value_for_type(v));
1887            }
1888            true
1889        }
1890        Type::Enumeration(en) => {
1891            matches!(value, Value::EnumerationValue(name, _) if name == en.name.as_str())
1892        }
1893        Type::LayoutCache => matches!(value, Value::LayoutCache(_)),
1894        Type::ArrayOfU16 => matches!(value, Value::ArrayOfU16(_)),
1895        Type::ComponentFactory => matches!(value, Value::ComponentFactory(_)),
1896        Type::StyledText => matches!(value, Value::StyledText(_)),
1897    }
1898}
1899
1900pub(crate) fn invoke_callback(
1901    component_instance: &ComponentInstance,
1902    element: &ElementRc,
1903    callback_name: &SmolStr,
1904    args: &[Value],
1905) -> Option<Value> {
1906    generativity::make_guard!(guard);
1907    match enclosing_component_instance_for_element(element, component_instance, guard) {
1908        ComponentInstance::InstanceRef(enclosing_component) => {
1909            let description = enclosing_component.description;
1910            let element = element.borrow();
1911            if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1912            {
1913                if let Some(callback_offset) = description.custom_callbacks.get(callback_name) {
1914                    let callback = callback_offset.apply(&*enclosing_component.instance);
1915                    let res = callback.call(args);
1916                    return Some(if res != Value::Void {
1917                        res
1918                    } else if let Some(Type::Callback(callback)) = description
1919                        .original
1920                        .root_element
1921                        .borrow()
1922                        .property_declarations
1923                        .get(callback_name)
1924                        .map(|d| &d.property_type)
1925                    {
1926                        // If the callback was not set, the return value will be Value::Void, but we need
1927                        // to make sure that the value is actually of the right type as returned by the
1928                        // callback, otherwise we will get panics later
1929                        default_value_for_type(&callback.return_type)
1930                    } else {
1931                        res
1932                    });
1933                } else if enclosing_component.description.original.is_global() {
1934                    return None;
1935                }
1936            };
1937            let item_info = &description.items[element.id.as_str()];
1938            let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1939            item_info
1940                .rtti
1941                .callbacks
1942                .get(callback_name.as_str())
1943                .map(|callback| callback.call(item, args))
1944        }
1945        ComponentInstance::GlobalComponent(global) => {
1946            Some(global.as_ref().invoke_callback(callback_name, args).unwrap())
1947        }
1948    }
1949}
1950
1951pub(crate) fn set_callback_handler(
1952    component_instance: &ComponentInstance,
1953    element: &ElementRc,
1954    callback_name: &str,
1955    handler: CallbackHandler,
1956) -> Result<(), ()> {
1957    generativity::make_guard!(guard);
1958    match enclosing_component_instance_for_element(element, component_instance, guard) {
1959        ComponentInstance::InstanceRef(enclosing_component) => {
1960            let description = enclosing_component.description;
1961            let element = element.borrow();
1962            if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1963            {
1964                if let Some(callback_offset) = description.custom_callbacks.get(callback_name) {
1965                    let callback = callback_offset.apply(&*enclosing_component.instance);
1966                    callback.set_handler(handler);
1967                    return Ok(());
1968                } else if enclosing_component.description.original.is_global() {
1969                    return Err(());
1970                }
1971            };
1972            let item_info = &description.items[element.id.as_str()];
1973            let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1974            if let Some(callback) = item_info.rtti.callbacks.get(callback_name) {
1975                callback.set_handler(item, handler);
1976                Ok(())
1977            } else {
1978                Err(())
1979            }
1980        }
1981        ComponentInstance::GlobalComponent(global) => {
1982            global.as_ref().set_callback_handler(callback_name, handler)
1983        }
1984    }
1985}
1986
1987/// Invoke the function.
1988///
1989/// Return None if the function don't exist
1990pub(crate) fn call_function(
1991    component_instance: &ComponentInstance,
1992    element: &ElementRc,
1993    function_name: &str,
1994    args: Vec<Value>,
1995) -> Option<Value> {
1996    generativity::make_guard!(guard);
1997    match enclosing_component_instance_for_element(element, component_instance, guard) {
1998        ComponentInstance::InstanceRef(c) => {
1999            let mut ctx = EvalLocalContext::from_function_arguments(c, args);
2000            eval_expression(
2001                &element.borrow().bindings.get(function_name)?.borrow().expression,
2002                &mut ctx,
2003            )
2004            .into()
2005        }
2006        ComponentInstance::GlobalComponent(g) => g.as_ref().eval_function(function_name, args).ok(),
2007    }
2008}
2009
2010/// Return the component instance which hold the given element.
2011/// Does not take in account the global component.
2012pub fn enclosing_component_for_element<'a, 'old_id, 'new_id>(
2013    element: &'a ElementRc,
2014    component: InstanceRef<'a, 'old_id>,
2015    _guard: generativity::Guard<'new_id>,
2016) -> InstanceRef<'a, 'new_id> {
2017    let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
2018    if Rc::ptr_eq(enclosing, &component.description.original) {
2019        // Safety: new_id is an unique id
2020        unsafe {
2021            std::mem::transmute::<InstanceRef<'a, 'old_id>, InstanceRef<'a, 'new_id>>(component)
2022        }
2023    } else {
2024        assert!(!enclosing.is_global());
2025        // Safety: this is the only place we use this 'static lifetime in this function and nothing is returned with it
2026        // For some reason we can't make a new guard here because the compiler thinks we are returning that
2027        // (it assumes that the 'id must outlive 'a , which is not true)
2028        let static_guard = unsafe { generativity::Guard::new(generativity::Id::<'static>::new()) };
2029
2030        let parent_instance = component
2031            .parent_instance(static_guard)
2032            .expect("accessing deleted parent (issue #6426)");
2033        enclosing_component_for_element(element, parent_instance, _guard)
2034    }
2035}
2036
2037/// Return the component instance which hold the given element.
2038/// The difference with enclosing_component_for_element is that it takes the GlobalComponent into account.
2039pub(crate) fn enclosing_component_instance_for_element<'a, 'new_id>(
2040    element: &'a ElementRc,
2041    component_instance: &ComponentInstance<'a, '_>,
2042    guard: generativity::Guard<'new_id>,
2043) -> ComponentInstance<'a, 'new_id> {
2044    let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
2045    match component_instance {
2046        ComponentInstance::InstanceRef(component) => {
2047            if enclosing.is_global() && !Rc::ptr_eq(enclosing, &component.description.original) {
2048                ComponentInstance::GlobalComponent(
2049                    component
2050                        .description
2051                        .extra_data_offset
2052                        .apply(component.instance.get_ref())
2053                        .globals
2054                        .get()
2055                        .unwrap()
2056                        .get(enclosing.root_element.borrow().id.as_str())
2057                        .unwrap(),
2058                )
2059            } else {
2060                ComponentInstance::InstanceRef(enclosing_component_for_element(
2061                    element, *component, guard,
2062                ))
2063            }
2064        }
2065        ComponentInstance::GlobalComponent(global) => {
2066            //assert!(Rc::ptr_eq(enclosing, &global.component));
2067            ComponentInstance::GlobalComponent(global.clone())
2068        }
2069    }
2070}
2071
2072pub fn new_struct_with_bindings<ElementType: 'static + Default + corelib::rtti::BuiltinItem>(
2073    bindings: &i_slint_compiler::object_tree::BindingsMap,
2074    local_context: &mut EvalLocalContext,
2075) -> ElementType {
2076    let mut element = ElementType::default();
2077    for (prop, info) in ElementType::fields::<Value>().into_iter() {
2078        if let Some(binding) = &bindings.get(prop) {
2079            let value = eval_expression(&binding.borrow(), local_context);
2080            info.set_field(&mut element, value).unwrap();
2081        }
2082    }
2083    element
2084}
2085
2086fn convert_from_lyon_path<'a>(
2087    events_it: impl IntoIterator<Item = &'a i_slint_compiler::expression_tree::Expression>,
2088    points_it: impl IntoIterator<Item = &'a i_slint_compiler::expression_tree::Expression>,
2089    local_context: &mut EvalLocalContext,
2090) -> PathData {
2091    let events = events_it
2092        .into_iter()
2093        .map(|event_expr| eval_expression(event_expr, local_context).try_into().unwrap())
2094        .collect::<SharedVector<_>>();
2095
2096    let points = points_it
2097        .into_iter()
2098        .map(|point_expr| {
2099            let point_value = eval_expression(point_expr, local_context);
2100            let point_struct: Struct = point_value.try_into().unwrap();
2101            let mut point = i_slint_core::graphics::Point::default();
2102            let x: f64 = point_struct.get_field("x").unwrap().clone().try_into().unwrap();
2103            let y: f64 = point_struct.get_field("y").unwrap().clone().try_into().unwrap();
2104            point.x = x as _;
2105            point.y = y as _;
2106            point
2107        })
2108        .collect::<SharedVector<_>>();
2109
2110    PathData::Events(events, points)
2111}
2112
2113pub fn convert_path(path: &ExprPath, local_context: &mut EvalLocalContext) -> PathData {
2114    match path {
2115        ExprPath::Elements(elements) => PathData::Elements(
2116            elements
2117                .iter()
2118                .map(|element| convert_path_element(element, local_context))
2119                .collect::<SharedVector<PathElement>>(),
2120        ),
2121        ExprPath::Events(events, points) => {
2122            convert_from_lyon_path(events.iter(), points.iter(), local_context)
2123        }
2124        ExprPath::Commands(commands) => {
2125            if let Value::String(commands) = eval_expression(commands, local_context) {
2126                PathData::Commands(commands)
2127            } else {
2128                panic!("binding to path commands does not evaluate to string");
2129            }
2130        }
2131    }
2132}
2133
2134fn convert_path_element(
2135    expr_element: &ExprPathElement,
2136    local_context: &mut EvalLocalContext,
2137) -> PathElement {
2138    match expr_element.element_type.native_class.class_name.as_str() {
2139        "MoveTo" => {
2140            PathElement::MoveTo(new_struct_with_bindings(&expr_element.bindings, local_context))
2141        }
2142        "LineTo" => {
2143            PathElement::LineTo(new_struct_with_bindings(&expr_element.bindings, local_context))
2144        }
2145        "ArcTo" => {
2146            PathElement::ArcTo(new_struct_with_bindings(&expr_element.bindings, local_context))
2147        }
2148        "CubicTo" => {
2149            PathElement::CubicTo(new_struct_with_bindings(&expr_element.bindings, local_context))
2150        }
2151        "QuadraticTo" => PathElement::QuadraticTo(new_struct_with_bindings(
2152            &expr_element.bindings,
2153            local_context,
2154        )),
2155        "Close" => PathElement::Close,
2156        _ => panic!(
2157            "Cannot create unsupported path element {}",
2158            expr_element.element_type.native_class.class_name
2159        ),
2160    }
2161}
2162
2163/// Create a value suitable as the default value of a given type
2164pub fn default_value_for_type(ty: &Type) -> Value {
2165    match ty {
2166        Type::Float32 | Type::Int32 => Value::Number(0.),
2167        Type::String => Value::String(Default::default()),
2168        Type::Color | Type::Brush => Value::Brush(Default::default()),
2169        Type::Duration | Type::Angle | Type::PhysicalLength | Type::LogicalLength | Type::Rem => {
2170            Value::Number(0.)
2171        }
2172        Type::Image => Value::Image(Default::default()),
2173        Type::Bool => Value::Bool(false),
2174        Type::Callback { .. } => Value::Void,
2175        Type::Struct(s) => Value::Struct(
2176            s.fields
2177                .iter()
2178                .map(|(n, t)| (n.to_string(), default_value_for_type(t)))
2179                .collect::<Struct>(),
2180        ),
2181        Type::Array(_) | Type::Model => Value::Model(Default::default()),
2182        Type::Percent => Value::Number(0.),
2183        Type::Enumeration(e) => Value::EnumerationValue(
2184            e.name.to_string(),
2185            e.values.get(e.default_value).unwrap().to_string(),
2186        ),
2187        Type::Easing => Value::EasingCurve(Default::default()),
2188        Type::Void | Type::Invalid => Value::Void,
2189        Type::UnitProduct(_) => Value::Number(0.),
2190        Type::PathData => Value::PathData(Default::default()),
2191        Type::LayoutCache => Value::LayoutCache(Default::default()),
2192        Type::ArrayOfU16 => Value::ArrayOfU16(Default::default()),
2193        Type::ComponentFactory => Value::ComponentFactory(Default::default()),
2194        Type::InferredProperty
2195        | Type::InferredCallback
2196        | Type::ElementReference
2197        | Type::Function { .. } => {
2198            panic!("There can't be such property")
2199        }
2200        Type::StyledText => Value::StyledText(Default::default()),
2201    }
2202}
2203
2204fn menu_item_tree_properties(
2205    context_menu_item_tree: vtable::VRc<i_slint_core::menus::MenuVTable, MenuFromItemTree>,
2206) -> (Box<dyn Fn() -> Value>, CallbackHandler, CallbackHandler) {
2207    let context_menu_item_tree_ = context_menu_item_tree.clone();
2208    let entries = Box::new(move || {
2209        let mut entries = SharedVector::default();
2210        context_menu_item_tree_.sub_menu(None, &mut entries);
2211        Value::Model(ModelRc::new(VecModel::from(
2212            entries.into_iter().map(Value::from).collect::<Vec<_>>(),
2213        )))
2214    });
2215    let context_menu_item_tree_ = context_menu_item_tree.clone();
2216    let sub_menu = Box::new(move |args: &[Value]| -> Value {
2217        let mut entries = SharedVector::default();
2218        context_menu_item_tree_.sub_menu(Some(&args[0].clone().try_into().unwrap()), &mut entries);
2219        Value::Model(ModelRc::new(VecModel::from(
2220            entries.into_iter().map(Value::from).collect::<Vec<_>>(),
2221        )))
2222    });
2223    let activated = Box::new(move |args: &[Value]| -> Value {
2224        context_menu_item_tree.activate(&args[0].clone().try_into().unwrap());
2225        Value::Void
2226    });
2227    (entries, sub_menu, activated)
2228}