Skip to main content

slint_interpreter/
eval.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use crate::api::{SetPropertyError, Struct, Value};
5use crate::dynamic_item_tree::{CallbackHandler, InstanceRef};
6use core::cell::RefCell;
7use core::ffi::c_void;
8use core::pin::Pin;
9use corelib::graphics::{
10    ConicGradientBrush, GradientStop, LinearGradientBrush, PathElement, RadialGradientBrush,
11};
12use corelib::input::FocusReason;
13use corelib::items::{ItemRc, ItemRef, PropertyAnimation, WindowItem};
14use corelib::menus::{Menu, MenuFromItemTree};
15use corelib::model::{Model, ModelExt, ModelRc, VecModel};
16use corelib::rtti::AnimatedBindingKind;
17use corelib::window::{WindowInner, WindowKind};
18use corelib::{Brush, Color, PathData, SharedString, SharedVector};
19use i_slint_compiler::diagnostics::Spanned;
20use i_slint_compiler::expression_tree::{
21    BuiltinFunction, Callable, EasingCurve, Expression, MinMaxOp, MouseCursorInner,
22    Path as ExprPath, PathElement as ExprPathElement,
23};
24use i_slint_compiler::langtype::{ConstantExpression, Type};
25use i_slint_compiler::namedreference::NamedReference;
26use i_slint_compiler::object_tree::{Element, ElementRc};
27use i_slint_core::api::ToSharedString;
28use i_slint_core::{self as corelib};
29use smol_str::SmolStr;
30use std::collections::HashMap;
31use std::rc::{Rc, Weak};
32
33pub trait ErasedPropertyInfo {
34    fn get(&self, item: Pin<ItemRef>) -> Value;
35    fn set(
36        &self,
37        item: Pin<ItemRef>,
38        value: Value,
39        animation: Option<PropertyAnimation>,
40    ) -> Result<(), ()>;
41    fn set_binding(
42        &self,
43        item: Pin<ItemRef>,
44        binding: Box<dyn Fn() -> Value>,
45        animation: AnimatedBindingKind,
46    );
47    fn offset(&self) -> usize;
48
49    #[cfg(slint_debug_property)]
50    fn set_debug_name(&self, item: Pin<ItemRef>, name: String);
51
52    /// Safety: Property2 must be a (pinned) pointer to a `Property<T>`
53    /// where T is the same T as the one represented by this property.
54    unsafe fn link_two_ways(&self, item: Pin<ItemRef>, property2: *const c_void);
55
56    fn prepare_for_two_way_binding(&self, item: Pin<ItemRef>) -> Pin<Rc<corelib::Property<Value>>>;
57
58    fn link_two_way_with_map(
59        &self,
60        item: Pin<ItemRef>,
61        property2: Pin<Rc<corelib::Property<Value>>>,
62        map: Option<Rc<dyn corelib::rtti::TwoWayBindingMapping<Value>>>,
63    );
64
65    fn link_two_way_to_model_data(
66        &self,
67        item: Pin<ItemRef>,
68        getter: Box<dyn Fn() -> Option<Value>>,
69        setter: Box<dyn Fn(&Value)>,
70    );
71}
72
73impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedPropertyInfo
74    for &'static dyn corelib::rtti::PropertyInfo<Item, Value>
75{
76    fn get(&self, item: Pin<ItemRef>) -> Value {
77        (*self).get(ItemRef::downcast_pin(item).unwrap()).unwrap()
78    }
79    fn set(
80        &self,
81        item: Pin<ItemRef>,
82        value: Value,
83        animation: Option<PropertyAnimation>,
84    ) -> Result<(), ()> {
85        (*self).set(ItemRef::downcast_pin(item).unwrap(), value, animation)
86    }
87    fn set_binding(
88        &self,
89        item: Pin<ItemRef>,
90        binding: Box<dyn Fn() -> Value>,
91        animation: AnimatedBindingKind,
92    ) {
93        (*self).set_binding(ItemRef::downcast_pin(item).unwrap(), binding, animation).unwrap();
94    }
95    fn offset(&self) -> usize {
96        (*self).offset()
97    }
98    #[cfg(slint_debug_property)]
99    fn set_debug_name(&self, item: Pin<ItemRef>, name: String) {
100        (*self).set_debug_name(ItemRef::downcast_pin(item).unwrap(), name);
101    }
102    unsafe fn link_two_ways(&self, item: Pin<ItemRef>, property2: *const c_void) {
103        // Safety: ErasedPropertyInfo::link_two_ways and PropertyInfo::link_two_ways have the same safety requirement
104        unsafe { (*self).link_two_ways(ItemRef::downcast_pin(item).unwrap(), property2) }
105    }
106
107    fn prepare_for_two_way_binding(&self, item: Pin<ItemRef>) -> Pin<Rc<corelib::Property<Value>>> {
108        (*self).prepare_for_two_way_binding(ItemRef::downcast_pin(item).unwrap())
109    }
110
111    fn link_two_way_with_map(
112        &self,
113        item: Pin<ItemRef>,
114        property2: Pin<Rc<corelib::Property<Value>>>,
115        map: Option<Rc<dyn corelib::rtti::TwoWayBindingMapping<Value>>>,
116    ) {
117        (*self).link_two_way_with_map(ItemRef::downcast_pin(item).unwrap(), property2, map)
118    }
119
120    fn link_two_way_to_model_data(
121        &self,
122        item: Pin<ItemRef>,
123        getter: Box<dyn Fn() -> Option<Value>>,
124        setter: Box<dyn Fn(&Value)>,
125    ) {
126        (*self).link_two_way_to_model_data(ItemRef::downcast_pin(item).unwrap(), getter, setter)
127    }
128}
129
130pub trait ErasedCallbackInfo {
131    fn call(&self, item: Pin<ItemRef>, args: &[Value]) -> Value;
132    fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>);
133}
134
135impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedCallbackInfo
136    for &'static dyn corelib::rtti::CallbackInfo<Item, Value>
137{
138    fn call(&self, item: Pin<ItemRef>, args: &[Value]) -> Value {
139        (*self).call(ItemRef::downcast_pin(item).unwrap(), args).unwrap()
140    }
141
142    fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>) {
143        (*self).set_handler(ItemRef::downcast_pin(item).unwrap(), handler).unwrap()
144    }
145}
146
147impl corelib::rtti::ValueType for Value {}
148
149#[derive(Clone)]
150pub(crate) enum ComponentInstance<'a, 'id> {
151    InstanceRef(InstanceRef<'a, 'id>),
152    GlobalComponent(Pin<Rc<dyn crate::global_component::GlobalComponent>>),
153}
154
155/// The local variable needed for binding evaluation
156pub struct EvalLocalContext<'a, 'id> {
157    local_variables: HashMap<SmolStr, Value>,
158    function_arguments: Vec<Value>,
159    pub(crate) component_instance: InstanceRef<'a, 'id>,
160    /// When Some, a return statement was executed and one must stop evaluating
161    return_value: Option<Value>,
162}
163
164impl<'a, 'id> EvalLocalContext<'a, 'id> {
165    pub fn from_component_instance(component: InstanceRef<'a, 'id>) -> Self {
166        Self {
167            local_variables: Default::default(),
168            function_arguments: Default::default(),
169            component_instance: component,
170            return_value: None,
171        }
172    }
173
174    /// Create a context for a function and passing the arguments
175    pub fn from_function_arguments(
176        component: InstanceRef<'a, 'id>,
177        function_arguments: Vec<Value>,
178    ) -> Self {
179        Self {
180            component_instance: component,
181            function_arguments,
182            local_variables: Default::default(),
183            return_value: None,
184        }
185    }
186}
187
188/// Evaluates `predicate` against each row of `model`, binding `arg_name` to the row value.
189/// Stops as soon as `on_result` returns `Some`; shared by `ArrayAny`/`ArrayAll`/`ArrayFindIndex`,
190/// which differ only in what they do with each row's boolean result.
191fn eval_array_row_predicate<R>(
192    model: &ModelRc<Value>,
193    arg_name: &SmolStr,
194    predicate: &Expression,
195    local_context: &mut EvalLocalContext,
196    mut on_result: impl FnMut(usize, bool) -> Option<R>,
197) -> Option<R> {
198    model.model_tracker().track_row_count_changes();
199    for row in 0..model.row_count() {
200        let x = model.row_data_tracked(row).unwrap_or_default();
201        let previous = local_context.local_variables.insert(arg_name.clone(), x);
202        let result: bool = eval_expression(predicate, local_context).try_into().unwrap();
203        match previous {
204            Some(prev) => {
205                local_context.local_variables.insert(arg_name.clone(), prev);
206            }
207            None => {
208                local_context.local_variables.remove(arg_name);
209            }
210        }
211        if let Some(r) = on_result(row, result) {
212            return Some(r);
213        }
214    }
215    None
216}
217
218/// Evaluate `expression` as a length / number and return the resulting f32.
219/// Caller's responsibility to only pass length-typed expressions.
220fn eval_to_f32(expression: &Expression, local_context: &mut EvalLocalContext) -> f32 {
221    match eval_expression(expression, local_context) {
222        Value::Number(n) => n as f32,
223        other => unreachable!("expected length-typed expression; got {other:?} for {expression:?}"),
224    }
225}
226
227/// Evaluate an expression and return a Value as the result of this expression
228pub fn eval_expression(expression: &Expression, local_context: &mut EvalLocalContext) -> Value {
229    if let Some(r) = &local_context.return_value {
230        return r.clone();
231    }
232    match expression {
233        Expression::Invalid => panic!("invalid expression while evaluating"),
234        Expression::Uncompiled(_) => panic!("uncompiled expression while evaluating"),
235        Expression::StringLiteral(s) => Value::String(s.as_str().into()),
236        Expression::NumberLiteral(n, _unit) => Value::Number(*n),
237        Expression::BoolLiteral(b) => Value::Bool(*b),
238        Expression::ElementReference(_) => todo!(
239            "Element references are only supported in the context of built-in function calls at the moment"
240        ),
241        Expression::PropertyReference(nr) => load_property_helper(
242            &ComponentInstance::InstanceRef(local_context.component_instance),
243            &nr.element(),
244            nr.name(),
245        )
246        .unwrap(),
247        Expression::RepeaterIndexReference { element } => load_property_helper(
248            &ComponentInstance::InstanceRef(local_context.component_instance),
249            &element.upgrade().unwrap().borrow().base_type.as_component().root_element,
250            crate::dynamic_item_tree::SPECIAL_PROPERTY_INDEX,
251        )
252        .unwrap(),
253        Expression::RepeaterModelReference { element } => {
254            let value = load_property_helper(
255                &ComponentInstance::InstanceRef(local_context.component_instance),
256                &element.upgrade().unwrap().borrow().base_type.as_component().root_element,
257                crate::dynamic_item_tree::SPECIAL_PROPERTY_MODEL_DATA,
258            )
259            .unwrap();
260            if matches!(value, Value::Void) {
261                // Uninitialized model data (because the model returned None) should still be initialized to the default value of the type
262                default_value_for_type(&expression.ty())
263            } else {
264                value
265            }
266        }
267        Expression::FunctionParameterReference { index, .. } => {
268            local_context.function_arguments[*index].clone()
269        }
270        Expression::StructFieldAccess { base, name } => {
271            if let Value::Struct(o) = eval_expression(base, local_context) {
272                o.get_field(name).cloned().unwrap_or(Value::Void)
273            } else {
274                Value::Void
275            }
276        }
277        Expression::ArrayIndex { array, index } => {
278            let array = eval_expression(array, local_context);
279            let index = eval_expression(index, local_context);
280            match (array, index) {
281                (Value::Model(model), Value::Number(index)) => model
282                    .row_data_tracked(index as isize as usize)
283                    .unwrap_or_else(|| default_value_for_type(&expression.ty())),
284                _ => Value::Void,
285            }
286        }
287        Expression::Cast { from, to } => cast_value(eval_expression(from, local_context), to),
288        Expression::CodeBlock(sub) => {
289            let mut v = Value::Void;
290            for e in sub {
291                v = eval_expression(e, local_context);
292                if let Some(r) = &local_context.return_value {
293                    return r.clone();
294                }
295            }
296            v
297        }
298        Expression::FunctionCall { function, arguments, source_location } => match &function {
299            Callable::Function(nr) => {
300                let is_item_member = nr
301                    .element()
302                    .borrow()
303                    .native_class()
304                    .is_some_and(|n| n.properties.contains_key(nr.name()));
305                if is_item_member {
306                    call_item_member_function(nr, local_context)
307                } else {
308                    let args = arguments
309                        .iter()
310                        .map(|e| eval_expression(e, local_context))
311                        .collect::<Vec<_>>();
312                    call_function(
313                        &ComponentInstance::InstanceRef(local_context.component_instance),
314                        &nr.element(),
315                        nr.name(),
316                        args,
317                    )
318                    .unwrap()
319                }
320            }
321            Callable::Callback(nr) => {
322                let args =
323                    arguments.iter().map(|e| eval_expression(e, local_context)).collect::<Vec<_>>();
324                invoke_callback(
325                    &ComponentInstance::InstanceRef(local_context.component_instance),
326                    &nr.element(),
327                    nr.name(),
328                    &args,
329                )
330                .unwrap()
331            }
332            Callable::Builtin(f) => {
333                call_builtin_function(f.clone(), arguments, local_context, source_location)
334            }
335        },
336        Expression::SelfAssignment { lhs, rhs, op, .. } => {
337            let rhs = eval_expression(rhs, local_context);
338            eval_assignment(lhs, *op, rhs, local_context);
339            Value::Void
340        }
341        Expression::BinaryExpression { lhs, rhs, op } => {
342            let lhs = eval_expression(lhs, local_context);
343            // && and || short circuit like in the generated code, or else side
344            // effects in the rhs would run in the interpreter only
345            match (op, &lhs) {
346                ('&', Value::Bool(false)) => return Value::Bool(false),
347                ('|', Value::Bool(true)) => return Value::Bool(true),
348                _ => {}
349            }
350            let rhs = eval_expression(rhs, local_context);
351
352            match (op, lhs, rhs) {
353                ('+', Value::String(mut a), Value::String(b)) => {
354                    a.push_str(b.as_str());
355                    Value::String(a)
356                }
357                ('+', Value::Number(a), Value::Number(b)) => Value::Number(a + b),
358                ('+', a @ Value::Struct(_), b @ Value::Struct(_)) => {
359                    let a: Option<corelib::layout::LayoutInfo> = a.try_into().ok();
360                    let b: Option<corelib::layout::LayoutInfo> = b.try_into().ok();
361                    if let (Some(a), Some(b)) = (a, b) {
362                        a.merge(&b).into()
363                    } else {
364                        panic!("unsupported {a:?} {op} {b:?}");
365                    }
366                }
367                ('-', Value::Number(a), Value::Number(b)) => Value::Number(a - b),
368                ('/', Value::Number(a), Value::Number(b)) => Value::Number(a / b),
369                ('*', Value::Number(a), Value::Number(b)) => Value::Number(a * b),
370                ('<', Value::Number(a), Value::Number(b)) => Value::Bool(a < b),
371                ('>', Value::Number(a), Value::Number(b)) => Value::Bool(a > b),
372                ('≤', Value::Number(a), Value::Number(b)) => Value::Bool(a <= b),
373                ('≥', Value::Number(a), Value::Number(b)) => Value::Bool(a >= b),
374                ('<', Value::String(a), Value::String(b)) => Value::Bool(a < b),
375                ('>', Value::String(a), Value::String(b)) => Value::Bool(a > b),
376                ('≤', Value::String(a), Value::String(b)) => Value::Bool(a <= b),
377                ('≥', Value::String(a), Value::String(b)) => Value::Bool(a >= b),
378                ('=', a, b) => Value::Bool(a == b),
379                ('!', a, b) => Value::Bool(a != b),
380                ('&', Value::Bool(a), Value::Bool(b)) => Value::Bool(a && b),
381                ('|', Value::Bool(a), Value::Bool(b)) => Value::Bool(a || b),
382                (op, lhs, rhs) => panic!("unsupported {lhs:?} {op} {rhs:?}"),
383            }
384        }
385        Expression::UnaryOp { sub, op } => {
386            let sub = eval_expression(sub, local_context);
387            eval_unary_op(sub, *op).unwrap_or_else(|sub| panic!("unsupported {op} {sub:?}"))
388        }
389        Expression::ImageReference { resource_ref, nine_slice, .. } => {
390            let mut image = match resource_ref {
391                i_slint_compiler::expression_tree::ImageReference::None => Ok(Default::default()),
392                i_slint_compiler::expression_tree::ImageReference::DataUri(data_uri) => {
393                    i_slint_compiler::data_uri::decode_data_uri(data_uri)
394                        .ok()
395                        .and_then(|(data, extension)| {
396                            corelib::graphics::load_image_from_data_uri(data_uri, &data, &extension)
397                                .ok()
398                        })
399                        .ok_or_else(Default::default)
400                }
401                i_slint_compiler::expression_tree::ImageReference::Url(url)
402                    if url.scheme() == "builtin" =>
403                {
404                    let path = std::path::Path::new(url.as_str());
405                    i_slint_compiler::fileaccess::load_file(path)
406                        .and_then(|virtual_file| virtual_file.builtin_contents)
407                        .map(|virtual_file| {
408                            let extension = path.extension().unwrap().to_str().unwrap();
409                            corelib::graphics::load_image_from_embedded_data(
410                                corelib::slice::Slice::from_slice(virtual_file),
411                                corelib::slice::Slice::from_slice(extension.as_bytes()),
412                            )
413                        })
414                        .ok_or_else(Default::default)
415                }
416                i_slint_compiler::expression_tree::ImageReference::Path(path) => {
417                    corelib::graphics::Image::load_from_path(std::path::Path::new(path))
418                }
419                i_slint_compiler::expression_tree::ImageReference::Url(url) => {
420                    #[cfg(target_arch = "wasm32")]
421                    {
422                        corelib::graphics::load_as_html_image(url.as_str())
423                    }
424                    // URL image references only work on the web, where the browser fetches them.
425                    #[cfg(not(target_arch = "wasm32"))]
426                    {
427                        let _ = url;
428                        Err(Default::default())
429                    }
430                }
431                i_slint_compiler::expression_tree::ImageReference::EmbeddedData { .. } => {
432                    todo!()
433                }
434                i_slint_compiler::expression_tree::ImageReference::EmbeddedTexture { .. } => {
435                    todo!()
436                }
437            }
438            .unwrap_or_else(|_| {
439                eprintln!("Could not load image {resource_ref:?}");
440                Default::default()
441            });
442            if let Some(n) = nine_slice {
443                image.set_nine_slice_edges(n[0], n[1], n[2], n[3]);
444            }
445            Value::Image(image)
446        }
447        Expression::Condition { condition, true_expr, false_expr } => {
448            match eval_expression(condition, local_context).try_into() as Result<bool, _> {
449                Ok(true) => eval_expression(true_expr, local_context),
450                Ok(false) => eval_expression(false_expr, local_context),
451                _ => local_context
452                    .return_value
453                    .clone()
454                    .expect("conditional expression did not evaluate to boolean"),
455            }
456        }
457        Expression::Array { values, .. } => {
458            Value::Model(ModelRc::new(corelib::model::SharedVectorModel::from(
459                values
460                    .iter()
461                    .map(|e| eval_expression(e, local_context))
462                    .collect::<SharedVector<_>>(),
463            )))
464        }
465        Expression::Struct { values, .. } => Value::Struct(
466            values
467                .iter()
468                .map(|(k, v)| (k.to_string(), eval_expression(v, local_context)))
469                .collect(),
470        ),
471        Expression::PathData(data) => Value::PathData(convert_path(data, local_context)),
472        Expression::StoreLocalVariable { name, value } => {
473            let value = eval_expression(value, local_context);
474            local_context.local_variables.insert(name.clone(), value);
475            Value::Void
476        }
477        Expression::ReadLocalVariable { name, .. } => {
478            local_context.local_variables.get(name).unwrap().clone()
479        }
480        Expression::EasingCurve(curve) => Value::EasingCurve(match curve {
481            EasingCurve::Linear => corelib::animations::EasingCurve::Linear,
482            EasingCurve::EaseInElastic => corelib::animations::EasingCurve::EaseInElastic,
483            EasingCurve::EaseOutElastic => corelib::animations::EasingCurve::EaseOutElastic,
484            EasingCurve::EaseInOutElastic => corelib::animations::EasingCurve::EaseInOutElastic,
485            EasingCurve::EaseInBounce => corelib::animations::EasingCurve::EaseInBounce,
486            EasingCurve::EaseOutBounce => corelib::animations::EasingCurve::EaseOutBounce,
487            EasingCurve::EaseInOutBounce => corelib::animations::EasingCurve::EaseInOutBounce,
488            EasingCurve::CubicBezier(a, b, c, d) => {
489                corelib::animations::EasingCurve::CubicBezier([*a, *b, *c, *d])
490            }
491        }),
492        Expression::MouseCursor(cursor) => Value::MouseCursorInner(match cursor {
493            MouseCursorInner::BuiltIn(cursor) => corelib::cursor::MouseCursorInner::BuiltIn(
494                eval_expression(cursor, local_context).try_into().unwrap(),
495            ),
496            MouseCursorInner::CustomMouseCursor { image, hotspot_x, hotspot_y } => {
497                let image = eval_expression(image, local_context).try_into().unwrap();
498                let hotspot_x = eval_expression(hotspot_x, local_context).try_into().unwrap();
499                let hotspot_y = eval_expression(hotspot_y, local_context).try_into().unwrap();
500
501                corelib::cursor::MouseCursorInner::CustomMouseCursor { image, hotspot_x, hotspot_y }
502            }
503        }),
504        Expression::LinearGradient { angle, stops } => {
505            let angle = eval_expression(angle, local_context);
506            Value::Brush(Brush::LinearGradient(LinearGradientBrush::new(
507                angle.try_into().unwrap(),
508                stops.iter().map(|(color, stop)| {
509                    let color = eval_expression(color, local_context).try_into().unwrap();
510                    let position = eval_expression(stop, local_context).try_into().unwrap();
511                    GradientStop { color, position }
512                }),
513            )))
514        }
515        Expression::RadialGradient { stops, center, radius } => {
516            let mut g = RadialGradientBrush::new_circle(stops.iter().map(|(color, stop)| {
517                let color = eval_expression(color, local_context).try_into().unwrap();
518                let position = eval_expression(stop, local_context).try_into().unwrap();
519                GradientStop { color, position }
520            }));
521            if let Some((cx, cy)) = center {
522                let cx: f32 = eval_expression(cx, local_context).try_into().unwrap();
523                let cy: f32 = eval_expression(cy, local_context).try_into().unwrap();
524                g = g.with_center(cx, cy);
525            }
526            if let Some(r) = radius {
527                let r: f32 = eval_expression(r, local_context).try_into().unwrap();
528                g = g.with_radius(r);
529            }
530            Value::Brush(Brush::RadialGradient(g))
531        }
532        Expression::ConicGradient { from_angle, stops, center } => {
533            let from_angle: f32 = eval_expression(from_angle, local_context).try_into().unwrap();
534            let mut g = ConicGradientBrush::new(
535                from_angle,
536                stops.iter().map(|(color, stop)| {
537                    let color = eval_expression(color, local_context).try_into().unwrap();
538                    let position = eval_expression(stop, local_context).try_into().unwrap();
539                    GradientStop { color, position }
540                }),
541            );
542            if let Some((cx, cy)) = center {
543                let cx: f32 = eval_expression(cx, local_context).try_into().unwrap();
544                let cy: f32 = eval_expression(cy, local_context).try_into().unwrap();
545                g = g.with_center(cx, cy);
546            }
547            Value::Brush(Brush::ConicGradient(g))
548        }
549        Expression::EnumerationValue(value) => {
550            Value::EnumerationValue(value.enumeration.name.to_string(), value.to_string())
551        }
552        Expression::Keys(ks) => {
553            let mut modifiers = i_slint_core::input::KeyboardModifiers::default();
554            modifiers.alt = ks.modifiers.alt;
555            modifiers.control = ks.modifiers.control;
556            modifiers.shift = ks.modifiers.shift;
557            modifiers.meta = ks.modifiers.meta;
558
559            Value::Keys(i_slint_core::input::make_keys(
560                SharedString::from(&*ks.key),
561                modifiers,
562                ks.ignore_shift,
563                ks.ignore_alt,
564            ))
565        }
566        Expression::ReturnStatement(x) => {
567            let val = x.as_ref().map_or(Value::Void, |x| eval_expression(x, local_context));
568            if local_context.return_value.is_none() {
569                local_context.return_value = Some(val);
570            }
571            local_context.return_value.clone().unwrap()
572        }
573        Expression::LayoutCacheAccess {
574            layout_cache_prop,
575            index,
576            repeater_index,
577            entries_per_item,
578        } => {
579            let cache = load_property_helper(
580                &ComponentInstance::InstanceRef(local_context.component_instance),
581                &layout_cache_prop.element(),
582                layout_cache_prop.name(),
583            )
584            .unwrap();
585            if let Value::LayoutCache(cache) = cache {
586                // Coordinate cache
587                if let Some(ri) = repeater_index {
588                    let offset: usize = eval_expression(ri, local_context).try_into().unwrap();
589                    Value::Number(
590                        cache
591                            .get((cache[*index] as usize) + offset * entries_per_item)
592                            .copied()
593                            .unwrap_or(0.)
594                            .into(),
595                    )
596                } else {
597                    Value::Number(cache[*index].into())
598                }
599            } else if let Value::ArrayOfU16(cache) = cache {
600                // Organized Data cache
601                if let Some(ri) = repeater_index {
602                    let offset: usize = eval_expression(ri, local_context).try_into().unwrap();
603                    Value::Number(
604                        cache
605                            .get((cache[*index] as usize) + offset * entries_per_item)
606                            .copied()
607                            .unwrap_or(0)
608                            .into(),
609                    )
610                } else {
611                    Value::Number(cache[*index].into())
612                }
613            } else {
614                panic!("invalid layout cache")
615            }
616        }
617        Expression::GridRepeaterCacheAccess {
618            layout_cache_prop,
619            index,
620            repeater_index,
621            stride,
622            child_offset,
623            inner_repeater_index,
624            entries_per_item,
625        } => {
626            let cache = load_property_helper(
627                &ComponentInstance::InstanceRef(local_context.component_instance),
628                &layout_cache_prop.element(),
629                layout_cache_prop.name(),
630            )
631            .unwrap();
632            if let Value::LayoutCache(cache) = cache {
633                // Coordinate cache
634                let row_idx: usize =
635                    eval_expression(repeater_index, local_context).try_into().unwrap();
636                let stride_val: usize = eval_expression(stride, local_context).try_into().unwrap();
637                if let Some(inner_ri) = inner_repeater_index {
638                    let inner_offset: usize =
639                        eval_expression(inner_ri, local_context).try_into().unwrap();
640                    let base = cache[*index] as usize;
641                    let data_idx = base
642                        + row_idx * stride_val
643                        + *child_offset
644                        + inner_offset * *entries_per_item;
645                    Value::Number(cache.get(data_idx).copied().unwrap_or(0.).into())
646                } else {
647                    let base = cache[*index] as usize;
648                    let data_idx = base + row_idx * stride_val + *child_offset;
649                    Value::Number(cache.get(data_idx).copied().unwrap_or(0.).into())
650                }
651            } else if let Value::ArrayOfU16(cache) = cache {
652                // Organized Data cache
653                let row_idx: usize =
654                    eval_expression(repeater_index, local_context).try_into().unwrap();
655                let stride_val: usize = eval_expression(stride, local_context).try_into().unwrap();
656                if let Some(inner_ri) = inner_repeater_index {
657                    let inner_offset: usize =
658                        eval_expression(inner_ri, local_context).try_into().unwrap();
659                    let base = cache[*index] as usize;
660                    let data_idx = base
661                        + row_idx * stride_val
662                        + *child_offset
663                        + inner_offset * *entries_per_item;
664                    Value::Number(cache.get(data_idx).copied().unwrap_or(0).into())
665                } else {
666                    let base = cache[*index] as usize;
667                    let data_idx = base + row_idx * stride_val + *child_offset;
668                    Value::Number(cache.get(data_idx).copied().unwrap_or(0).into())
669                }
670            } else {
671                panic!("invalid layout cache")
672            }
673        }
674        Expression::ComputeBoxLayoutInfo { layout, orientation, cross_axis_size } => {
675            let cross = cross_axis_size.as_deref().map(|e| eval_to_f32(e, local_context));
676            crate::eval_layout::compute_box_layout_info(layout, *orientation, local_context, cross)
677        }
678        Expression::ComputeGridLayoutInfo {
679            layout_organized_data_prop,
680            layout,
681            orientation,
682            cross_axis_size,
683        } => {
684            let cross = cross_axis_size.as_deref().map(|e| eval_to_f32(e, local_context));
685            let cache = load_property_helper(
686                &ComponentInstance::InstanceRef(local_context.component_instance),
687                &layout_organized_data_prop.element(),
688                layout_organized_data_prop.name(),
689            )
690            .unwrap();
691            if let Value::ArrayOfU16(organized_data) = cache {
692                crate::eval_layout::compute_grid_layout_info(
693                    layout,
694                    &organized_data,
695                    *orientation,
696                    local_context,
697                    cross,
698                )
699            } else {
700                panic!("invalid layout organized data cache")
701            }
702        }
703        Expression::OrganizeGridLayout(lay) => {
704            crate::eval_layout::organize_grid_layout(lay, local_context)
705        }
706        Expression::SolveBoxLayout(lay, o) => {
707            crate::eval_layout::solve_box_layout(lay, *o, local_context)
708        }
709        Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } => {
710            let cache = load_property_helper(
711                &ComponentInstance::InstanceRef(local_context.component_instance),
712                &layout_organized_data_prop.element(),
713                layout_organized_data_prop.name(),
714            )
715            .unwrap();
716            if let Value::ArrayOfU16(organized_data) = cache {
717                crate::eval_layout::solve_grid_layout(
718                    &organized_data,
719                    layout,
720                    *orientation,
721                    local_context,
722                )
723            } else {
724                panic!("invalid layout organized data cache")
725            }
726        }
727        Expression::SolveFlexboxLayout(layout) => {
728            crate::eval_layout::solve_flexbox_layout(layout, local_context)
729        }
730        Expression::ComputeFlexboxLayoutInfo { layout, orientation, cross_axis_size } => {
731            let cross = cross_axis_size.as_deref().map(|e| eval_to_f32(e, local_context));
732            crate::eval_layout::compute_flexbox_layout_info(
733                layout,
734                *orientation,
735                local_context,
736                cross,
737            )
738        }
739        Expression::MinMax { ty: _, op, lhs, rhs } => {
740            let Value::Number(lhs) = eval_expression(lhs, local_context) else {
741                return local_context
742                    .return_value
743                    .clone()
744                    .expect("minmax lhs expression did not evaluate to number");
745            };
746            let Value::Number(rhs) = eval_expression(rhs, local_context) else {
747                return local_context
748                    .return_value
749                    .clone()
750                    .expect("minmax rhs expression did not evaluate to number");
751            };
752            match op {
753                MinMaxOp::Min => Value::Number(lhs.min(rhs)),
754                MinMaxOp::Max => Value::Number(lhs.max(rhs)),
755            }
756        }
757        Expression::EmptyComponentFactory => Value::ComponentFactory(Default::default()),
758        Expression::EmptyDataTransfer => Value::DataTransfer(Default::default()),
759        Expression::DebugHook { expression, id: _id, .. } => {
760            #[cfg(feature = "internal")]
761            {
762                if let Some(hook_value) = crate::debug_hook::trigger_debug_hook(
763                    &local_context.component_instance,
764                    _id.clone(),
765                ) {
766                    return hook_value;
767                }
768            }
769
770            eval_expression(expression, local_context)
771        }
772        Expression::Closure { .. } => unreachable!(
773            "closures are dispatched by their consuming builtin and should not go through eval_expression"
774        ),
775    }
776}
777
778fn call_builtin_function(
779    f: BuiltinFunction,
780    arguments: &[Expression],
781    local_context: &mut EvalLocalContext,
782    source_location: &Option<i_slint_compiler::diagnostics::SourceLocation>,
783) -> Value {
784    match f {
785        BuiltinFunction::GetWindowScaleFactor => Value::Number(
786            local_context.component_instance.access_window(|window| window.scale_factor()) as _,
787        ),
788        BuiltinFunction::GetWindowDefaultFontSize => Value::Number({
789            let component = local_context.component_instance;
790            let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
791            WindowItem::resolved_default_font_size(vtable::VRc::into_dyn(item_comp)).get() as _
792        }),
793        BuiltinFunction::AnimationTick => {
794            Value::Number(i_slint_core::animations::animation_tick() as f64)
795        }
796        BuiltinFunction::Debug => {
797            use corelib::debug_log::*;
798
799            let to_print: SharedString =
800                eval_expression(&arguments[0], local_context).try_into().unwrap();
801            let location = source_location.as_ref().and_then(|location| {
802                location.source_file().map(|file| {
803                    let (line, column) = file.line_column(
804                        location.span.offset,
805                        i_slint_compiler::diagnostics::ByteFormat::Utf8,
806                    );
807                    let path = file.path().to_string_lossy();
808                    (line, column, path)
809                })
810            });
811            let location = location.as_ref().map(|(line, column, path)| LogMessageLocation {
812                path,
813                line: *line,
814                column: *column,
815            });
816            let root_weak =
817                vtable::VWeak::into_dyn(local_context.component_instance.root_weak().clone());
818            if let Some(root) = root_weak.upgrade()
819                && let Some(ctx) = corelib::window::context_for_root(&root)
820            {
821                ctx.dispatch_log_message(LogMessage::new(
822                    LogMessageSource::SlintCode,
823                    location,
824                    format_args!("{to_print}"),
825                ));
826            } else {
827                log_message(LogMessage::new(
828                    LogMessageSource::SlintCode,
829                    location,
830                    format_args!("{to_print}"),
831                ));
832            }
833            Value::Void
834        }
835        BuiltinFunction::DecimalSeparator => Value::String(
836            local_context
837                .component_instance
838                .access_window(|window| window.context().locale_decimal_separator())
839                .into(),
840        ),
841        BuiltinFunction::Mod => {
842            let mut to_num = |e| -> f64 { eval_expression(e, local_context).try_into().unwrap() };
843            Value::Number(to_num(&arguments[0]).rem_euclid(to_num(&arguments[1])))
844        }
845        BuiltinFunction::Round => {
846            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
847            Value::Number(x.round())
848        }
849        BuiltinFunction::Ceil => {
850            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
851            Value::Number(x.ceil())
852        }
853        BuiltinFunction::Floor => {
854            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
855            Value::Number(x.floor())
856        }
857        BuiltinFunction::Sqrt => {
858            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
859            Value::Number(x.sqrt())
860        }
861        BuiltinFunction::Abs => {
862            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
863            Value::Number(x.abs())
864        }
865        BuiltinFunction::Sin => {
866            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
867            Value::Number(x.to_radians().sin())
868        }
869        BuiltinFunction::Cos => {
870            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
871            Value::Number(x.to_radians().cos())
872        }
873        BuiltinFunction::Tan => {
874            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
875            Value::Number(x.to_radians().tan())
876        }
877        BuiltinFunction::ASin => {
878            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
879            Value::Number(x.asin().to_degrees())
880        }
881        BuiltinFunction::ACos => {
882            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
883            Value::Number(x.acos().to_degrees())
884        }
885        BuiltinFunction::ATan => {
886            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
887            Value::Number(x.atan().to_degrees())
888        }
889        BuiltinFunction::ATan2 => {
890            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
891            let y: f64 = eval_expression(&arguments[1], local_context).try_into().unwrap();
892            Value::Number(x.atan2(y).to_degrees())
893        }
894        BuiltinFunction::Log => {
895            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
896            let y: f64 = eval_expression(&arguments[1], local_context).try_into().unwrap();
897            Value::Number(x.log(y))
898        }
899        BuiltinFunction::Ln => {
900            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
901            Value::Number(x.ln())
902        }
903        BuiltinFunction::Pow => {
904            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
905            let y: f64 = eval_expression(&arguments[1], local_context).try_into().unwrap();
906            Value::Number(x.powf(y))
907        }
908        BuiltinFunction::Exp => {
909            let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
910            Value::Number(x.exp())
911        }
912        BuiltinFunction::ToFixed => {
913            let n: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
914            let digits: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
915            let digits: usize = digits.max(0) as usize;
916            Value::String(i_slint_core::string::shared_string_from_number_fixed(n, digits))
917        }
918        BuiltinFunction::ToPrecision => {
919            let n: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
920            let precision: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
921            let precision: usize = precision.max(0) as usize;
922            Value::String(i_slint_core::string::shared_string_from_number_precision(n, precision))
923        }
924        BuiltinFunction::ToStringUnlocalized => {
925            let n: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
926            Value::String(i_slint_core::string::shared_string_from_number_unlocalized(n))
927        }
928        BuiltinFunction::SetFocusItem => {
929            if arguments.len() != 1 {
930                panic!("internal error: incorrect argument count to SetFocusItem")
931            }
932            let component = local_context.component_instance;
933            if let Expression::ElementReference(focus_item) = &arguments[0] {
934                generativity::make_guard!(guard);
935
936                let focus_item = focus_item.upgrade().unwrap();
937                let enclosing_component =
938                    enclosing_component_for_element(&focus_item, component, guard);
939                let description = enclosing_component.description;
940
941                let item_info = &description.items[focus_item.borrow().id.as_str()];
942
943                let focus_item_comp =
944                    enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
945
946                component.access_window(|window| {
947                    window.set_focus_item(
948                        &corelib::items::ItemRc::new(
949                            vtable::VRc::into_dyn(focus_item_comp),
950                            item_info.item_index(),
951                        ),
952                        true,
953                        FocusReason::Programmatic,
954                    )
955                });
956                Value::Void
957            } else {
958                panic!("internal error: argument to SetFocusItem must be an element")
959            }
960        }
961        BuiltinFunction::ClearFocusItem => {
962            if arguments.len() != 1 {
963                panic!("internal error: incorrect argument count to SetFocusItem")
964            }
965            let component = local_context.component_instance;
966            if let Expression::ElementReference(focus_item) = &arguments[0] {
967                generativity::make_guard!(guard);
968
969                let focus_item = focus_item.upgrade().unwrap();
970                let enclosing_component =
971                    enclosing_component_for_element(&focus_item, component, guard);
972                let description = enclosing_component.description;
973
974                let item_info = &description.items[focus_item.borrow().id.as_str()];
975
976                let focus_item_comp =
977                    enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
978
979                component.access_window(|window| {
980                    window.set_focus_item(
981                        &corelib::items::ItemRc::new(
982                            vtable::VRc::into_dyn(focus_item_comp),
983                            item_info.item_index(),
984                        ),
985                        false,
986                        FocusReason::Programmatic,
987                    )
988                });
989                Value::Void
990            } else {
991                panic!("internal error: argument to ClearFocusItem must be an element")
992            }
993        }
994        BuiltinFunction::ShowPopupWindow => {
995            if arguments.len() != 1 {
996                panic!("internal error: incorrect argument count to ShowPopupWindow")
997            }
998            let component = local_context.component_instance;
999            if let Expression::ElementReference(popup_window) = &arguments[0] {
1000                let popup_window = popup_window.upgrade().unwrap();
1001                let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
1002                let parent_component = {
1003                    let parent_elem = pop_comp.parent_element().unwrap();
1004                    parent_elem.borrow().enclosing_component.upgrade().unwrap()
1005                };
1006                let popup_list = parent_component.popup_windows.borrow();
1007                let popup =
1008                    popup_list.iter().find(|p| Rc::ptr_eq(&p.component, &pop_comp)).unwrap();
1009
1010                generativity::make_guard!(guard);
1011                let enclosing_component =
1012                    enclosing_component_for_element(&popup.parent_element, component, guard);
1013                let parent_item_info = &enclosing_component.description.items
1014                    [popup.parent_element.borrow().id.as_str()];
1015                let parent_item_comp =
1016                    enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1017                let parent_item = corelib::items::ItemRc::new(
1018                    vtable::VRc::into_dyn(parent_item_comp),
1019                    parent_item_info.item_index(),
1020                );
1021
1022                let close_policy = Value::EnumerationValue(
1023                    popup.close_policy.enumeration.name.to_string(),
1024                    popup.close_policy.to_string(),
1025                )
1026                .try_into()
1027                .expect("Invalid internal enumeration representation for close policy");
1028                let popup_x = popup.x.clone();
1029                let popup_y = popup.y.clone();
1030
1031                crate::dynamic_item_tree::show_popup(
1032                    popup_window,
1033                    enclosing_component,
1034                    popup,
1035                    move |instance_ref| {
1036                        let comp = ComponentInstance::InstanceRef(instance_ref);
1037                        let x = load_property_helper(&comp, &popup_x.element(), popup_x.name())
1038                            .unwrap();
1039                        let y = load_property_helper(&comp, &popup_y.element(), popup_y.name())
1040                            .unwrap();
1041                        corelib::api::LogicalPosition::new(
1042                            x.try_into().unwrap(),
1043                            y.try_into().unwrap(),
1044                        )
1045                    },
1046                    close_policy,
1047                    (*enclosing_component.self_weak().get().unwrap()).clone(),
1048                    component.window_adapter(),
1049                    &parent_item,
1050                );
1051                Value::Void
1052            } else {
1053                panic!("internal error: argument to ShowPopupWindow must be an element")
1054            }
1055        }
1056        BuiltinFunction::ClosePopupWindow => {
1057            let component = local_context.component_instance;
1058            if let Expression::ElementReference(popup_window) = &arguments[0] {
1059                let popup_window = popup_window.upgrade().unwrap();
1060                let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
1061                let parent_component = {
1062                    let parent_elem = pop_comp.parent_element().unwrap();
1063                    parent_elem.borrow().enclosing_component.upgrade().unwrap()
1064                };
1065                let popup_list = parent_component.popup_windows.borrow();
1066                let popup =
1067                    popup_list.iter().find(|p| Rc::ptr_eq(&p.component, &pop_comp)).unwrap();
1068
1069                generativity::make_guard!(guard);
1070                let enclosing_component =
1071                    enclosing_component_for_element(&popup.parent_element, component, guard);
1072                crate::dynamic_item_tree::close_popup(
1073                    popup_window,
1074                    enclosing_component,
1075                    enclosing_component.window_adapter(),
1076                );
1077
1078                Value::Void
1079            } else {
1080                panic!("internal error: argument to ClosePopupWindow must be an element")
1081            }
1082        }
1083        BuiltinFunction::ShowPopupMenu | BuiltinFunction::ShowPopupMenuInternal => {
1084            let [Expression::ElementReference(element), entries, position] = arguments else {
1085                panic!("internal error: incorrect argument count to ShowPopupMenu")
1086            };
1087            let position = eval_expression(position, local_context)
1088                .try_into()
1089                .expect("internal error: popup menu position argument should be a point");
1090
1091            let component = local_context.component_instance;
1092            let elem = element.upgrade().unwrap();
1093            generativity::make_guard!(guard);
1094            let enclosing_component = enclosing_component_for_element(&elem, component, guard);
1095            let description = enclosing_component.description;
1096            let item_info = &description.items[elem.borrow().id.as_str()];
1097            let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1098            let item_tree = vtable::VRc::into_dyn(item_comp);
1099            let item_rc = corelib::items::ItemRc::new(item_tree.clone(), item_info.item_index());
1100
1101            generativity::make_guard!(guard);
1102            let compiled = enclosing_component.description.popup_menu_description.unerase(guard);
1103            let extra_data = enclosing_component
1104                .description
1105                .extra_data_offset
1106                .apply(enclosing_component.as_ref());
1107            let inst = crate::dynamic_item_tree::instantiate(
1108                compiled.clone(),
1109                Some((*enclosing_component.self_weak().get().unwrap()).clone()),
1110                None,
1111                Some(&crate::dynamic_item_tree::WindowOptions::UseExistingWindow(
1112                    component.window_adapter(),
1113                )),
1114                extra_data.globals.get().unwrap().clone(),
1115            );
1116
1117            generativity::make_guard!(guard);
1118            let inst_ref = inst.unerase(guard);
1119            if let Expression::ElementReference(e) = entries {
1120                let menu_item_tree =
1121                    e.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
1122                let menu_item_tree = crate::dynamic_item_tree::make_menu_item_tree(
1123                    &menu_item_tree,
1124                    &enclosing_component,
1125                    None,
1126                    None,
1127                );
1128
1129                if component.access_window(|window| {
1130                    window.show_native_popup_menu(
1131                        vtable::VRc::into_dyn(menu_item_tree.clone()),
1132                        position,
1133                        &item_rc,
1134                    )
1135                }) {
1136                    return Value::Void;
1137                }
1138
1139                let (entries, sub_menu, activated) = menu_item_tree_properties(menu_item_tree);
1140
1141                compiled.set_binding(inst_ref.borrow(), "entries", entries).unwrap();
1142                compiled.set_callback_handler(inst_ref.borrow(), "sub-menu", sub_menu).unwrap();
1143                compiled.set_callback_handler(inst_ref.borrow(), "activated", activated).unwrap();
1144            } else {
1145                let entries = eval_expression(entries, local_context);
1146                compiled.set_property(inst_ref.borrow(), "entries", entries).unwrap();
1147                let item_weak = item_rc.downgrade();
1148                compiled
1149                    .set_callback_handler(
1150                        inst_ref.borrow(),
1151                        "sub-menu",
1152                        Box::new(move |args: &[Value]| -> Value {
1153                            item_weak
1154                                .upgrade()
1155                                .unwrap()
1156                                .downcast::<corelib::items::ContextMenu>()
1157                                .unwrap()
1158                                .sub_menu
1159                                .call(&(args[0].clone().try_into().unwrap(),))
1160                                .into()
1161                        }),
1162                    )
1163                    .unwrap();
1164                let item_weak = item_rc.downgrade();
1165                compiled
1166                    .set_callback_handler(
1167                        inst_ref.borrow(),
1168                        "activated",
1169                        Box::new(move |args: &[Value]| -> Value {
1170                            item_weak
1171                                .upgrade()
1172                                .unwrap()
1173                                .downcast::<corelib::items::ContextMenu>()
1174                                .unwrap()
1175                                .activated
1176                                .call(&(args[0].clone().try_into().unwrap(),));
1177                            Value::Void
1178                        }),
1179                    )
1180                    .unwrap();
1181            }
1182            let item_weak = item_rc.downgrade();
1183            compiled
1184                .set_callback_handler(
1185                    inst_ref.borrow(),
1186                    "close-popup",
1187                    Box::new(move |_args: &[Value]| -> Value {
1188                        let Some(item_rc) = item_weak.upgrade() else { return Value::Void };
1189                        if let Some(id) = item_rc
1190                            .downcast::<corelib::items::ContextMenu>()
1191                            .unwrap()
1192                            .popup_id
1193                            .take()
1194                        {
1195                            WindowInner::from_pub(item_rc.window_adapter().unwrap().window())
1196                                .close_popup(id);
1197                        }
1198                        Value::Void
1199                    }),
1200                )
1201                .unwrap();
1202            component.access_window(|window| {
1203                let context_menu_elem = item_rc.downcast::<corelib::items::ContextMenu>().unwrap();
1204                if let Some(old_id) = context_menu_elem.popup_id.take() {
1205                    window.close_popup(old_id)
1206                }
1207                let id = window.show_popup(
1208                    &vtable::VRc::into_dyn(inst.clone()),
1209                    Box::new(move || position),
1210                    corelib::items::PopupClosePolicy::CloseOnClickOutside,
1211                    &item_rc,
1212                    WindowKind::Menu,
1213                    Box::new(|_| {}),
1214                );
1215                context_menu_elem.popup_id.set(Some(id));
1216            });
1217            inst.run_setup_code();
1218            Value::Void
1219        }
1220        BuiltinFunction::SetSelectionOffsets => {
1221            if arguments.len() != 3 {
1222                panic!("internal error: incorrect argument count to select range function call")
1223            }
1224            let component = local_context.component_instance;
1225            if let Expression::ElementReference(element) = &arguments[0] {
1226                generativity::make_guard!(guard);
1227
1228                let elem = element.upgrade().unwrap();
1229                let enclosing_component = enclosing_component_for_element(&elem, component, guard);
1230                let description = enclosing_component.description;
1231                let item_info = &description.items[elem.borrow().id.as_str()];
1232                let item_ref =
1233                    unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1234
1235                let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1236                let item_rc = corelib::items::ItemRc::new(
1237                    vtable::VRc::into_dyn(item_comp),
1238                    item_info.item_index(),
1239                );
1240
1241                let window_adapter = component.window_adapter();
1242
1243                // TODO: Make this generic through RTTI
1244                if let Some(textinput) =
1245                    ItemRef::downcast_pin::<corelib::items::TextInput>(item_ref)
1246                {
1247                    let start: i32 =
1248                        eval_expression(&arguments[1], local_context).try_into().expect(
1249                            "internal error: second argument to set-selection-offsets must be an integer",
1250                        );
1251                    let end: i32 = eval_expression(&arguments[2], local_context).try_into().expect(
1252                        "internal error: third argument to set-selection-offsets must be an integer",
1253                    );
1254
1255                    textinput.set_selection_offsets(&window_adapter, &item_rc, start, end);
1256                } else {
1257                    panic!(
1258                        "internal error: member function called on element that doesn't have it: {}",
1259                        elem.borrow().original_name()
1260                    )
1261                }
1262
1263                Value::Void
1264            } else {
1265                panic!("internal error: first argument to set-selection-offsets must be an element")
1266            }
1267        }
1268        BuiltinFunction::ItemFontMetrics => {
1269            if arguments.len() != 1 {
1270                panic!(
1271                    "internal error: incorrect argument count to item font metrics function call"
1272                )
1273            }
1274            let component = local_context.component_instance;
1275            if let Expression::ElementReference(element) = &arguments[0] {
1276                generativity::make_guard!(guard);
1277
1278                let elem = element.upgrade().unwrap();
1279                let enclosing_component = enclosing_component_for_element(&elem, component, guard);
1280                let description = enclosing_component.description;
1281                let item_info = &description.items[elem.borrow().id.as_str()];
1282                let item_ref =
1283                    unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1284                let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1285                let item_rc = corelib::items::ItemRc::new(
1286                    vtable::VRc::into_dyn(item_comp),
1287                    item_info.item_index(),
1288                );
1289                let window_adapter = component.window_adapter();
1290                let metrics = i_slint_core::items::slint_text_item_fontmetrics(
1291                    &window_adapter,
1292                    item_ref,
1293                    &item_rc,
1294                );
1295                metrics.into()
1296            } else {
1297                panic!("internal error: argument to item-font-metrics must be an element")
1298            }
1299        }
1300        BuiltinFunction::StringIsFloat => {
1301            if arguments.len() != 1 {
1302                panic!("internal error: incorrect argument count to StringIsFloat")
1303            }
1304            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1305                Value::Bool(<f64 as core::str::FromStr>::from_str(s.as_str()).is_ok())
1306            } else {
1307                panic!("Argument not a string");
1308            }
1309        }
1310        BuiltinFunction::StringToFloat => {
1311            if arguments.len() != 1 {
1312                panic!("internal error: incorrect argument count to StringToFloat")
1313            }
1314            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1315                Value::Number(core::str::FromStr::from_str(s.as_str()).unwrap_or(0.))
1316            } else {
1317                panic!("Argument not a string");
1318            }
1319        }
1320        BuiltinFunction::StringIsEmpty => {
1321            if arguments.len() != 1 {
1322                panic!("internal error: incorrect argument count to StringIsEmpty")
1323            }
1324            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1325                Value::Bool(s.is_empty())
1326            } else {
1327                panic!("Argument not a string");
1328            }
1329        }
1330        BuiltinFunction::StringCharacterCount => {
1331            if arguments.len() != 1 {
1332                panic!("internal error: incorrect argument count to StringCharacterCount")
1333            }
1334            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1335                Value::Number(
1336                    unicode_segmentation::UnicodeSegmentation::graphemes(s.as_str(), true).count()
1337                        as f64,
1338                )
1339            } else {
1340                panic!("Argument not a string");
1341            }
1342        }
1343        BuiltinFunction::StringToLowercase => {
1344            if arguments.len() != 1 {
1345                panic!("internal error: incorrect argument count to StringToLowercase")
1346            }
1347            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1348                Value::String(s.to_lowercase().into())
1349            } else {
1350                panic!("Argument not a string");
1351            }
1352        }
1353        BuiltinFunction::StringToUppercase => {
1354            if arguments.len() != 1 {
1355                panic!("internal error: incorrect argument count to StringToUppercase")
1356            }
1357            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1358                Value::String(s.to_uppercase().into())
1359            } else {
1360                panic!("Argument not a string");
1361            }
1362        }
1363        BuiltinFunction::StringStartsWith => {
1364            if arguments.len() != 2 {
1365                panic!("internal error: incorrect argument count to StringStartsWith")
1366            }
1367            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1368                if let Value::String(pat) = eval_expression(&arguments[1], local_context) {
1369                    Value::Bool(s.starts_with(pat.as_str()))
1370                } else {
1371                    panic!("Second argument not a string");
1372                }
1373            } else {
1374                panic!("First argument not a string");
1375            }
1376        }
1377        BuiltinFunction::StringEndsWith => {
1378            if arguments.len() != 2 {
1379                panic!("internal error: incorrect argument count to StringEndsWith")
1380            }
1381            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1382                if let Value::String(pat) = eval_expression(&arguments[1], local_context) {
1383                    Value::Bool(s.ends_with(pat.as_str()))
1384                } else {
1385                    panic!("Second argument not a string");
1386                }
1387            } else {
1388                panic!("First argument not a string");
1389            }
1390        }
1391        BuiltinFunction::KeysToString => {
1392            if arguments.len() != 1 {
1393                panic!("internal error: incorrect argument count to KeysToString")
1394            }
1395            let Value::Keys(keys) = eval_expression(&arguments[0], local_context) else {
1396                panic!("Argument is not of type keys");
1397            };
1398            Value::String(ToSharedString::to_shared_string(&keys))
1399        }
1400        BuiltinFunction::ColorRgbaStruct => {
1401            if arguments.len() != 1 {
1402                panic!("internal error: incorrect argument count to ColorRGBAComponents")
1403            }
1404            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1405                let color = brush.color();
1406                let values = IntoIterator::into_iter([
1407                    ("red".to_string(), Value::Number(color.red().into())),
1408                    ("green".to_string(), Value::Number(color.green().into())),
1409                    ("blue".to_string(), Value::Number(color.blue().into())),
1410                    ("alpha".to_string(), Value::Number(color.alpha().into())),
1411                ])
1412                .collect();
1413                Value::Struct(values)
1414            } else {
1415                panic!("First argument not a color");
1416            }
1417        }
1418        BuiltinFunction::ColorHsvaStruct => {
1419            if arguments.len() != 1 {
1420                panic!("internal error: incorrect argument count to ColorHSVAComponents")
1421            }
1422            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1423                let color = brush.color().to_hsva();
1424                let values = IntoIterator::into_iter([
1425                    ("hue".to_string(), Value::Number(color.hue.into())),
1426                    ("saturation".to_string(), Value::Number(color.saturation.into())),
1427                    ("value".to_string(), Value::Number(color.value.into())),
1428                    ("alpha".to_string(), Value::Number(color.alpha.into())),
1429                ])
1430                .collect();
1431                Value::Struct(values)
1432            } else {
1433                panic!("First argument not a color");
1434            }
1435        }
1436        BuiltinFunction::ColorOklchStruct => {
1437            if arguments.len() != 1 {
1438                panic!("internal error: incorrect argument count to ColorOklchStruct")
1439            }
1440            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1441                let color = brush.color().to_oklch();
1442                let values = IntoIterator::into_iter([
1443                    ("lightness".to_string(), Value::Number(color.lightness.into())),
1444                    ("chroma".to_string(), Value::Number(color.chroma.into())),
1445                    ("hue".to_string(), Value::Number(color.hue.into())),
1446                    ("alpha".to_string(), Value::Number(color.alpha.into())),
1447                ])
1448                .collect();
1449                Value::Struct(values)
1450            } else {
1451                panic!("First argument not a color");
1452            }
1453        }
1454        BuiltinFunction::ColorBrighter => {
1455            if arguments.len() != 2 {
1456                panic!("internal error: incorrect argument count to ColorBrighter")
1457            }
1458            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1459                if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1460                    brush.brighter(factor as _).into()
1461                } else {
1462                    panic!("Second argument not a number");
1463                }
1464            } else {
1465                panic!("First argument not a color");
1466            }
1467        }
1468        BuiltinFunction::ColorDarker => {
1469            if arguments.len() != 2 {
1470                panic!("internal error: incorrect argument count to ColorDarker")
1471            }
1472            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1473                if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1474                    brush.darker(factor as _).into()
1475                } else {
1476                    panic!("Second argument not a number");
1477                }
1478            } else {
1479                panic!("First argument not a color");
1480            }
1481        }
1482        BuiltinFunction::ColorTransparentize => {
1483            if arguments.len() != 2 {
1484                panic!("internal error: incorrect argument count to ColorFaded")
1485            }
1486            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1487                if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1488                    brush.transparentize(factor as _).into()
1489                } else {
1490                    panic!("Second argument not a number");
1491                }
1492            } else {
1493                panic!("First argument not a color");
1494            }
1495        }
1496        BuiltinFunction::ColorMix => {
1497            if arguments.len() != 3 {
1498                panic!("internal error: incorrect argument count to ColorMix")
1499            }
1500
1501            let arg0 = eval_expression(&arguments[0], local_context);
1502            let arg1 = eval_expression(&arguments[1], local_context);
1503            let arg2 = eval_expression(&arguments[2], local_context);
1504
1505            if !matches!(arg0, Value::Brush(Brush::SolidColor(_))) {
1506                panic!("First argument not a color");
1507            }
1508            if !matches!(arg1, Value::Brush(Brush::SolidColor(_))) {
1509                panic!("Second argument not a color");
1510            }
1511            if !matches!(arg2, Value::Number(_)) {
1512                panic!("Third argument not a number");
1513            }
1514
1515            let (
1516                Value::Brush(Brush::SolidColor(color_a)),
1517                Value::Brush(Brush::SolidColor(color_b)),
1518                Value::Number(factor),
1519            ) = (arg0, arg1, arg2)
1520            else {
1521                unreachable!()
1522            };
1523
1524            color_a.mix(&color_b, factor as _).into()
1525        }
1526        BuiltinFunction::ColorWithAlpha => {
1527            if arguments.len() != 2 {
1528                panic!("internal error: incorrect argument count to ColorWithAlpha")
1529            }
1530            if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1531                if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1532                    brush.with_alpha(factor as _).into()
1533                } else {
1534                    panic!("Second argument not a number");
1535                }
1536            } else {
1537                panic!("First argument not a color");
1538            }
1539        }
1540        BuiltinFunction::ImageSize => {
1541            if arguments.len() != 1 {
1542                panic!("internal error: incorrect argument count to ImageSize")
1543            }
1544            if let Value::Image(img) = eval_expression(&arguments[0], local_context) {
1545                let size = img.size();
1546                let values = IntoIterator::into_iter([
1547                    ("width".to_string(), Value::Number(size.width as f64)),
1548                    ("height".to_string(), Value::Number(size.height as f64)),
1549                ])
1550                .collect();
1551                Value::Struct(values)
1552            } else {
1553                panic!("First argument not an image");
1554            }
1555        }
1556        BuiltinFunction::ArrayLength => {
1557            if arguments.len() != 1 {
1558                panic!("internal error: incorrect argument count to ArrayLength")
1559            }
1560            match eval_expression(&arguments[0], local_context) {
1561                Value::Model(model) => {
1562                    model.model_tracker().track_row_count_changes();
1563                    Value::Number(model.row_count() as f64)
1564                }
1565                _ => {
1566                    panic!("First argument not an array: {:?}", arguments[0]);
1567                }
1568            }
1569        }
1570        BuiltinFunction::ArrayPush => {
1571            if arguments.len() != 2 {
1572                panic!("internal error: incorrect argument count to ArrayPush")
1573            }
1574
1575            let model = match eval_expression(&arguments[0], local_context) {
1576                Value::Model(m) => m,
1577                _ => panic!("First argument not an array: {:?}", arguments[0]),
1578            };
1579            let value = eval_expression(&arguments[1], local_context);
1580
1581            model.push_row(value);
1582
1583            Value::Void
1584        }
1585        BuiltinFunction::ArrayRemove => {
1586            if arguments.len() != 2 {
1587                panic!("internal error: incorrect argument count to ArrayRemove")
1588            }
1589
1590            let model = match eval_expression(&arguments[0], local_context) {
1591                Value::Model(m) => m,
1592                _ => panic!("First argument not an array: {:?}", arguments[0]),
1593            };
1594            let index = match eval_expression(&arguments[1], local_context) {
1595                Value::Number(i) => i,
1596                _ => panic!("Second argument not an integer: {:?}", arguments[1]),
1597            };
1598
1599            model.remove_row(index as isize);
1600
1601            Value::Void
1602        }
1603
1604        BuiltinFunction::ArrayInsert => {
1605            if arguments.len() != 3 {
1606                panic!("internal error: incorrect argument count to ArrayInsert")
1607            }
1608
1609            let model = match eval_expression(&arguments[0], local_context) {
1610                Value::Model(m) => m,
1611                _ => panic!("First argument not an array: {:?}", arguments[0]),
1612            };
1613            let index = match eval_expression(&arguments[1], local_context) {
1614                Value::Number(i) => i,
1615                _ => panic!("Second argument not an integer: {:?}", arguments[1]),
1616            };
1617
1618            let value = eval_expression(&arguments[2], local_context);
1619            model.insert_row(index as isize, value);
1620
1621            Value::Void
1622        }
1623        BuiltinFunction::Rgb => {
1624            let r: i32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1625            let g: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1626            let b: i32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1627            let a: f32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1628            let r: u8 = r.clamp(0, 255) as u8;
1629            let g: u8 = g.clamp(0, 255) as u8;
1630            let b: u8 = b.clamp(0, 255) as u8;
1631            let a: u8 = (255. * a).clamp(0., 255.) as u8;
1632            Value::Brush(Brush::SolidColor(Color::from_argb_u8(a, r, g, b)))
1633        }
1634        BuiltinFunction::Hsv => {
1635            let h: f32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1636            let s: f32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1637            let v: f32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1638            let a: f32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1639            let a = (1. * a).clamp(0., 1.);
1640            Value::Brush(Brush::SolidColor(Color::from_hsva(h, s, v, a)))
1641        }
1642        BuiltinFunction::Oklch => {
1643            let l: f32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1644            let c: f32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1645            let h: f32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1646            let a: f32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1647            let l = l.clamp(0., 1.);
1648            let c = c.max(0.);
1649            let a = a.clamp(0., 1.);
1650            Value::Brush(Brush::SolidColor(Color::from_oklch(l, c, h, a)))
1651        }
1652        BuiltinFunction::ColorScheme => {
1653            let root_weak =
1654                vtable::VWeak::into_dyn(local_context.component_instance.root_weak().clone());
1655            let root = root_weak.upgrade().unwrap();
1656            corelib::window::context_for_root(&root)
1657                .map_or(corelib::items::ColorScheme::Unknown, |ctx| ctx.color_scheme(Some(&root)))
1658                .into()
1659        }
1660        BuiltinFunction::AccentColor => {
1661            let root_weak =
1662                vtable::VWeak::into_dyn(local_context.component_instance.root_weak().clone());
1663            let root = root_weak.upgrade().unwrap();
1664            Value::Brush(corelib::Brush::SolidColor(corelib::window::accent_color(&root)))
1665        }
1666        BuiltinFunction::SupportsNativeMenuBar => local_context
1667            .component_instance
1668            .window_adapter()
1669            .internal(corelib::InternalToken)
1670            .is_some_and(|x| x.supports_native_menu_bar())
1671            .into(),
1672        BuiltinFunction::SetupMenuBar => {
1673            let component = local_context.component_instance;
1674            let [
1675                Expression::PropertyReference(entries_nr),
1676                Expression::PropertyReference(sub_menu_nr),
1677                Expression::PropertyReference(activated_nr),
1678                Expression::ElementReference(item_tree_root),
1679                Expression::BoolLiteral(no_native),
1680                condition,
1681                visible,
1682                ..,
1683            ] = arguments
1684            else {
1685                panic!("internal error: incorrect argument count to SetupMenuBar")
1686            };
1687
1688            let menu_item_tree =
1689                item_tree_root.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
1690            let menu_item_tree = crate::dynamic_item_tree::make_menu_item_tree(
1691                &menu_item_tree,
1692                &component,
1693                Some(condition),
1694                Some(visible),
1695            );
1696
1697            let window_adapter = component.window_adapter();
1698            let window_inner = WindowInner::from_pub(window_adapter.window());
1699            let menubar = vtable::VRc::into_dyn(vtable::VRc::clone(&menu_item_tree));
1700            window_inner.setup_menubar_shortcuts(vtable::VRc::clone(&menubar));
1701
1702            if !no_native && window_inner.supports_native_menu_bar() {
1703                window_inner.setup_menubar(menubar);
1704                return Value::Void;
1705            }
1706
1707            let (entries, sub_menu, activated) = menu_item_tree_properties(menu_item_tree);
1708
1709            assert_eq!(
1710                entries_nr.element().borrow().id,
1711                component.description.original.root_element.borrow().id,
1712                "entries need to be in the main element"
1713            );
1714            local_context
1715                .component_instance
1716                .description
1717                .set_binding(component.borrow(), entries_nr.name(), entries)
1718                .unwrap();
1719            let i = &ComponentInstance::InstanceRef(local_context.component_instance);
1720            set_callback_handler(i, &sub_menu_nr.element(), sub_menu_nr.name(), sub_menu).unwrap();
1721            set_callback_handler(i, &activated_nr.element(), activated_nr.name(), activated)
1722                .unwrap();
1723
1724            Value::Void
1725        }
1726        BuiltinFunction::SetupSystemTrayIcon => {
1727            let [
1728                Expression::ElementReference(system_tray_elem),
1729                Expression::ElementReference(item_tree_root),
1730                rest @ ..,
1731            ] = arguments
1732            else {
1733                panic!("internal error: incorrect argument count to SetupSystemTrayIcon")
1734            };
1735
1736            let component = local_context.component_instance;
1737            let elem = system_tray_elem.upgrade().unwrap();
1738            generativity::make_guard!(guard);
1739            let enclosing_component = enclosing_component_for_element(&elem, component, guard);
1740            let description = enclosing_component.description;
1741            let item_info = &description.items[elem.borrow().id.as_str()];
1742            let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1743            let item_tree = vtable::VRc::into_dyn(item_comp);
1744            let item_rc = corelib::items::ItemRc::new(item_tree.clone(), item_info.item_index());
1745
1746            let menu_item_tree_component =
1747                item_tree_root.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
1748            let menu_vrc = crate::dynamic_item_tree::make_menu_item_tree(
1749                &menu_item_tree_component,
1750                &enclosing_component,
1751                rest.first(),
1752                None,
1753            );
1754
1755            let system_tray =
1756                item_rc.downcast::<corelib::items::SystemTrayIcon>().expect("SystemTrayIcon item");
1757            system_tray.as_pin_ref().set_menu(&item_rc, vtable::VRc::into_dyn(menu_vrc));
1758
1759            Value::Void
1760        }
1761        BuiltinFunction::MonthDayCount => {
1762            let m: u32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1763            let y: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1764            Value::Number(i_slint_core::date_time::month_day_count(m, y).unwrap_or(0) as f64)
1765        }
1766        BuiltinFunction::MonthOffset => {
1767            let m: u32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1768            let y: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1769
1770            Value::Number(i_slint_core::date_time::month_offset(m, y) as f64)
1771        }
1772        BuiltinFunction::FormatDate => {
1773            let f: SharedString = eval_expression(&arguments[0], local_context).try_into().unwrap();
1774            let d: u32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1775            let m: u32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1776            let y: i32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1777
1778            Value::String(i_slint_core::date_time::format_date(&f, d, m, y))
1779        }
1780        BuiltinFunction::DateNow => Value::Model(ModelRc::new(VecModel::from(
1781            i_slint_core::date_time::date_now()
1782                .into_iter()
1783                .map(|x| Value::Number(x as f64))
1784                .collect::<Vec<_>>(),
1785        ))),
1786        BuiltinFunction::ValidDate => {
1787            let d: SharedString = eval_expression(&arguments[0], local_context).try_into().unwrap();
1788            let f: SharedString = eval_expression(&arguments[1], local_context).try_into().unwrap();
1789            Value::Bool(i_slint_core::date_time::parse_date(d.as_str(), f.as_str()).is_some())
1790        }
1791        BuiltinFunction::ParseDate => {
1792            let d: SharedString = eval_expression(&arguments[0], local_context).try_into().unwrap();
1793            let f: SharedString = eval_expression(&arguments[1], local_context).try_into().unwrap();
1794
1795            Value::Model(ModelRc::new(
1796                i_slint_core::date_time::parse_date(d.as_str(), f.as_str())
1797                    .map(|x| {
1798                        VecModel::from(
1799                            x.into_iter().map(|x| Value::Number(x as f64)).collect::<Vec<_>>(),
1800                        )
1801                    })
1802                    .unwrap_or_default(),
1803            ))
1804        }
1805        BuiltinFunction::TextInputFocused => Value::Bool(
1806            local_context.component_instance.access_window(|window| window.text_input_focused())
1807                as _,
1808        ),
1809        BuiltinFunction::SetTextInputFocused => {
1810            local_context.component_instance.access_window(|window| {
1811                window.set_text_input_focused(
1812                    eval_expression(&arguments[0], local_context).try_into().unwrap(),
1813                )
1814            });
1815            Value::Void
1816        }
1817        BuiltinFunction::ImplicitLayoutInfo(orient) => {
1818            let component = local_context.component_instance;
1819            if let [Expression::ElementReference(item), constraint_expr] = arguments {
1820                generativity::make_guard!(guard);
1821
1822                let constraint: f32 =
1823                    eval_expression(constraint_expr, local_context).try_into().unwrap_or(-1.);
1824
1825                let item = item.upgrade().unwrap();
1826                let enclosing_component = enclosing_component_for_element(&item, component, guard);
1827                let description = enclosing_component.description;
1828                let item_info = &description.items[item.borrow().id.as_str()];
1829                let item_ref =
1830                    unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1831                let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1832                let window_adapter = component.window_adapter();
1833                item_ref
1834                    .as_ref()
1835                    .layout_info(
1836                        crate::eval_layout::to_runtime(orient),
1837                        constraint,
1838                        &window_adapter,
1839                        &ItemRc::new(vtable::VRc::into_dyn(item_comp), item_info.item_index()),
1840                    )
1841                    .into()
1842            } else {
1843                panic!("internal error: incorrect arguments to ImplicitLayoutInfo {arguments:?}");
1844            }
1845        }
1846        BuiltinFunction::ItemAbsolutePosition => {
1847            if arguments.len() != 1 {
1848                panic!("internal error: incorrect argument count to ItemAbsolutePosition")
1849            }
1850
1851            let component = local_context.component_instance;
1852
1853            if let Expression::ElementReference(item) = &arguments[0] {
1854                let item_rc = item_rc_for_element(item, component);
1855
1856                // Map the item's own geometry origin through the ancestor transforms so the
1857                // result is the item's absolute position (not its parent's).
1858                item_rc.map_to_window(item_rc.geometry().origin).to_untyped().into()
1859            } else {
1860                panic!("internal error: argument to SetFocusItem must be an element")
1861            }
1862        }
1863        BuiltinFunction::RegisterCustomFontByPath => {
1864            if arguments.len() != 1 {
1865                panic!("internal error: incorrect argument count to RegisterCustomFontByPath")
1866            }
1867            let component = local_context.component_instance;
1868            if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1869                // If the window adapter can't be created, log and skip the registration
1870                // instead of panicking: the same error resurfaces when the window is
1871                // actually used.
1872                let result = component.try_window_adapter().map_err(|e| e.to_string()).and_then(
1873                    |window_adapter| {
1874                        window_adapter
1875                            .renderer()
1876                            .register_font_from_path(&std::path::PathBuf::from(s.as_str()))
1877                            .map_err(|e| format!("Cannot load custom font {}: {e}", s.as_str()))
1878                    },
1879                );
1880                if let Err(err) = result {
1881                    corelib::debug_log!("{err}");
1882                }
1883                Value::Void
1884            } else {
1885                panic!("Argument not a string");
1886            }
1887        }
1888        BuiltinFunction::RegisterCustomFontByMemory | BuiltinFunction::RegisterBitmapFont => {
1889            unimplemented!()
1890        }
1891        BuiltinFunction::Translate => {
1892            let original: SharedString =
1893                eval_expression(&arguments[0], local_context).try_into().unwrap();
1894            let context: SharedString =
1895                eval_expression(&arguments[1], local_context).try_into().unwrap();
1896            let domain: SharedString =
1897                eval_expression(&arguments[2], local_context).try_into().unwrap();
1898            let args = eval_expression(&arguments[3], local_context);
1899            let Value::Model(args) = args else { panic!("Args to translate not a model {args:?}") };
1900            struct StringModelWrapper(ModelRc<Value>);
1901            impl corelib::translations::FormatArgs for StringModelWrapper {
1902                type Output<'a> = SharedString;
1903                fn from_index(&self, index: usize) -> Option<SharedString> {
1904                    self.0.row_data(index).map(|x| x.try_into().unwrap())
1905                }
1906            }
1907            Value::String(corelib::translations::translate(
1908                &original,
1909                &context,
1910                &domain,
1911                &StringModelWrapper(args),
1912                eval_expression(&arguments[4], local_context).try_into().unwrap(),
1913                &SharedString::try_from(eval_expression(&arguments[5], local_context)).unwrap(),
1914            ))
1915        }
1916        BuiltinFunction::Use24HourFormat => Value::Bool(corelib::date_time::use_24_hour_format()),
1917        BuiltinFunction::UpdateTimers => {
1918            crate::dynamic_item_tree::update_timers(local_context.component_instance);
1919            Value::Void
1920        }
1921        BuiltinFunction::DetectOperatingSystem => i_slint_core::detect_operating_system().into(),
1922        // start and stop are unreachable because they are lowered to simple assignment of running
1923        BuiltinFunction::StartTimer => unreachable!(),
1924        BuiltinFunction::StopTimer => unreachable!(),
1925        BuiltinFunction::RestartTimer => {
1926            if let [Expression::ElementReference(timer_element)] = arguments {
1927                crate::dynamic_item_tree::restart_timer(
1928                    timer_element.clone(),
1929                    local_context.component_instance,
1930                );
1931
1932                Value::Void
1933            } else {
1934                panic!("internal error: argument to RestartTimer must be an element")
1935            }
1936        }
1937        BuiltinFunction::OpenUrl => {
1938            let url: SharedString =
1939                eval_expression(&arguments[0], local_context).try_into().unwrap();
1940            let window_adapter = local_context.component_instance.window_adapter();
1941            Value::Bool(corelib::open_url(&url, window_adapter.window()).is_ok())
1942        }
1943        BuiltinFunction::MacosBringAllWindowsToFront => {
1944            corelib::macos_bring_all_windows_to_front();
1945            Value::Void
1946        }
1947        BuiltinFunction::ParseMarkdown => {
1948            let format_string: SharedString =
1949                eval_expression(&arguments[0], local_context).try_into().unwrap();
1950            let args: ModelRc<corelib::styled_text::StyledText> =
1951                eval_expression(&arguments[1], local_context).try_into().unwrap();
1952            Value::StyledText(corelib::styled_text::parse_markdown(
1953                &format_string,
1954                &args.iter().collect::<Vec<_>>(),
1955            ))
1956        }
1957        BuiltinFunction::StringToStyledText => {
1958            let string: SharedString =
1959                eval_expression(&arguments[0], local_context).try_into().unwrap();
1960            Value::StyledText(corelib::styled_text::string_to_styled_text(string.to_string()))
1961        }
1962        BuiltinFunction::ColorToStyledText => {
1963            let color: corelib::Color =
1964                eval_expression(&arguments[0], local_context).try_into().unwrap();
1965            Value::StyledText(corelib::styled_text::color_to_styled_text(color))
1966        }
1967        BuiltinFunction::PathPointAt => {
1968            let component = local_context.component_instance;
1969
1970            if let Expression::ElementReference(item) = &arguments[0] {
1971                let item_rc = item_rc_for_element(item, component);
1972
1973                let t: f32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1974
1975                item_rc
1976                    .downcast::<corelib::items::Path>()
1977                    .unwrap()
1978                    .as_pin_ref()
1979                    .point_at(&item_rc, t)
1980                    .to_untyped()
1981                    .into()
1982            } else {
1983                panic!("internal error: argument to PathPointAt must be an element")
1984            }
1985        }
1986        BuiltinFunction::PathAngleAt => {
1987            let component = local_context.component_instance;
1988
1989            if let Expression::ElementReference(item) = &arguments[0] {
1990                let item_rc = item_rc_for_element(item, component);
1991
1992                let t: f32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1993
1994                item_rc
1995                    .downcast::<corelib::items::Path>()
1996                    .unwrap()
1997                    .as_pin_ref()
1998                    .angle_at(&item_rc, t)
1999                    .into()
2000            } else {
2001                panic!("internal error: argument to PathAngleAt must be an element")
2002            }
2003        }
2004        BuiltinFunction::ArrayAny | BuiltinFunction::ArrayAll => {
2005            let is_all = matches!(f, BuiltinFunction::ArrayAll);
2006            let model: ModelRc<Value> =
2007                eval_expression(&arguments[0], local_context).try_into().unwrap();
2008            let Expression::Closure { arg_name, expression } = &arguments[1] else {
2009                panic!("internal error: Array.any/all expects a closure as second argument")
2010            };
2011            // `all` short-circuits on false, `any` short-circuits on true.
2012            eval_array_row_predicate(&model, arg_name, expression, local_context, |_row, result| {
2013                (result != is_all).then_some(Value::Bool(!is_all))
2014            })
2015            .unwrap_or(Value::Bool(is_all))
2016        }
2017        BuiltinFunction::ArrayFindIndex => {
2018            let model: ModelRc<Value> =
2019                eval_expression(&arguments[0], local_context).try_into().unwrap();
2020            let Expression::Closure { arg_name, expression } = &arguments[1] else {
2021                panic!("internal error: Array.find-index expects a closure as second argument")
2022            };
2023            eval_array_row_predicate(&model, arg_name, expression, local_context, |row, result| {
2024                result.then_some(Value::Number(row as f64))
2025            })
2026            .unwrap_or(Value::Number(-1.))
2027        }
2028    }
2029}
2030
2031fn item_rc_for_element(
2032    item: &Weak<RefCell<Element>>,
2033    component: InstanceRef,
2034) -> corelib::items::ItemRc {
2035    generativity::make_guard!(guard);
2036    let item = item.upgrade().unwrap();
2037    let enclosing_component = enclosing_component_for_element(&item, component, guard);
2038    let description = enclosing_component.description;
2039
2040    let item_info = &description.items[item.borrow().id.as_str()];
2041
2042    let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
2043
2044    corelib::items::ItemRc::new(vtable::VRc::into_dyn(item_comp), item_info.item_index())
2045}
2046
2047fn call_item_member_function(nr: &NamedReference, local_context: &mut EvalLocalContext) -> Value {
2048    let component = local_context.component_instance;
2049    let elem = nr.element();
2050    let name = nr.name().as_str();
2051    generativity::make_guard!(guard);
2052    let enclosing_component = enclosing_component_for_element(&elem, component, guard);
2053    let description = enclosing_component.description;
2054    let item_info = &description.items[elem.borrow().id.as_str()];
2055    let item_ref = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
2056
2057    let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
2058    let item_rc =
2059        corelib::items::ItemRc::new(vtable::VRc::into_dyn(item_comp), item_info.item_index());
2060
2061    let window_adapter = component.window_adapter();
2062
2063    // TODO: Make this generic through RTTI
2064    if let Some(textinput) = ItemRef::downcast_pin::<corelib::items::TextInput>(item_ref) {
2065        match name {
2066            "select-all" => textinput.select_all(&window_adapter, &item_rc),
2067            "clear-selection" => textinput.clear_selection(&window_adapter, &item_rc),
2068            "cut" => textinput.cut(&window_adapter, &item_rc),
2069            "copy" => textinput.copy(&window_adapter, &item_rc),
2070            "paste" => textinput.paste(&window_adapter, &item_rc),
2071            "undo" => textinput.undo(&window_adapter, &item_rc),
2072            "redo" => textinput.redo(&window_adapter, &item_rc),
2073            _ => panic!("internal: Unknown member function {name} called on TextInput"),
2074        }
2075    } else if let Some(s) = ItemRef::downcast_pin::<corelib::items::SwipeGestureHandler>(item_ref) {
2076        match name {
2077            "cancel" => s.cancel(&window_adapter, &item_rc),
2078            _ => panic!("internal: Unknown member function {name} called on SwipeGestureHandler"),
2079        }
2080    } else if let Some(s) = ItemRef::downcast_pin::<corelib::items::ContextMenu>(item_ref) {
2081        match name {
2082            "close" => s.close(&window_adapter, &item_rc),
2083            "is-open" => return Value::Bool(s.is_open(&window_adapter, &item_rc)),
2084            _ => {
2085                panic!("internal: Unknown member function {name} called on ContextMenu")
2086            }
2087        }
2088    } else if let Some(s) = ItemRef::downcast_pin::<corelib::items::WindowItem>(item_ref) {
2089        match name {
2090            "hide" => s.hide(&window_adapter, &item_rc),
2091            "close" => return Value::Bool(s.close(&window_adapter, &item_rc)),
2092            _ => {
2093                panic!("internal: Unknown member function {name} called on WindowItem")
2094            }
2095        }
2096    } else {
2097        panic!(
2098            "internal error: member function {name} called on element that doesn't have it: {}",
2099            elem.borrow().original_name()
2100        )
2101    }
2102
2103    Value::Void
2104}
2105
2106fn eval_assignment(lhs: &Expression, op: char, rhs: Value, local_context: &mut EvalLocalContext) {
2107    let eval = |lhs| match (lhs, &rhs, op) {
2108        (Value::String(ref mut a), Value::String(b), '+') => {
2109            a.push_str(b.as_str());
2110            Value::String(a.clone())
2111        }
2112        (Value::Number(a), Value::Number(b), '+') => Value::Number(a + b),
2113        (Value::Number(a), Value::Number(b), '-') => Value::Number(a - b),
2114        (Value::Number(a), Value::Number(b), '/') => Value::Number(a / b),
2115        (Value::Number(a), Value::Number(b), '*') => Value::Number(a * b),
2116        (lhs, rhs, op) => panic!("unsupported {lhs:?} {op} {rhs:?}"),
2117    };
2118    match lhs {
2119        Expression::PropertyReference(nr) => {
2120            let element = nr.element();
2121            generativity::make_guard!(guard);
2122            let enclosing_component = enclosing_component_instance_for_element(
2123                &element,
2124                &ComponentInstance::InstanceRef(local_context.component_instance),
2125                guard,
2126            );
2127
2128            match enclosing_component {
2129                ComponentInstance::InstanceRef(enclosing_component) => {
2130                    // Go through `store_property` (also for compound assignments) so the
2131                    // property's animation is applied, instead of setting it directly.
2132                    let value = if op == '=' {
2133                        rhs
2134                    } else {
2135                        eval(load_property(enclosing_component, &element, nr.name()).unwrap())
2136                    };
2137                    store_property(enclosing_component, &element, nr.name(), value).unwrap();
2138                }
2139                ComponentInstance::GlobalComponent(global) => {
2140                    let val = if op == '=' {
2141                        rhs
2142                    } else {
2143                        eval(global.as_ref().get_property(nr.name()).unwrap())
2144                    };
2145                    global.as_ref().set_property(nr.name(), val).unwrap();
2146                }
2147            }
2148        }
2149        Expression::StructFieldAccess { base, name } => {
2150            if let Value::Struct(mut o) = eval_expression(base, local_context) {
2151                let mut r = o.get_field(name).unwrap().clone();
2152                r = if op == '=' { rhs } else { eval(std::mem::take(&mut r)) };
2153                o.set_field(name.to_string(), r);
2154                eval_assignment(base, '=', Value::Struct(o), local_context)
2155            }
2156        }
2157        Expression::RepeaterModelReference { element } => {
2158            let element = element.upgrade().unwrap();
2159            let component_instance = local_context.component_instance;
2160            generativity::make_guard!(g1);
2161            let enclosing_component =
2162                enclosing_component_for_element(&element, component_instance, g1);
2163            // we need a 'static Repeater component in order to call model_set_row_data, so get it.
2164            // Safety: This is the only 'static Id in scope.
2165            let static_guard =
2166                unsafe { generativity::Guard::new(generativity::Id::<'static>::new()) };
2167            let repeater = crate::dynamic_item_tree::get_repeater_by_name(
2168                enclosing_component,
2169                element.borrow().id.as_str(),
2170                static_guard,
2171            );
2172            repeater.0.model_set_row_data(
2173                eval_expression(
2174                    &Expression::RepeaterIndexReference { element: Rc::downgrade(&element) },
2175                    local_context,
2176                )
2177                .try_into()
2178                .unwrap(),
2179                if op == '=' {
2180                    rhs
2181                } else {
2182                    eval(eval_expression(
2183                        &Expression::RepeaterModelReference { element: Rc::downgrade(&element) },
2184                        local_context,
2185                    ))
2186                },
2187            )
2188        }
2189        Expression::ArrayIndex { array, index } => {
2190            let array = eval_expression(array, local_context);
2191            let index = eval_expression(index, local_context);
2192            match (array, index) {
2193                (Value::Model(model), Value::Number(index)) => {
2194                    if index >= 0. && (index as usize) < model.row_count() {
2195                        let index = index as usize;
2196                        if op == '=' {
2197                            model.set_row_data(index, rhs);
2198                        } else {
2199                            model.set_row_data(
2200                                index,
2201                                eval(
2202                                    model
2203                                        .row_data(index)
2204                                        .unwrap_or_else(|| default_value_for_type(&lhs.ty())),
2205                                ),
2206                            );
2207                        }
2208                    }
2209                }
2210                _ => {
2211                    eprintln!("Attempting to write into an array that cannot be written");
2212                }
2213            }
2214        }
2215        _ => panic!("typechecking should make sure this was a PropertyReference"),
2216    }
2217}
2218
2219pub fn load_property(component: InstanceRef, element: &ElementRc, name: &str) -> Result<Value, ()> {
2220    load_property_helper(&ComponentInstance::InstanceRef(component), element, name)
2221}
2222
2223fn load_property_helper(
2224    component_instance: &ComponentInstance,
2225    element: &ElementRc,
2226    name: &str,
2227) -> Result<Value, ()> {
2228    generativity::make_guard!(guard);
2229    match enclosing_component_instance_for_element(element, component_instance, guard) {
2230        ComponentInstance::InstanceRef(enclosing_component) => {
2231            let element = element.borrow();
2232            if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
2233            {
2234                if let Some(x) = enclosing_component.description.custom_properties.get(name) {
2235                    return unsafe {
2236                        x.prop.get(Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset)))
2237                    };
2238                } else if enclosing_component.description.original.is_global() {
2239                    return Err(());
2240                }
2241            };
2242            let item_info = enclosing_component
2243                .description
2244                .items
2245                .get(element.id.as_str())
2246                .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, name));
2247            core::mem::drop(element);
2248            let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
2249            Ok(item_info.rtti.properties.get(name).ok_or(())?.get(item))
2250        }
2251        ComponentInstance::GlobalComponent(glob) => glob.as_ref().get_property(name),
2252    }
2253}
2254
2255pub fn store_property(
2256    component_instance: InstanceRef,
2257    element: &ElementRc,
2258    name: &str,
2259    mut value: Value,
2260) -> Result<(), SetPropertyError> {
2261    generativity::make_guard!(guard);
2262    match enclosing_component_instance_for_element(
2263        element,
2264        &ComponentInstance::InstanceRef(component_instance),
2265        guard,
2266    ) {
2267        ComponentInstance::InstanceRef(enclosing_component) => {
2268            let maybe_animation = match element.borrow().binding_cell_including_synthetic(name) {
2269                Some(b) => crate::dynamic_item_tree::animation_for_property(
2270                    enclosing_component,
2271                    &b.borrow().animation,
2272                ),
2273                None => {
2274                    crate::dynamic_item_tree::animation_for_property(enclosing_component, &None)
2275                }
2276            };
2277
2278            let component = element.borrow().enclosing_component.upgrade().unwrap();
2279            if element.borrow().id == component.root_element.borrow().id {
2280                if let Some(x) = enclosing_component.description.custom_properties.get(name) {
2281                    if let Some(orig_decl) = enclosing_component
2282                        .description
2283                        .original
2284                        .root_element
2285                        .borrow()
2286                        .property_declarations
2287                        .get(name)
2288                    {
2289                        // Do an extra type checking because PropertyInfo::set won't do it for custom structures or array
2290                        if !check_value_type(&mut value, &orig_decl.property_type) {
2291                            return Err(SetPropertyError::WrongType);
2292                        }
2293                    }
2294                    unsafe {
2295                        let p = Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset));
2296                        return x
2297                            .prop
2298                            .set(p, value, maybe_animation.as_animation())
2299                            .map_err(|()| SetPropertyError::WrongType);
2300                    }
2301                } else if enclosing_component.description.original.is_global() {
2302                    return Err(SetPropertyError::NoSuchProperty);
2303                }
2304            };
2305            let item_info = &enclosing_component.description.items[element.borrow().id.as_str()];
2306            let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
2307            let p = &item_info.rtti.properties.get(name).ok_or(SetPropertyError::NoSuchProperty)?;
2308            p.set(item, value, maybe_animation.as_animation())
2309                .map_err(|()| SetPropertyError::WrongType)?;
2310        }
2311        ComponentInstance::GlobalComponent(glob) => {
2312            glob.as_ref().set_property(name, value)?;
2313        }
2314    }
2315    Ok(())
2316}
2317
2318/// Return true if the Value can be used for a property of the given type
2319fn check_value_type(value: &mut Value, ty: &Type) -> bool {
2320    match ty {
2321        Type::Void => true,
2322        Type::Invalid
2323        | Type::InferredProperty
2324        | Type::InferredCallback
2325        | Type::Callback { .. }
2326        | Type::Function { .. }
2327        | Type::ElementReference
2328        | Type::Closure => panic!("not valid property type"),
2329        Type::Float32 => matches!(value, Value::Number(_)),
2330        Type::Int32 => matches!(value, Value::Number(_)),
2331        Type::String => matches!(value, Value::String(_)),
2332        Type::Color => matches!(value, Value::Brush(_)),
2333        Type::UnitProduct(_)
2334        | Type::Duration
2335        | Type::PhysicalLength
2336        | Type::LogicalLength
2337        | Type::Rem
2338        | Type::Angle
2339        | Type::Percent => matches!(value, Value::Number(_)),
2340        Type::Image => matches!(value, Value::Image(_)),
2341        Type::Bool => matches!(value, Value::Bool(_)),
2342        Type::Model => {
2343            matches!(value, Value::Model(_) | Value::Bool(_) | Value::Number(_))
2344        }
2345        Type::PathData => matches!(value, Value::PathData(_)),
2346        Type::Easing => matches!(value, Value::EasingCurve(_)),
2347        Type::MouseCursor => matches!(value, Value::MouseCursorInner(_)),
2348        Type::Brush => matches!(value, Value::Brush(_)),
2349        Type::Array(inner) => {
2350            matches!(value, Value::Model(m) if m.iter().all(|mut v| check_value_type(&mut v, inner)))
2351        }
2352        Type::Struct(s) => {
2353            let Value::Struct(str) = value else { return false };
2354            if !str
2355                .0
2356                .iter_mut()
2357                .all(|(k, v)| s.fields.get(k).is_some_and(|ty| check_value_type(v, ty)))
2358            {
2359                return false;
2360            }
2361            for k in s.fields.keys() {
2362                str.0.entry(k.clone()).or_insert_with(|| default_value_for_struct_field(s, k));
2363            }
2364            true
2365        }
2366        Type::Enumeration(en) => {
2367            matches!(value, Value::EnumerationValue(name, _) if name == en.name.as_str())
2368        }
2369        Type::Keys => matches!(value, Value::Keys(_)),
2370        Type::LayoutCache => matches!(value, Value::LayoutCache(_)),
2371        Type::ArrayOfU16 => matches!(value, Value::ArrayOfU16(_)),
2372        Type::ComponentFactory => matches!(value, Value::ComponentFactory(_)),
2373        Type::StyledText => matches!(value, Value::StyledText(_)),
2374        Type::DataTransfer => matches!(value, Value::DataTransfer(_)),
2375    }
2376}
2377
2378pub(crate) fn invoke_callback(
2379    component_instance: &ComponentInstance,
2380    element: &ElementRc,
2381    callback_name: &SmolStr,
2382    args: &[Value],
2383) -> Option<Value> {
2384    generativity::make_guard!(guard);
2385    match enclosing_component_instance_for_element(element, component_instance, guard) {
2386        ComponentInstance::InstanceRef(enclosing_component) => {
2387            // Keep the component alive while the callback runs: the callback may close the popup
2388            // that owns this callback, and Callback::call() restores the handler after returning.
2389            let _component_guard = enclosing_component
2390                .self_weak()
2391                .get()
2392                .expect("component self weak must be initialized before invoking callbacks")
2393                .upgrade()
2394                .expect("component must be alive while invoking callbacks");
2395            let description = enclosing_component.description;
2396            let element = element.borrow();
2397            if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
2398            {
2399                if let Some(callback_offset) = description.custom_callbacks.get(callback_name) {
2400                    if let Some(tracker_offset) = description.callback_trackers.get(callback_name) {
2401                        tracker_offset.apply_pin(enclosing_component.instance).get();
2402                    }
2403                    let callback = callback_offset.apply(&*enclosing_component.instance);
2404                    let res = callback.call(args);
2405                    return Some(if res != Value::Void {
2406                        res
2407                    } else if let Some(Type::Callback(callback)) = description
2408                        .original
2409                        .root_element
2410                        .borrow()
2411                        .property_declarations
2412                        .get(callback_name)
2413                        .map(|d| &d.property_type)
2414                    {
2415                        // If the callback was not set, the return value will be Value::Void, but we need
2416                        // to make sure that the value is actually of the right type as returned by the
2417                        // callback, otherwise we will get panics later
2418                        default_value_for_type(&callback.return_type)
2419                    } else {
2420                        res
2421                    });
2422                } else if enclosing_component.description.original.is_global() {
2423                    return None;
2424                }
2425            };
2426            let item_info = &description.items[element.id.as_str()];
2427            let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
2428            item_info
2429                .rtti
2430                .callbacks
2431                .get(callback_name.as_str())
2432                .map(|callback| callback.call(item, args))
2433        }
2434        ComponentInstance::GlobalComponent(global) => {
2435            Some(global.as_ref().invoke_callback(callback_name, args).unwrap())
2436        }
2437    }
2438}
2439
2440pub(crate) fn set_callback_handler(
2441    component_instance: &ComponentInstance,
2442    element: &ElementRc,
2443    callback_name: &str,
2444    handler: CallbackHandler,
2445) -> Result<(), ()> {
2446    generativity::make_guard!(guard);
2447    match enclosing_component_instance_for_element(element, component_instance, guard) {
2448        ComponentInstance::InstanceRef(enclosing_component) => {
2449            let description = enclosing_component.description;
2450            let element = element.borrow();
2451            if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
2452            {
2453                if let Some(callback_offset) = description.custom_callbacks.get(callback_name) {
2454                    let callback = callback_offset.apply(&*enclosing_component.instance);
2455                    callback.set_handler(handler);
2456                    if let Some(tracker_offset) = description.callback_trackers.get(callback_name) {
2457                        tracker_offset.apply_pin(enclosing_component.instance).mark_dirty();
2458                    }
2459                    return Ok(());
2460                } else if enclosing_component.description.original.is_global() {
2461                    return Err(());
2462                }
2463            };
2464            let item_info = &description.items[element.id.as_str()];
2465            let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
2466            if let Some(callback) = item_info.rtti.callbacks.get(callback_name) {
2467                callback.set_handler(item, handler);
2468                Ok(())
2469            } else {
2470                Err(())
2471            }
2472        }
2473        ComponentInstance::GlobalComponent(global) => {
2474            global.as_ref().set_callback_handler(callback_name, handler)
2475        }
2476    }
2477}
2478
2479/// Invoke the function.
2480///
2481/// Return None if the function don't exist
2482pub(crate) fn call_function(
2483    component_instance: &ComponentInstance,
2484    element: &ElementRc,
2485    function_name: &str,
2486    args: Vec<Value>,
2487) -> Option<Value> {
2488    generativity::make_guard!(guard);
2489    match enclosing_component_instance_for_element(element, component_instance, guard) {
2490        ComponentInstance::InstanceRef(c) => {
2491            // Keep the component alive while the function runs: the function may close the popup
2492            // that owns this function or callbacks it invokes.
2493            let _component_guard = c
2494                .self_weak()
2495                .get()
2496                .expect("component self weak must be initialized before invoking functions")
2497                .upgrade()
2498                .expect("component must be alive while invoking functions");
2499            let mut ctx = EvalLocalContext::from_function_arguments(c, args);
2500            eval_expression(
2501                &element
2502                    .borrow()
2503                    .binding_cell_including_synthetic(function_name)?
2504                    .borrow()
2505                    .expression,
2506                &mut ctx,
2507            )
2508            .into()
2509        }
2510        ComponentInstance::GlobalComponent(g) => g.as_ref().eval_function(function_name, args).ok(),
2511    }
2512}
2513
2514/// Return the component instance which hold the given element.
2515/// Does not take in account the global component.
2516pub fn enclosing_component_for_element<'a, 'old_id, 'new_id>(
2517    element: &'a ElementRc,
2518    component: InstanceRef<'a, 'old_id>,
2519    _guard: generativity::Guard<'new_id>,
2520) -> InstanceRef<'a, 'new_id> {
2521    let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
2522    if Rc::ptr_eq(enclosing, &component.description.original) {
2523        // Safety: new_id is an unique id
2524        unsafe {
2525            std::mem::transmute::<InstanceRef<'a, 'old_id>, InstanceRef<'a, 'new_id>>(component)
2526        }
2527    } else {
2528        assert!(!enclosing.is_global());
2529        // Safety: this is the only place we use this 'static lifetime in this function and nothing is returned with it
2530        // For some reason we can't make a new guard here because the compiler thinks we are returning that
2531        // (it assumes that the 'id must outlive 'a , which is not true)
2532        let static_guard = unsafe { generativity::Guard::new(generativity::Id::<'static>::new()) };
2533
2534        let parent_instance = component
2535            .parent_instance(static_guard)
2536            .expect("accessing deleted parent (issue #6426)");
2537        enclosing_component_for_element(element, parent_instance, _guard)
2538    }
2539}
2540
2541/// Return the component instance which hold the given element.
2542/// The difference with enclosing_component_for_element is that it takes the GlobalComponent into account.
2543pub(crate) fn enclosing_component_instance_for_element<'a, 'new_id>(
2544    element: &'a ElementRc,
2545    component_instance: &ComponentInstance<'a, '_>,
2546    guard: generativity::Guard<'new_id>,
2547) -> ComponentInstance<'a, 'new_id> {
2548    let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
2549    match component_instance {
2550        ComponentInstance::InstanceRef(component) => {
2551            if enclosing.is_global() && !Rc::ptr_eq(enclosing, &component.description.original) {
2552                ComponentInstance::GlobalComponent(
2553                    component
2554                        .description
2555                        .extra_data_offset
2556                        .apply(component.instance.get_ref())
2557                        .globals
2558                        .get()
2559                        .unwrap()
2560                        .get(enclosing.root_element.borrow().id.as_str())
2561                        .unwrap(),
2562                )
2563            } else {
2564                ComponentInstance::InstanceRef(enclosing_component_for_element(
2565                    element, *component, guard,
2566                ))
2567            }
2568        }
2569        ComponentInstance::GlobalComponent(global) => {
2570            //assert!(Rc::ptr_eq(enclosing, &global.component));
2571            ComponentInstance::GlobalComponent(global.clone())
2572        }
2573    }
2574}
2575
2576/// Look up a binding by property name across the two binding containers the interpreter builds
2577/// structs from: an element's sealed [`Bindings`](i_slint_compiler::object_tree::Bindings) and a
2578/// `PathElement`'s raw binding map.
2579pub(crate) trait BindingLookup {
2580    fn lookup_binding(
2581        &self,
2582        name: &str,
2583    ) -> Option<&std::cell::RefCell<i_slint_compiler::expression_tree::BindingExpression>>;
2584}
2585impl BindingLookup for i_slint_compiler::object_tree::BindingsMap {
2586    fn lookup_binding(
2587        &self,
2588        name: &str,
2589    ) -> Option<&std::cell::RefCell<i_slint_compiler::expression_tree::BindingExpression>> {
2590        self.get(name)
2591    }
2592}
2593impl BindingLookup for i_slint_compiler::object_tree::Bindings {
2594    fn lookup_binding(
2595        &self,
2596        name: &str,
2597    ) -> Option<&std::cell::RefCell<i_slint_compiler::expression_tree::BindingExpression>> {
2598        self.binding_cell_including_synthetic(name)
2599    }
2600}
2601
2602pub fn new_struct_with_bindings<ElementType: 'static + Default + corelib::rtti::BuiltinItem>(
2603    bindings: &impl BindingLookup,
2604    local_context: &mut EvalLocalContext,
2605) -> ElementType {
2606    let mut element = ElementType::default();
2607    for (prop, info) in ElementType::fields::<Value>().into_iter() {
2608        if let Some(binding) = bindings.lookup_binding(prop) {
2609            let value = eval_expression(&binding.borrow(), local_context);
2610            info.set_field(&mut element, value).unwrap();
2611        }
2612    }
2613    element
2614}
2615
2616fn convert_from_lyon_path<'a>(
2617    events_it: impl IntoIterator<Item = &'a i_slint_compiler::expression_tree::Expression>,
2618    points_it: impl IntoIterator<Item = &'a i_slint_compiler::expression_tree::Expression>,
2619    local_context: &mut EvalLocalContext,
2620) -> PathData {
2621    let events = events_it
2622        .into_iter()
2623        .map(|event_expr| eval_expression(event_expr, local_context).try_into().unwrap())
2624        .collect::<SharedVector<_>>();
2625
2626    let points = points_it
2627        .into_iter()
2628        .map(|point_expr| {
2629            let point_value = eval_expression(point_expr, local_context);
2630            let point_struct: Struct = point_value.try_into().unwrap();
2631            let mut point = i_slint_core::graphics::Point::default();
2632            let x: f64 = point_struct.get_field("x").unwrap().clone().try_into().unwrap();
2633            let y: f64 = point_struct.get_field("y").unwrap().clone().try_into().unwrap();
2634            point.x = x as _;
2635            point.y = y as _;
2636            point
2637        })
2638        .collect::<SharedVector<_>>();
2639
2640    PathData::Events(events, points)
2641}
2642
2643pub fn convert_path(path: &ExprPath, local_context: &mut EvalLocalContext) -> PathData {
2644    match path {
2645        ExprPath::Elements(elements) => PathData::Elements(
2646            elements
2647                .iter()
2648                .map(|element| convert_path_element(element, local_context))
2649                .collect::<SharedVector<PathElement>>(),
2650        ),
2651        ExprPath::Events(events, points) => {
2652            convert_from_lyon_path(events.iter(), points.iter(), local_context)
2653        }
2654        ExprPath::Commands(commands) => {
2655            if let Value::String(commands) = eval_expression(commands, local_context) {
2656                PathData::Commands(commands)
2657            } else {
2658                panic!("binding to path commands does not evaluate to string");
2659            }
2660        }
2661    }
2662}
2663
2664fn convert_path_element(
2665    expr_element: &ExprPathElement,
2666    local_context: &mut EvalLocalContext,
2667) -> PathElement {
2668    match expr_element.element_type.native_class.class_name.as_str() {
2669        "MoveTo" => {
2670            PathElement::MoveTo(new_struct_with_bindings(&expr_element.bindings, local_context))
2671        }
2672        "LineTo" => {
2673            PathElement::LineTo(new_struct_with_bindings(&expr_element.bindings, local_context))
2674        }
2675        "ArcTo" => {
2676            PathElement::ArcTo(new_struct_with_bindings(&expr_element.bindings, local_context))
2677        }
2678        "CubicTo" => {
2679            PathElement::CubicTo(new_struct_with_bindings(&expr_element.bindings, local_context))
2680        }
2681        "QuadraticTo" => PathElement::QuadraticTo(new_struct_with_bindings(
2682            &expr_element.bindings,
2683            local_context,
2684        )),
2685        "Close" => PathElement::Close,
2686        _ => panic!(
2687            "Cannot create unsupported path element {}",
2688            expr_element.element_type.native_class.class_name
2689        ),
2690    }
2691}
2692
2693/// Create a value suitable as the default value of a given type
2694pub fn default_value_for_type(ty: &Type) -> Value {
2695    match ty {
2696        Type::Float32 | Type::Int32 => Value::Number(0.),
2697        Type::String => Value::String(Default::default()),
2698        Type::Color | Type::Brush => Value::Brush(Default::default()),
2699        Type::Duration | Type::Angle | Type::PhysicalLength | Type::LogicalLength | Type::Rem => {
2700            Value::Number(0.)
2701        }
2702        Type::Image => Value::Image(Default::default()),
2703        Type::Bool => Value::Bool(false),
2704        Type::Callback { .. } => Value::Void,
2705        Type::Struct(s) => Value::Struct(
2706            s.fields
2707                .keys()
2708                .map(|n| (n.to_string(), default_value_for_struct_field(s, n)))
2709                .collect::<Struct>(),
2710        ),
2711        Type::Array(_) | Type::Model => Value::Model(Default::default()),
2712        Type::Percent => Value::Number(0.),
2713        Type::Enumeration(e) => Value::EnumerationValue(
2714            e.name.to_string(),
2715            e.values.get(e.default_value).unwrap().to_string(),
2716        ),
2717        Type::Keys => Value::Keys(Default::default()),
2718        Type::DataTransfer => Value::DataTransfer(Default::default()),
2719        Type::Easing => Value::EasingCurve(Default::default()),
2720        Type::MouseCursor => Value::MouseCursorInner(Default::default()),
2721        Type::Void | Type::Invalid => Value::Void,
2722        Type::UnitProduct(_) => Value::Number(0.),
2723        Type::PathData => Value::PathData(Default::default()),
2724        Type::LayoutCache => Value::LayoutCache(Default::default()),
2725        Type::ArrayOfU16 => Value::ArrayOfU16(Default::default()),
2726        Type::ComponentFactory => Value::ComponentFactory(Default::default()),
2727        Type::InferredProperty
2728        | Type::InferredCallback
2729        | Type::ElementReference
2730        | Type::Function { .. }
2731        | Type::Closure => {
2732            panic!("There can't be such property")
2733        }
2734        Type::StyledText => Value::StyledText(Default::default()),
2735    }
2736}
2737
2738/// Create a value for the default of a struct field:
2739/// the user-declared default value (`struct Foo { bar: int = 42 }`) if there is one,
2740/// otherwise the default value for the field's type.
2741pub fn default_value_for_struct_field(
2742    s: &i_slint_compiler::langtype::Struct,
2743    field_name: &str,
2744) -> Value {
2745    match s.field_defaults.get(field_name) {
2746        Some(expr) => eval_constant_expression(expr),
2747        None => default_value_for_type(
2748            s.fields.get(field_name).expect("default value requested for unknown struct field"),
2749        ),
2750    }
2751}
2752
2753/// Convert a value to the given type, as [`Expression::Cast`] does
2754fn cast_value(value: Value, to: &Type) -> Value {
2755    match (value, to) {
2756        (Value::Number(n), Type::Int32) => Value::Number(n.trunc()),
2757        (Value::Number(n), Type::String) => {
2758            Value::String(i_slint_core::string::shared_string_from_number(n))
2759        }
2760        (Value::Number(n), Type::Color) => Color::from_argb_encoded(n as u32).into(),
2761        (Value::Brush(brush), Type::Color) => brush.color().into(),
2762        (Value::EnumerationValue(_, val), Type::String) => Value::String(val.into()),
2763        (v, _) => v,
2764    }
2765}
2766
2767/// Apply a unary operator to a value; returns the unmodified value as the error
2768/// for unsupported combinations
2769fn eval_unary_op(sub: Value, op: char) -> Result<Value, Value> {
2770    match (sub, op) {
2771        (Value::Number(a), '+') => Ok(Value::Number(a)),
2772        (Value::Number(a), '-') => Ok(Value::Number(-a)),
2773        (Value::Bool(a), '!') => Ok(Value::Bool(!a)),
2774        (sub, _) => Err(sub),
2775    }
2776}
2777
2778/// Evaluate a constant expression as stored in [`i_slint_compiler::langtype::Struct::field_defaults`],
2779/// which needs no evaluation context.
2780/// Mirrors [`eval_expression`] for the corresponding expressions.
2781fn eval_constant_expression(expr: &ConstantExpression) -> Value {
2782    match expr {
2783        ConstantExpression::StringLiteral(s) => Value::String(s.as_str().into()),
2784        ConstantExpression::NumberLiteral(n, _unit) => Value::Number(*n),
2785        ConstantExpression::BoolLiteral(b) => Value::Bool(*b),
2786        ConstantExpression::EnumerationValue(value) => {
2787            Value::EnumerationValue(value.enumeration.name.to_string(), value.to_string())
2788        }
2789        ConstantExpression::Cast { from, to } => cast_value(eval_constant_expression(from), to),
2790        ConstantExpression::UnaryOp { sub, op } => {
2791            // The resolver only accepts the unary operators on matching operand types
2792            eval_unary_op(eval_constant_expression(sub), *op)
2793                .unwrap_or_else(|sub| panic!("unsupported {op} {sub:?}"))
2794        }
2795        ConstantExpression::Struct { values, .. } => Value::Struct(
2796            values
2797                .iter()
2798                .map(|(k, v)| (k.to_string(), eval_constant_expression(v)))
2799                .collect::<Struct>(),
2800        ),
2801        ConstantExpression::Array { values, .. } => {
2802            Value::Model(ModelRc::new(corelib::model::SharedVectorModel::from(
2803                values.iter().map(eval_constant_expression).collect::<SharedVector<_>>(),
2804            )))
2805        }
2806    }
2807}
2808
2809fn menu_item_tree_properties(
2810    context_menu_item_tree: vtable::VRc<i_slint_core::menus::MenuVTable, MenuFromItemTree>,
2811) -> (Box<dyn Fn() -> Value>, CallbackHandler, CallbackHandler) {
2812    let context_menu_item_tree_ = context_menu_item_tree.clone();
2813    let entries = Box::new(move || {
2814        let mut entries = SharedVector::default();
2815        context_menu_item_tree_.sub_menu(None, &mut entries);
2816        Value::Model(ModelRc::new(VecModel::from(
2817            entries.into_iter().map(Value::from).collect::<Vec<_>>(),
2818        )))
2819    });
2820    let context_menu_item_tree_ = context_menu_item_tree.clone();
2821    let sub_menu = Box::new(move |args: &[Value]| -> Value {
2822        let mut entries = SharedVector::default();
2823        context_menu_item_tree_.sub_menu(Some(&args[0].clone().try_into().unwrap()), &mut entries);
2824        Value::Model(ModelRc::new(VecModel::from(
2825            entries.into_iter().map(Value::from).collect::<Vec<_>>(),
2826        )))
2827    });
2828    let activated = Box::new(move |args: &[Value]| -> Value {
2829        context_menu_item_tree.activate(&args[0].clone().try_into().unwrap());
2830        Value::Void
2831    });
2832    (entries, sub_menu, activated)
2833}