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