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