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