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