slint_interpreter/
eval.rs

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