1use crate::Value;
11use crate::globals::{GlobalInstance, GlobalStorage};
12use crate::instance::SubComponentInstance;
13use i_slint_compiler::diagnostics::SourceLocation;
14use i_slint_compiler::expression_tree::{BuiltinFunction, MinMaxOp};
15use i_slint_compiler::langtype::{ConstantExpression, Type};
16use i_slint_compiler::llr::{self, Expression, LocalMemberIndex, MemberReference};
17use i_slint_core::graphics::{
18 Brush, ConicGradientBrush, GradientStop, LinearGradientBrush, RadialGradientBrush,
19};
20use i_slint_core::model::{Model, ModelExt, ModelRc, SharedVectorModel};
21use i_slint_core::{Color, SharedString, SharedVector};
22use smol_str::SmolStr;
23use std::collections::HashMap;
24use std::pin::Pin;
25use std::rc::{Rc, Weak};
26
27pub struct EvalContext {
29 pub current: Option<Pin<Rc<SubComponentInstance>>>,
32 pub compilation_unit: Rc<llr::CompilationUnit>,
35 pub globals: Weak<GlobalStorage>,
37 pub locals: HashMap<SmolStr, Value>,
39 pub function_arguments: Vec<Value>,
41 pub function_arg_types: Vec<Type>,
44 pub return_value: Option<Value>,
46}
47
48impl EvalContext {
49 pub fn new(current: Pin<Rc<SubComponentInstance>>) -> Self {
52 let globals = current
53 .root
54 .get()
55 .and_then(|w| w.upgrade())
56 .map(|inst| Rc::downgrade(&inst.globals))
57 .unwrap_or_default();
58 Self {
59 compilation_unit: current.compilation_unit.clone(),
60 current: Some(current),
61 globals,
62 locals: HashMap::new(),
63 function_arguments: Vec::new(),
64 function_arg_types: Vec::new(),
65 return_value: None,
66 }
67 }
68
69 pub fn for_global(globals: Weak<GlobalStorage>, cu: Rc<llr::CompilationUnit>) -> Self {
71 Self {
72 current: None,
73 compilation_unit: cu,
74 globals,
75 locals: HashMap::new(),
76 function_arguments: Vec::new(),
77 function_arg_types: Vec::new(),
78 return_value: None,
79 }
80 }
81
82 pub fn with_arguments(current: Pin<Rc<SubComponentInstance>>, args: Vec<Value>) -> Self {
83 let mut ctx = Self::new(current);
84 ctx.function_arguments = args;
85 ctx
86 }
87}
88
89fn root_instance(
92 ctx: &EvalContext,
93) -> Option<vtable::VRc<i_slint_core::item_tree::ItemTreeVTable, crate::instance::Instance>> {
94 match ctx.current.as_ref() {
95 Some(c) => c.root.get()?.upgrade(),
96 None => ctx.globals.upgrade()?.root.get()?.upgrade(),
97 }
98}
99
100pub(crate) fn try_walk_parent(
106 start: &Pin<Rc<SubComponentInstance>>,
107 level: usize,
108) -> Option<Pin<Rc<SubComponentInstance>>> {
109 let mut current = start.clone();
110 for _ in 0..level {
111 current = Pin::new(current.parent.upgrade()?);
112 }
113 Some(current)
114}
115
116pub(crate) fn walk_parent(
120 start: &Pin<Rc<SubComponentInstance>>,
121 level: usize,
122) -> Pin<Rc<SubComponentInstance>> {
123 try_walk_parent(start, level).expect("parent vanished during evaluation")
124}
125
126impl i_slint_compiler::llr::TypeResolutionContext for EvalContext {
127 fn property_ty(&self, mr: &MemberReference) -> &Type {
128 let cu = &self.compilation_unit;
129 match mr {
130 MemberReference::Global { global_index, member } => {
131 let g = &cu.globals[*global_index];
132 match member {
133 LocalMemberIndex::Property(idx) => &g.properties[*idx].ty,
134 LocalMemberIndex::Function(idx) => &g.functions[*idx].ret_ty,
135 LocalMemberIndex::Callback(idx) => &g.callbacks[*idx].ty,
138 LocalMemberIndex::Native { .. } | LocalMemberIndex::Timer(_) => &Type::Invalid,
139 }
140 }
141 MemberReference::Relative { parent_level, local_reference } => {
142 let current =
143 self.current.as_ref().expect("property_ty needs a sub-component context");
144 let Some(sub) = try_walk_parent(current, *parent_level) else {
148 return &Type::Invalid;
149 };
150 let mut sc_idx = sub.sub_component_idx;
151 for i in &local_reference.sub_component_path {
152 sc_idx = cu.sub_components[sc_idx].sub_components[*i].ty;
153 }
154 let sc = &cu.sub_components[sc_idx];
155 match &local_reference.reference {
156 LocalMemberIndex::Property(idx) => &sc.properties[*idx].ty,
157 LocalMemberIndex::Function(idx) => &sc.functions[*idx].ret_ty,
158 LocalMemberIndex::Callback(idx) => &sc.callbacks[*idx].ty,
159 LocalMemberIndex::Timer(_) => &Type::Invalid,
161 LocalMemberIndex::Native { item_index, prop_name, .. } => {
162 if prop_name == "elements" {
163 return &Type::PathData;
165 }
166 sc.items[*item_index]
167 .ty
168 .lookup_property(prop_name)
169 .unwrap_or(&Type::Invalid)
170 }
171 }
172 }
173 }
174 }
175
176 fn arg_type(&self, index: usize) -> &Type {
177 self.function_arg_types.get(index).unwrap_or(&Type::Invalid)
178 }
179}
180
181pub(crate) fn walk_sub_path(
183 mut current: Pin<Rc<SubComponentInstance>>,
184 path: &[llr::SubComponentInstanceIdx],
185) -> Pin<Rc<SubComponentInstance>> {
186 for &idx in path {
187 let next = current.sub_components[idx].clone();
188 current = next;
189 }
190 current
191}
192
193pub(crate) fn try_walk_to(
197 ctx: &EvalContext,
198 parent_level: usize,
199 local_reference: &llr::LocalMemberReference,
200) -> Option<Pin<Rc<SubComponentInstance>>> {
201 let base = try_walk_parent(ctx.current.as_ref()?, parent_level)?;
202 Some(walk_sub_path(base, &local_reference.sub_component_path))
203}
204
205pub(crate) fn find_flat_item_index(
207 item_table: &[Option<(
208 Box<[i_slint_compiler::llr::SubComponentInstanceIdx]>,
209 i_slint_compiler::llr::ItemInstanceIdx,
210 )>],
211 path: &[i_slint_compiler::llr::SubComponentInstanceIdx],
212 item_index: i_slint_compiler::llr::ItemInstanceIdx,
213) -> Option<usize> {
214 item_table.iter().position(|entry| {
215 entry.as_ref().is_some_and(|(p, i)| p.as_ref() == path && *i == item_index)
216 })
217}
218
219fn load_local(instance: &SubComponentInstance, member: &LocalMemberIndex) -> Value {
220 match member {
221 LocalMemberIndex::Property(idx) => Pin::as_ref(&instance.properties[*idx]).get(),
222 LocalMemberIndex::Native { item_index, prop_name, .. } => {
223 Pin::as_ref(&instance.items[*item_index]).get_property(prop_name).unwrap_or(Value::Void)
224 }
225 LocalMemberIndex::Callback(_)
226 | LocalMemberIndex::Function(_)
227 | LocalMemberIndex::Timer(_) => {
228 panic!("load_local called on callback/function/timer reference")
229 }
230 }
231}
232
233fn eval_array_row_predicate(
239 arg_name: &SmolStr,
240 predicate: &Expression,
241 ctx: &mut EvalContext,
242 row_value: Value,
243) -> bool {
244 let previous = ctx.locals.insert(arg_name.clone(), row_value);
245 let result = eval_expression(ctx, predicate).try_into().unwrap();
246 match previous {
247 Some(prev) => {
248 ctx.locals.insert(arg_name.clone(), prev);
249 }
250 None => {
251 ctx.locals.remove(arg_name);
252 }
253 }
254 result
255}
256
257fn set_maybe_animated(
259 prop: Pin<&i_slint_core::Property<Value>>,
260 ty: &Type,
261 value: Value,
262 animation: Option<i_slint_core::items::PropertyAnimation>,
263) {
264 match animation {
265 Some(anim) => match crate::bindings::animated_value_map(ty) {
266 Some(map) => prop.set_animated_value_with_map(value, anim, map),
267 None => prop.set_animated_value(value, anim),
268 },
269 None => prop.set(value),
270 }
271}
272
273fn store_local(
274 instance: &SubComponentInstance,
275 member: &LocalMemberIndex,
276 value: Value,
277 animation: Option<i_slint_core::items::PropertyAnimation>,
278) {
279 match member {
280 LocalMemberIndex::Property(idx) => {
281 let sc = &instance.compilation_unit.sub_components[instance.sub_component_idx];
282 set_maybe_animated(
283 Pin::as_ref(&instance.properties[*idx]),
284 &sc.properties[*idx].ty,
285 value,
286 animation,
287 );
288 }
289 LocalMemberIndex::Native { item_index, prop_name, .. } => {
290 let _ =
291 Pin::as_ref(&instance.items[*item_index]).set_property(prop_name, value, animation);
292 }
293 LocalMemberIndex::Callback(_)
294 | LocalMemberIndex::Function(_)
295 | LocalMemberIndex::Timer(_) => {
296 panic!("store_local called on callback/function/timer reference")
297 }
298 }
299}
300
301fn walk_to_target_with_animation(
308 start: Pin<Rc<SubComponentInstance>>,
309 local_reference: &llr::LocalMemberReference,
310) -> (Pin<Rc<SubComponentInstance>>, Option<i_slint_core::items::PropertyAnimation>) {
311 let cu = start.compilation_unit.clone();
312 let path = &local_reference.sub_component_path;
313 let mut animation = None;
314 let mut owner = start;
315 for depth in 0..=path.len() {
316 if animation.is_none() {
317 let sc = &cu.sub_components[owner.sub_component_idx];
318 if !sc.animations.is_empty() {
319 let key = llr::LocalMemberReference {
320 sub_component_path: path[depth..].to_vec(),
321 reference: local_reference.reference.clone(),
322 };
323 if let Some(expr) = sc.animations.get(&key) {
324 animation = Some((owner.clone(), expr.clone()));
325 }
326 }
327 }
328 if let Some(&idx) = path.get(depth) {
329 let next = owner.sub_components[idx].clone();
330 owner = next;
331 }
332 }
333 let animation = animation.map(|(scope, expr)| {
334 let mut ctx = EvalContext::new(scope);
335 crate::bindings::value_to_property_animation(eval_expression(&mut ctx, &expr))
336 });
337 (owner, animation)
338}
339
340pub fn load_property(ctx: &EvalContext, mr: &MemberReference) -> Value {
341 match mr {
342 MemberReference::Global { global_index, member } => {
343 let Some(storage) = ctx.globals.upgrade() else { return Value::Void };
344 let Some(global) = storage.get(*global_index) else { return Value::Void };
345 load_global(global, member)
346 }
347 MemberReference::Relative { parent_level, local_reference } => {
348 let Some(instance) = try_walk_to(ctx, *parent_level, local_reference) else {
349 return Value::Void;
350 };
351 load_local(&instance, &local_reference.reference)
352 }
353 }
354}
355
356pub fn store_property(ctx: &EvalContext, mr: &MemberReference, value: Value) {
357 match mr {
358 MemberReference::Global { global_index, member } => {
359 let Some(storage) = ctx.globals.upgrade() else { return };
360 let Some(global) = storage.get(*global_index) else { return };
361 store_global(global, member, value);
362 }
363 MemberReference::Relative { parent_level, local_reference } => {
364 let Some(base) =
365 ctx.current.as_ref().and_then(|start| try_walk_parent(start, *parent_level))
366 else {
367 return;
368 };
369 let (instance, animation) = walk_to_target_with_animation(base, local_reference);
370 store_local(&instance, &local_reference.reference, value, animation);
371 }
372 }
373}
374
375pub fn invoke_callback(ctx: &EvalContext, mr: &MemberReference, args: &[Value]) -> Value {
376 match mr {
377 MemberReference::Global { global_index, member } => {
378 let Some(storage) = ctx.globals.upgrade() else { return Value::Void };
379 let Some(global) = storage.get(*global_index) else { return Value::Void };
380 let LocalMemberIndex::Callback(idx) = member else {
381 panic!("invoke_callback on non-callback global reference")
382 };
383 let cb = &global.compilation_unit.globals[global.global_idx].callbacks[*idx];
384 if let Some(native) = &global.native {
385 let res = native.as_ref().invoke_callback(&cb.name, args).unwrap_or(Value::Void);
386 return ensure_typed_default(res, &cb.ret_ty);
387 }
388 if let Some(tracker) = global.callback_trackers[*idx].as_ref() {
391 Pin::as_ref(tracker).get();
392 }
393 let res = Pin::as_ref(&global.callbacks[*idx]).call(args);
394 ensure_typed_default(res, &cb.ret_ty)
395 }
396 MemberReference::Relative { parent_level, local_reference } => {
397 let Some(instance) = try_walk_to(ctx, *parent_level, local_reference) else {
398 return Value::Void;
399 };
400 match &local_reference.reference {
401 LocalMemberIndex::Callback(idx) => {
402 if let Some(tracker) = instance.callback_trackers[*idx].as_ref() {
406 Pin::as_ref(tracker).get();
407 }
408 let res = Pin::as_ref(&instance.callbacks[*idx]).call(args);
409 let ret_ty = instance.compilation_unit.sub_components
410 [instance.sub_component_idx]
411 .callbacks[*idx]
412 .ret_ty
413 .clone();
414 ensure_typed_default(res, &ret_ty)
415 }
416 LocalMemberIndex::Native { item_index, prop_name, .. } => {
417 Pin::as_ref(&instance.items[*item_index])
418 .call_callback(prop_name, args)
419 .unwrap_or(Value::Void)
420 }
421 _ => panic!("invoke_callback on non-callback reference: {mr:?}"),
422 }
423 }
424 }
425}
426
427pub(crate) fn ensure_typed_default(value: Value, ret_ty: &Type) -> Value {
430 if matches!(value, Value::Void) { default_value_for_type(ret_ty) } else { value }
431}
432
433pub fn invoke_function(ctx: &EvalContext, mr: &MemberReference, args: Vec<Value>) -> Value {
434 match mr {
435 MemberReference::Global { global_index, member } => {
436 let Some(storage) = ctx.globals.upgrade() else { return Value::Void };
437 let Some(global) = storage.get(*global_index) else { return Value::Void };
438 let LocalMemberIndex::Function(idx) = member else {
439 panic!("invoke_function on non-function global reference")
440 };
441 let function = &global.compilation_unit.globals[global.global_idx].functions[*idx];
442 let code = function.code.borrow().clone();
443 let mut inner_ctx =
444 EvalContext::for_global(ctx.globals.clone(), global.compilation_unit.clone());
445 inner_ctx.function_arg_types = function.args.clone();
446 inner_ctx.function_arguments = args;
447 eval_expression(&mut inner_ctx, &code)
448 }
449 MemberReference::Relative { parent_level, local_reference } => {
450 let Some(instance) = try_walk_to(ctx, *parent_level, local_reference) else {
451 return Value::Void;
452 };
453 let LocalMemberIndex::Function(idx) = &local_reference.reference else {
454 panic!("invoke_function on non-function reference")
455 };
456 let sc = &instance.compilation_unit.sub_components[instance.sub_component_idx];
457 let function = &sc.functions[*idx];
458 let code = function.code.borrow().clone();
459 let mut inner_ctx = EvalContext::with_arguments(instance.clone(), args);
460 inner_ctx.function_arg_types = function.args.clone();
461 eval_expression(&mut inner_ctx, &code)
462 }
463 }
464}
465
466fn load_global(global: &Rc<GlobalInstance>, member: &LocalMemberIndex) -> Value {
467 match member {
468 LocalMemberIndex::Property(idx) => {
469 if let Some(native) = &global.native {
470 let g = &global.compilation_unit.globals[global.global_idx];
471 return native
472 .as_ref()
473 .get_property(&g.properties[*idx].name)
474 .unwrap_or(Value::Void);
475 }
476 Pin::as_ref(&global.properties[*idx]).get()
477 }
478 _ => panic!("load_global called on non-property"),
479 }
480}
481
482pub(crate) fn store_global(global: &Rc<GlobalInstance>, member: &LocalMemberIndex, value: Value) {
483 if let LocalMemberIndex::Property(idx) = member {
484 let g = &global.compilation_unit.globals[global.global_idx];
485 if let Some(native) = &global.native {
487 let _ = native.as_ref().set_property(&g.properties[*idx].name, value, None);
488 return;
489 }
490 set_maybe_animated(
491 Pin::as_ref(&global.properties[*idx]),
492 &g.properties[*idx].ty,
493 value,
494 None,
495 );
496 }
497}
498
499fn cast_to_path_data(ctx: &mut EvalContext, from: &Expression) -> Value {
509 use i_slint_core::graphics::PathData;
510 use i_slint_core::items::PathEvent;
511
512 match from {
513 Expression::Array { values, .. } => {
514 let elements: SharedVector<i_slint_core::graphics::PathElement> =
515 values.iter().filter_map(|e| path_element_from_expression(ctx, e)).collect();
516 Value::PathData(PathData::Elements(elements))
517 }
518 Expression::Struct { values, .. }
519 if let Some(events) = values.get("events")
520 && let Some(points) = values.get("points") =>
521 {
522 let events_value = eval_expression(ctx, events);
523 let points_value = eval_expression(ctx, points);
524 let events: SharedVector<PathEvent> = match events_value {
529 Value::Model(m) => {
530 (0..m.row_count()).filter_map(|i| m.row_data(i)?.try_into().ok()).collect()
531 }
532 _ => SharedVector::default(),
533 };
534 let points: SharedVector<lyon_path::math::Point> = match points_value {
535 Value::Model(m) => {
536 (0..m.row_count()).filter_map(|i| m.row_data(i)?.try_into().ok()).collect()
537 }
538 _ => SharedVector::default(),
539 };
540 Value::PathData(PathData::Events(events, points))
541 }
542 _ => match eval_expression(ctx, from) {
543 Value::String(s) => Value::PathData(PathData::Commands(s)),
544 _ => Value::PathData(PathData::None),
545 },
546 }
547}
548
549fn path_element_from_expression(
553 ctx: &mut EvalContext,
554 expr: &Expression,
555) -> Option<i_slint_core::graphics::PathElement> {
556 use i_slint_compiler::langtype::{BuiltinStruct, StructName};
557 use i_slint_core::graphics::{
558 PathArcTo, PathCubicTo, PathElement, PathLineTo, PathMoveTo, PathQuadraticTo,
559 };
560 let Expression::Struct { ty, values } = expr else { return None };
561 let StructName::Builtin(bs) = &ty.name else { return None };
562 let get_f32 = |field: &str, ctx: &mut EvalContext| -> f32 {
563 values
564 .get(field)
565 .map(|e| eval_expression(ctx, e))
566 .and_then(|v| f64::try_from(v).ok())
567 .unwrap_or(0.0) as f32
568 };
569 let get_bool = |field: &str, ctx: &mut EvalContext| -> bool {
570 values
571 .get(field)
572 .map(|e| eval_expression(ctx, e))
573 .map(|v| matches!(v, Value::Bool(true)))
574 .unwrap_or(false)
575 };
576 Some(match bs {
577 BuiltinStruct::PathMoveTo => {
578 PathElement::MoveTo(PathMoveTo { x: get_f32("x", ctx), y: get_f32("y", ctx) })
579 }
580 BuiltinStruct::PathLineTo => {
581 PathElement::LineTo(PathLineTo { x: get_f32("x", ctx), y: get_f32("y", ctx) })
582 }
583 BuiltinStruct::PathArcTo => PathElement::ArcTo(PathArcTo {
584 x: get_f32("x", ctx),
585 y: get_f32("y", ctx),
586 radius_x: get_f32("radius-x", ctx),
587 radius_y: get_f32("radius-y", ctx),
588 x_rotation: get_f32("x-rotation", ctx),
589 large_arc: get_bool("large-arc", ctx),
590 sweep: get_bool("sweep", ctx),
591 }),
592 BuiltinStruct::PathCubicTo => PathElement::CubicTo(PathCubicTo {
593 x: get_f32("x", ctx),
594 y: get_f32("y", ctx),
595 control_1_x: get_f32("control-1-x", ctx),
596 control_1_y: get_f32("control-1-y", ctx),
597 control_2_x: get_f32("control-2-x", ctx),
598 control_2_y: get_f32("control-2-y", ctx),
599 }),
600 BuiltinStruct::PathQuadraticTo => PathElement::QuadraticTo(PathQuadraticTo {
601 x: get_f32("x", ctx),
602 y: get_f32("y", ctx),
603 control_x: get_f32("control-x", ctx),
604 control_y: get_f32("control-y", ctx),
605 }),
606 BuiltinStruct::PathClose => PathElement::Close,
607 _ => return None,
608 })
609}
610
611pub fn default_value_for_type(ty: &Type) -> Value {
614 match ty {
615 Type::Float32
616 | Type::Int32
617 | Type::Duration
618 | Type::Angle
619 | Type::PhysicalLength
620 | Type::LogicalLength
621 | Type::Rem
622 | Type::Percent
623 | Type::UnitProduct(_) => Value::Number(0.),
624 Type::String => Value::String(Default::default()),
625 Type::Color | Type::Brush => Value::Brush(Brush::default()),
626 Type::Bool => Value::Bool(false),
627 Type::Image => Value::Image(Default::default()),
628 Type::Struct(s) => Value::Struct(
629 s.fields
630 .keys()
631 .map(|k| (k.to_string(), default_value_for_struct_field(s, k)))
632 .collect(),
633 ),
634 Type::Array(_) | Type::Model => Value::Model(ModelRc::default()),
635 Type::Keys => Value::Keys(Default::default()),
636 Type::DataTransfer => Value::DataTransfer(Default::default()),
637 Type::StyledText => Value::StyledText(Default::default()),
638 Type::Enumeration(en) => {
639 let default = en.clone().default_value();
640 Value::EnumerationValue(en.name.to_string(), default.to_string())
641 }
642 Type::ComponentFactory => Value::ComponentFactory(Default::default()),
643 Type::MouseCursor => Value::MouseCursorInner(Default::default()),
644 Type::Void => Value::Void,
645 Type::Invalid
648 | Type::InferredProperty
649 | Type::InferredCallback
650 | Type::Callback(_)
651 | Type::Function(_)
652 | Type::PathData
653 | Type::Easing
654 | Type::ElementReference
655 | Type::ArrayOfU16
656 | Type::LayoutCache
657 | Type::Closure => Value::Void,
658 }
659}
660
661pub fn default_value_for_struct_field(
665 s: &i_slint_compiler::langtype::Struct,
666 field_name: &str,
667) -> Value {
668 match s.field_defaults.get(field_name) {
669 Some(expr) => eval_constant_expression(expr),
670 None => default_value_for_type(
671 s.fields.get(field_name).expect("default value requested for unknown struct field"),
672 ),
673 }
674}
675
676pub(crate) fn fill_missing_struct_fields(
677 value: &mut crate::Struct,
678 ty: &i_slint_compiler::langtype::Struct,
679) {
680 for k in ty.fields.keys() {
681 value.0.entry(k.clone()).or_insert_with(|| default_value_for_struct_field(ty, k));
682 }
683}
684
685fn eval_constant_expression(expr: &ConstantExpression) -> Value {
688 match expr {
689 ConstantExpression::StringLiteral(s) => Value::String(s.as_str().into()),
690 ConstantExpression::NumberLiteral(n, _unit) => Value::Number(*n),
691 ConstantExpression::BoolLiteral(b) => Value::Bool(*b),
692 ConstantExpression::EnumerationValue(value) => {
693 Value::EnumerationValue(value.enumeration.name.to_string(), value.to_string())
694 }
695 ConstantExpression::Cast { from, to } => {
696 cast_constant_value(eval_constant_expression(from), to)
697 }
698 ConstantExpression::UnaryOp { sub, op } => {
699 match (eval_constant_expression(sub), op) {
701 (Value::Number(a), '+') => Value::Number(a),
702 (Value::Number(a), '-') => Value::Number(-a),
703 (Value::Bool(a), '!') => Value::Bool(!a),
704 (sub, _) => panic!("unsupported {op} {sub:?}"),
705 }
706 }
707 ConstantExpression::Struct { values, .. } => Value::Struct(
708 values
709 .iter()
710 .map(|(k, v)| (k.to_string(), eval_constant_expression(v)))
711 .collect::<crate::api::Struct>(),
712 ),
713 ConstantExpression::Array { values, .. } => {
714 Value::Model(ModelRc::new(SharedVectorModel::from(
715 values.iter().map(eval_constant_expression).collect::<SharedVector<_>>(),
716 )))
717 }
718 }
719}
720
721fn cast_constant_value(value: Value, to: &Type) -> Value {
723 match (value, to) {
724 (Value::Number(n), Type::Int32) => Value::Number(n.trunc()),
725 (Value::Number(n), Type::String) => {
726 Value::String(i_slint_core::string::shared_string_from_number(n))
727 }
728 (Value::Number(n), Type::Color) => Color::from_argb_encoded(n as u32).into(),
729 (Value::Brush(brush), Type::Color) => brush.color().into(),
730 (Value::EnumerationValue(_, val), Type::String) => Value::String(val.into()),
731 (v, _) => v,
732 }
733}
734
735pub fn eval_expression(ctx: &mut EvalContext, expression: &Expression) -> Value {
736 if let Some(r) = &ctx.return_value {
737 return r.clone();
738 }
739 match expression {
740 Expression::StringLiteral(s) => Value::String(s.as_str().into()),
741 Expression::NumberLiteral(n) => Value::Number(*n),
742 Expression::BoolLiteral(b) => Value::Bool(*b),
743 Expression::KeysLiteral(ks) => Value::Keys({
744 let mut modifiers = i_slint_core::input::KeyboardModifiers::default();
745 modifiers.alt = ks.modifiers.alt;
746 modifiers.control = ks.modifiers.control;
747 modifiers.shift = ks.modifiers.shift;
748 modifiers.meta = ks.modifiers.meta;
749 i_slint_core::input::make_keys(
750 SharedString::from(&*ks.key),
751 modifiers,
752 ks.ignore_shift,
753 ks.ignore_alt,
754 )
755 }),
756 Expression::PropertyReference(mr) => load_property(ctx, mr),
757 Expression::FunctionParameterReference { index } => ctx.function_arguments[*index].clone(),
758 Expression::StoreLocalVariable { name, value } => {
759 let v = eval_expression(ctx, value);
760 ctx.locals.insert(name.clone(), v);
761 Value::Void
762 }
763 Expression::ReadLocalVariable { name, .. } => {
764 ctx.locals.get(name).cloned().unwrap_or(Value::Void)
765 }
766 Expression::StructFieldAccess { base, name } => {
767 if let Value::Struct(s) = eval_expression(ctx, base)
768 && let Some(v) = s.get_field(name)
769 && !matches!(v, Value::Void)
770 {
771 return v.clone();
772 }
773 match base.ty(&*ctx) {
775 Type::Struct(s) if s.fields.contains_key(name) => {
776 default_value_for_struct_field(&s, name)
777 }
778 _ => Value::Void,
779 }
780 }
781 Expression::ArrayIndex { array, index } => {
782 let array_v = eval_expression(ctx, array);
783 let index = eval_expression(ctx, index);
784 match (array_v, index) {
785 (Value::Model(m), Value::Number(i)) => {
786 let idx = i as isize as usize;
787 m.row_data_tracked(idx).unwrap_or_else(|| {
788 default_value_for_type(&expression.ty(&*ctx))
791 })
792 }
793 _ => Value::Void,
794 }
795 }
796 Expression::Cast { from, to } => {
797 if matches!(to, Type::PathData) {
801 return cast_to_path_data(ctx, from);
802 }
803 let v = eval_expression(ctx, from);
804 match (v, to) {
805 (Value::Number(n), Type::Int32) => Value::Number(n.trunc()),
806 (Value::Number(n), Type::String) => {
807 Value::String(i_slint_core::string::shared_string_from_number(n))
808 }
809 (Value::Number(n), Type::Color) => Color::from_argb_encoded(n as u32).into(),
810 (Value::Brush(brush), Type::Color) => brush.color().into(),
811 (Value::EnumerationValue(_, val), Type::String) => Value::String(val.into()),
812 (v, _) => v,
813 }
814 }
815 Expression::CodeBlock(sub) => {
816 let mut v = Value::Void;
817 for e in sub {
818 v = eval_expression(ctx, e);
819 if let Some(r) = &ctx.return_value {
820 return r.clone();
821 }
822 }
823 v
824 }
825 Expression::BuiltinFunctionCall { function, arguments, source_location } => {
826 call_builtin_function(ctx, function.clone(), arguments, source_location)
827 }
828 Expression::CallBackCall { callback, arguments } => {
829 let args: Vec<Value> = arguments.iter().map(|e| eval_expression(ctx, e)).collect();
830 invoke_callback(ctx, callback, &args)
831 }
832 Expression::FunctionCall { function, arguments } => {
833 let args: Vec<Value> = arguments.iter().map(|e| eval_expression(ctx, e)).collect();
834 invoke_function(ctx, function, args)
835 }
836 Expression::ItemMemberFunctionCall { function } => call_item_member_function(ctx, function),
837 Expression::ExtraBuiltinFunctionCall { function, arguments, .. } => {
838 crate::eval_layout::call_extra_builtin(ctx, function, arguments)
839 }
840 Expression::PropertyAssignment { property, value } => {
841 let v = eval_expression(ctx, value);
842 store_property(ctx, property, v);
843 Value::Void
844 }
845 Expression::ModelDataAssignment { level, value } => {
846 let new_value = eval_expression(ctx, value);
847 if let Some(current) = ctx.current.as_ref() {
848 let Some(walker) = try_walk_parent(current, *level) else { return Value::Void };
849 if let Some((parent_weak, repeater_idx)) = walker.repeated_in.get()
850 && let Some(parent) = parent_weak.upgrade()
851 {
852 let row = walker.compilation_unit.sub_components[walker.sub_component_idx]
855 .properties
856 .iter_enumerated()
857 .find(|(_, p)| p.name.as_str() == "model_index")
858 .map(|(idx, _)| {
859 let v = std::pin::Pin::as_ref(&walker.properties[idx]).get();
860 f64::try_from(v).unwrap_or(0.) as usize
861 })
862 .unwrap_or(0);
863 let parent_pinned = std::pin::Pin::new(parent);
864 let repeater = &parent_pinned.repeaters[*repeater_idx];
865 repeater.model_set_row_data(row, new_value);
866 }
867 }
868 Value::Void
869 }
870 Expression::ArrayIndexAssignment { array, index, value } => {
871 let value = eval_expression(ctx, value);
872 let array = eval_expression(ctx, array);
873 let index = eval_expression(ctx, index);
874 if let (Value::Model(m), Value::Number(i)) = (array, index)
875 && i >= 0.0
876 {
877 let i = i.trunc() as usize;
878 if i < m.row_count() {
879 m.set_row_data(i, value);
880 }
881 }
882 Value::Void
883 }
884 Expression::SliceIndexAssignment { slice_name, index, value } => {
885 let value = eval_expression(ctx, value);
886 match ctx.locals.get_mut(slice_name.as_str()) {
887 Some(Value::ArrayOfU16(vec)) => {
888 if let Value::Number(n) = value
889 && *index < vec.len()
890 {
891 vec.make_mut_slice()[*index] = n as u16;
892 }
893 }
894 Some(Value::Model(m)) if *index < m.row_count() => {
895 m.set_row_data(*index, value);
896 }
897 _ => {}
898 }
899 Value::Void
900 }
901 Expression::BinaryExpression { lhs, rhs, op, .. } => {
902 let lhs = eval_expression(ctx, lhs);
903 match (op, &lhs) {
906 ('&', Value::Bool(false)) => return Value::Bool(false),
907 ('|', Value::Bool(true)) => return Value::Bool(true),
908 _ => {}
909 }
910 let rhs = eval_expression(ctx, rhs);
911 binary_op(*op, lhs, rhs)
912 }
913 Expression::UnaryOp { sub, op } => {
914 let sub = eval_expression(ctx, sub);
915 match (sub, op) {
916 (Value::Number(a), '+') => Value::Number(a),
917 (Value::Number(a), '-') => Value::Number(-a),
918 (Value::Bool(a), '!') => Value::Bool(!a),
919 (Value::Void, '+' | '-') => Value::Number(0.0),
922 (Value::Void, '!') => Value::Bool(true),
923 (s, o) => panic!("unsupported {o} {s:?}"),
924 }
925 }
926 Expression::ImageReference { resource_ref, nine_slice } => {
927 let mut image = load_image_reference(resource_ref);
928 if let Some(n) = nine_slice {
929 image.set_nine_slice_edges(n[0], n[1], n[2], n[3]);
930 }
931 Value::Image(image)
932 }
933 Expression::Condition { condition, true_expr, false_expr, .. } => {
934 match eval_expression(ctx, condition) {
935 Value::Bool(true) => eval_expression(ctx, true_expr),
936 Value::Bool(false) => eval_expression(ctx, false_expr),
937 _ => Value::Void,
938 }
939 }
940 Expression::Array { values, .. } => Value::Model(ModelRc::new(SharedVectorModel::from(
941 values.iter().map(|e| eval_expression(ctx, e)).collect::<SharedVector<_>>(),
942 ))),
943 Expression::Struct { values, .. } => Value::Struct(
944 values.iter().map(|(k, v)| (k.to_string(), eval_expression(ctx, v))).collect(),
945 ),
946 Expression::EasingCurve(curve) => {
947 use i_slint_compiler::expression_tree::EasingCurve as EC;
948 use i_slint_core::animations::EasingCurve as Core;
949 Value::EasingCurve(match curve {
950 EC::Linear => Core::Linear,
951 EC::EaseInElastic => Core::EaseInElastic,
952 EC::EaseOutElastic => Core::EaseOutElastic,
953 EC::EaseInOutElastic => Core::EaseInOutElastic,
954 EC::EaseInBounce => Core::EaseInBounce,
955 EC::EaseOutBounce => Core::EaseOutBounce,
956 EC::EaseInOutBounce => Core::EaseInOutBounce,
957 EC::CubicBezier(a, b, c, d) => Core::CubicBezier([*a, *b, *c, *d]),
958 EC::Spring(bounce) => Core::Spring(*bounce),
959 })
960 }
961 Expression::MouseCursor(cursor) => {
962 use i_slint_compiler::expression_tree::MouseCursorInner as Expr;
963 use i_slint_core::cursor::MouseCursorInner as Core;
964 Value::MouseCursorInner(match cursor {
965 Expr::BuiltIn(cursor) => {
966 Core::BuiltIn(eval_expression(ctx, cursor).try_into().unwrap_or_default())
967 }
968 Expr::CustomMouseCursor { image, hotspot_x, hotspot_y } => {
969 Core::CustomMouseCursor {
970 image: eval_expression(ctx, image).try_into().unwrap_or_default(),
971 hotspot_x: eval_expression(ctx, hotspot_x).try_into().unwrap_or_default(),
972 hotspot_y: eval_expression(ctx, hotspot_y).try_into().unwrap_or_default(),
973 }
974 }
975 })
976 }
977 Expression::LinearGradient { angle, stops } => {
978 let angle: f32 = eval_expression(ctx, angle).try_into().unwrap_or_default();
979 Value::Brush(Brush::LinearGradient(LinearGradientBrush::new(
980 angle,
981 eval_stops(ctx, stops),
982 )))
983 }
984 Expression::RadialGradient { stops, center, radius } => {
985 let mut g = RadialGradientBrush::new_circle(eval_stops(ctx, stops));
986 if let Some((cx, cy)) = center {
987 let cx: f32 = eval_expression(ctx, cx).try_into().unwrap_or_default();
988 let cy: f32 = eval_expression(ctx, cy).try_into().unwrap_or_default();
989 g = g.with_center(cx, cy);
990 }
991 if let Some(r) = radius {
992 let r: f32 = eval_expression(ctx, r).try_into().unwrap_or_default();
993 g = g.with_radius(r);
994 }
995 Value::Brush(Brush::RadialGradient(g))
996 }
997 Expression::ConicGradient { from_angle, stops, center } => {
998 let from_angle: f32 = eval_expression(ctx, from_angle).try_into().unwrap_or_default();
999 let mut g = ConicGradientBrush::new(from_angle, eval_stops(ctx, stops));
1000 if let Some((cx, cy)) = center {
1001 let cx: f32 = eval_expression(ctx, cx).try_into().unwrap_or_default();
1002 let cy: f32 = eval_expression(ctx, cy).try_into().unwrap_or_default();
1003 g = g.with_center(cx, cy);
1004 }
1005 Value::Brush(Brush::ConicGradient(g))
1006 }
1007 Expression::EnumerationValue(value) => {
1008 Value::EnumerationValue(value.enumeration.name.to_string(), value.to_string())
1009 }
1010 Expression::LayoutCacheAccess {
1011 layout_cache_prop,
1012 index,
1013 repeater_index,
1014 entries_per_item,
1015 } => {
1016 let cache = load_property(ctx, layout_cache_prop);
1017 layout_cache_access(ctx, cache, *index, repeater_index.as_deref(), *entries_per_item)
1018 }
1019 Expression::GridRepeaterCacheAccess {
1020 layout_cache_prop,
1021 index,
1022 repeater_index,
1023 stride,
1024 child_offset,
1025 inner_repeater_index,
1026 entries_per_item,
1027 } => {
1028 let cache = load_property(ctx, layout_cache_prop);
1029 let offset: usize = eval_expression(ctx, repeater_index).try_into().unwrap_or_default();
1030 let stride_val: usize = eval_expression(ctx, stride).try_into().unwrap_or_default();
1031 let inner_offset: usize = inner_repeater_index
1032 .as_deref()
1033 .map(|e| {
1034 let i: usize = eval_expression(ctx, e).try_into().unwrap_or_default();
1035 i * *entries_per_item
1036 })
1037 .unwrap_or(0);
1038 grid_repeater_cache_access(
1039 cache,
1040 *index,
1041 offset,
1042 stride_val,
1043 *child_offset,
1044 inner_offset,
1045 )
1046 }
1047 Expression::WithLayoutItemInfo {
1048 cells_variable,
1049 elements,
1050 orientation,
1051 repeated_cross_size,
1052 sub_expression,
1053 ..
1054 } => with_layout_item_info(
1055 ctx,
1056 cells_variable,
1057 elements,
1058 *orientation,
1059 repeated_cross_size.as_deref(),
1060 sub_expression,
1061 ),
1062 Expression::WithFlexboxLayoutItemInfo {
1063 cells_h_variable,
1064 cells_v_variable,
1065 flex_props_variable,
1066 elements,
1067 repeated_cross_width,
1068 sub_expression,
1069 ..
1070 } => with_flexbox_layout_item_info(
1071 ctx,
1072 cells_h_variable,
1073 cells_v_variable,
1074 flex_props_variable.as_deref(),
1075 elements,
1076 repeated_cross_width.as_deref(),
1077 sub_expression,
1078 ),
1079 Expression::WithGridInputData { cells_variable, elements, sub_expression, .. } => {
1080 with_grid_input_data(ctx, cells_variable, elements, sub_expression)
1081 }
1082 Expression::MinMax { ty: _, op, lhs, rhs } => {
1083 let Value::Number(lhs) = eval_expression(ctx, lhs) else { return Value::Void };
1084 let Value::Number(rhs) = eval_expression(ctx, rhs) else { return Value::Void };
1085 match op {
1086 MinMaxOp::Min => Value::Number(lhs.min(rhs)),
1087 MinMaxOp::Max => Value::Number(lhs.max(rhs)),
1088 }
1089 }
1090 Expression::EmptyComponentFactory => Value::ComponentFactory(Default::default()),
1091 Expression::EmptyDataTransfer => Value::DataTransfer(Default::default()),
1092 Expression::SolveFlexboxLayoutWithMeasure { .. } => {
1093 crate::eval_layout::solve_flexbox_layout_with_measure(ctx, expression)
1094 }
1095 Expression::FlexboxLayoutInfoCrossAxisWithMeasure { .. } => {
1096 crate::eval_layout::flexbox_layout_info_cross_axis_with_measure(ctx, expression)
1097 }
1098 Expression::BoxLayoutInfoOrthoWithMeasure { .. } => {
1099 crate::eval_layout::box_layout_info_ortho_with_measure(ctx, expression)
1100 }
1101 #[cfg(feature = "bundle-translations")]
1102 Expression::TranslationReference { format_args, string_index, plural } => {
1103 eval_translation_reference(ctx, format_args, *string_index, plural.as_deref())
1104 }
1105 #[cfg(not(feature = "bundle-translations"))]
1108 Expression::TranslationReference { .. } => Value::String(Default::default()),
1109 Expression::Closure { .. } => unreachable!(
1110 "closures are dispatched by their consuming builtin and should not go through eval_expression"
1111 ),
1112 Expression::DebugHook { expression, id } => {
1113 if let Some(hook_value) = crate::debug_hook::trigger_debug_hook(ctx, id) {
1114 return hook_value;
1115 }
1116 eval_expression(ctx, expression)
1117 }
1118 }
1119}
1120
1121fn with_layout_item_info(
1122 ctx: &mut EvalContext,
1123 cells_variable: &str,
1124 elements: &[itertools::Either<Expression, i_slint_compiler::llr::LayoutRepeatedElement>],
1125 orientation: i_slint_compiler::layout::Orientation,
1126 repeated_cross_size: Option<&Expression>,
1127 sub_expression: &Expression,
1128) -> Value {
1129 let cross_size: Option<f32> =
1134 repeated_cross_size.and_then(|e| eval_expression(ctx, e).try_into().ok());
1135 let mut cells: Vec<Value> = Vec::with_capacity(elements.len());
1136 let mut repeated_indices: Vec<u32> = Vec::new();
1137 let mut repeater_steps: Vec<u32> = Vec::new();
1138 for el in elements {
1139 match el {
1140 itertools::Either::Left(expr) => cells.push(eval_expression(ctx, expr)),
1141 itertools::Either::Right(repeater) => {
1142 let offset = cells.len() as u32;
1143 let (instances, step) = push_repeater_layout_items(
1144 ctx,
1145 repeater.repeater_index,
1146 repeater.row_child_templates.as_deref(),
1147 orientation,
1148 cross_size,
1149 repeater.cross_width.as_ref(),
1150 &mut cells,
1151 );
1152 repeated_indices.push(offset);
1153 repeated_indices.push(instances);
1154 repeater_steps.push(step);
1155 }
1156 }
1157 }
1158 let prev_cells =
1159 ctx.locals.insert(SmolStr::from(cells_variable), Value::Model(model_from_vec(cells)));
1160 let prev_ri = ctx.locals.insert(
1161 SmolStr::new_static("repeated_indices"),
1162 Value::Model(model_from_vec(
1163 repeated_indices.into_iter().map(|i| Value::Number(i as f64)).collect(),
1164 )),
1165 );
1166 let prev_rs = ctx.locals.insert(
1167 SmolStr::new_static("repeater_steps"),
1168 Value::Model(model_from_vec(
1169 repeater_steps.into_iter().map(|i| Value::Number(i as f64)).collect(),
1170 )),
1171 );
1172 let result = eval_expression(ctx, sub_expression);
1173 restore_local(ctx, cells_variable, prev_cells);
1174 restore_local(ctx, "repeated_indices", prev_ri);
1175 restore_local(ctx, "repeater_steps", prev_rs);
1176 result
1177}
1178
1179fn push_repeater_layout_items(
1180 ctx: &mut EvalContext,
1181 repeater_idx: i_slint_compiler::llr::RepeatedElementIdx,
1182 row_child_templates: Option<&[i_slint_compiler::llr::RowChildTemplateInfo]>,
1183 orientation: i_slint_compiler::layout::Orientation,
1184 cross_size: Option<f32>,
1185 grid_cross_width: Option<&Expression>,
1186 cells: &mut Vec<Value>,
1187) -> (u32, u32) {
1188 use i_slint_core::model::RepeatedItemTree;
1189 let Some(current) = ctx.current.as_ref() else { return (0, 0) };
1190 let repeater = ¤t.repeaters[repeater_idx];
1191 repeater.track_instance_changes();
1192 let instances = repeater.instances_vec();
1193 let core_orientation = llr_to_core_orientation(orientation);
1194 let push_cell = |cells: &mut Vec<Value>, info: i_slint_core::layout::LayoutItemInfo| {
1195 let mut struct_value = crate::api::Struct::default();
1196 struct_value.set_field("constraint".to_string(), info.constraint.into());
1197 if info.cross_axis_self_alignment != i_slint_core::items::CrossAxisAlignment::Auto {
1200 struct_value.set_field(
1201 "cross-axis-self-alignment".to_string(),
1202 Value::EnumerationValue(
1203 "CrossAxisAlignment".to_string(),
1204 info.cross_axis_self_alignment.to_string(),
1205 ),
1206 );
1207 }
1208 if info.layout_order != 0 {
1210 struct_value
1211 .set_field("layout-order".to_string(), Value::Number(info.layout_order as f64));
1212 }
1213 cells.push(Value::Struct(struct_value));
1214 };
1215 let step = match row_child_templates {
1216 None => {
1217 for (i, instance) in instances.iter().enumerate() {
1221 let info = match (cross_size, core_orientation) {
1222 (Some(cs), i_slint_core::items::Orientation::Vertical) => {
1223 RepeatedItemTree::layout_item_info_at_cross_width(instance.as_pin_ref(), cs)
1224 }
1225 (Some(_), i_slint_core::items::Orientation::Horizontal) => {
1226 unreachable!("a horizontal main pass forwards no cross size")
1227 }
1228 (None, _) => {
1231 match grid_cross_width.and_then(|e| eval_grid_measure_width(ctx, e, i)) {
1232 Some(w) => RepeatedItemTree::layout_item_info_at_cross_width(
1233 instance.as_pin_ref(),
1234 w,
1235 ),
1236 None => RepeatedItemTree::layout_item_info(
1237 instance.as_pin_ref(),
1238 core_orientation,
1239 None,
1240 ),
1241 }
1242 }
1243 };
1244 push_cell(cells, info);
1245 }
1246 1
1247 }
1248 Some(templates) => {
1249 debug_assert!(cross_size.is_none());
1252 let max_total = instances
1256 .iter()
1257 .map(|inst| total_row_child_count(&inst.root_sub_component, templates))
1258 .max()
1259 .unwrap_or(i_slint_compiler::llr::static_child_count(templates));
1260 for instance in &instances {
1261 for child_idx in 0..max_total {
1262 let info = RepeatedItemTree::layout_item_info(
1263 instance.as_pin_ref(),
1264 core_orientation,
1265 Some(child_idx),
1266 );
1267 push_cell(cells, info);
1268 }
1269 }
1270 max_total as u32
1271 }
1272 };
1273 (instances.len() as u32, step)
1274}
1275
1276fn eval_grid_measure_width(ctx: &mut EvalContext, expr: &Expression, index: usize) -> Option<f32> {
1280 use i_slint_compiler::llr::lower_layout_expression::GRID_MEASURE_REPEATER_INDEX_LOCAL;
1281 let prev = ctx.locals.insert(
1282 SmolStr::new_static(GRID_MEASURE_REPEATER_INDEX_LOCAL),
1283 Value::Number(index as f64),
1284 );
1285 let value = eval_expression(ctx, expr);
1286 restore_local(ctx, GRID_MEASURE_REPEATER_INDEX_LOCAL, prev);
1287 value.try_into().ok()
1288}
1289
1290fn total_row_child_count(
1291 sub: &Pin<std::rc::Rc<crate::instance::SubComponentInstance>>,
1292 templates: &[i_slint_compiler::llr::RowChildTemplateInfo],
1293) -> usize {
1294 use i_slint_compiler::llr::{RowChildTemplateInfo, static_child_count};
1295 let mut total = static_child_count(templates);
1296 for entry in templates {
1297 if let RowChildTemplateInfo::Repeated { repeater_index, .. } = entry {
1298 let repeater = &sub.repeaters[*repeater_index];
1299 repeater.track_instance_changes();
1300 total += repeater.range().len();
1301 }
1302 }
1303 total
1304}
1305
1306pub(crate) fn llr_to_core_orientation(
1307 o: i_slint_compiler::layout::Orientation,
1308) -> i_slint_core::items::Orientation {
1309 match o {
1310 i_slint_compiler::layout::Orientation::Horizontal => {
1311 i_slint_core::items::Orientation::Horizontal
1312 }
1313 i_slint_compiler::layout::Orientation::Vertical => {
1314 i_slint_core::items::Orientation::Vertical
1315 }
1316 }
1317}
1318
1319fn with_flexbox_layout_item_info(
1320 ctx: &mut EvalContext,
1321 cells_h_variable: &str,
1322 cells_v_variable: &str,
1323 flex_props_variable: Option<&str>,
1324 elements: &[itertools::Either<
1325 (Expression, Expression, Expression),
1326 i_slint_compiler::llr::LayoutRepeatedElement,
1327 >],
1328 repeated_cross_width: Option<&Expression>,
1329 sub_expression: &Expression,
1330) -> Value {
1331 let cross_width =
1334 repeated_cross_width.map(|e| eval_expression(ctx, e).try_into().unwrap_or_default());
1335 let mut cells_h: Vec<Value> = Vec::with_capacity(elements.len());
1336 let mut cells_v: Vec<Value> = Vec::with_capacity(elements.len());
1337 let mut flex_props: Vec<Value> = Vec::with_capacity(elements.len());
1338 let mut repeated_indices: Vec<u32> = Vec::new();
1339 for el in elements {
1340 match el {
1341 itertools::Either::Left((h, v, props)) => {
1342 cells_h.push(eval_expression(ctx, h));
1343 cells_v.push(eval_expression(ctx, v));
1344 if flex_props_variable.is_some() {
1348 flex_props.push(eval_expression(ctx, props));
1349 }
1350 }
1351 itertools::Either::Right(repeater) => {
1352 let offset = cells_h.len() as u32;
1353 let instances = push_repeater_flexbox_items(
1354 ctx,
1355 repeater.repeater_index,
1356 cross_width,
1357 &mut cells_h,
1358 &mut cells_v,
1359 flex_props_variable.is_some().then_some(&mut flex_props),
1360 );
1361 repeated_indices.push(offset);
1362 repeated_indices.push(instances);
1363 }
1364 }
1365 }
1366 let prev_h =
1367 ctx.locals.insert(SmolStr::from(cells_h_variable), Value::Model(model_from_vec(cells_h)));
1368 let prev_v =
1369 ctx.locals.insert(SmolStr::from(cells_v_variable), Value::Model(model_from_vec(cells_v)));
1370 let prev_fp = flex_props_variable.map(|name| {
1371 ctx.locals.insert(SmolStr::from(name), Value::Model(model_from_vec(flex_props)))
1372 });
1373 let prev_ri = ctx.locals.insert(
1374 SmolStr::new_static("repeated_indices"),
1375 Value::Model(model_from_vec(
1376 repeated_indices.into_iter().map(|i| Value::Number(i as f64)).collect(),
1377 )),
1378 );
1379 let result = eval_expression(ctx, sub_expression);
1380 restore_local(ctx, cells_h_variable, prev_h);
1381 restore_local(ctx, cells_v_variable, prev_v);
1382 if let Some(name) = flex_props_variable {
1383 restore_local(ctx, name, prev_fp.flatten());
1384 }
1385 restore_local(ctx, "repeated_indices", prev_ri);
1386 result
1387}
1388
1389fn push_repeater_flexbox_items(
1390 ctx: &mut EvalContext,
1391 repeater_idx: i_slint_compiler::llr::RepeatedElementIdx,
1392 cross_width: Option<f32>,
1393 cells_h: &mut Vec<Value>,
1394 cells_v: &mut Vec<Value>,
1395 mut flex_props: Option<&mut Vec<Value>>,
1396) -> u32 {
1397 use i_slint_core::items::Orientation;
1398 use i_slint_core::model::RepeatedItemTree;
1399 let Some(current) = ctx.current.as_ref() else { return 0 };
1400 let repeater = ¤t.repeaters[repeater_idx];
1401 repeater.track_instance_changes();
1402 let instances = repeater.instances_vec();
1403 let instance_count = instances.len() as u32;
1404 for instance in instances {
1405 let info_h = RepeatedItemTree::flexbox_layout_item_info(
1409 instance.as_pin_ref(),
1410 Orientation::Horizontal,
1411 None,
1412 );
1413 let info_v = match cross_width {
1416 Some(w) => instance.as_pin_ref().flexbox_layout_item_info_at_cross_width(w),
1417 None => RepeatedItemTree::flexbox_layout_item_info(
1418 instance.as_pin_ref(),
1419 Orientation::Vertical,
1420 None,
1421 ),
1422 };
1423 if let Some(fp) = flex_props.as_mut() {
1426 fp.push(flex_props_to_value(info_h.props));
1427 }
1428 cells_h.push(layout_item_info_to_value(info_h.constraint));
1429 cells_v.push(layout_item_info_to_value(info_v.constraint));
1430 }
1431 instance_count
1432}
1433
1434fn layout_item_info_to_value(constraint: i_slint_core::layout::LayoutInfo) -> Value {
1435 let mut s = crate::api::Struct::default();
1436 s.set_field("constraint".to_string(), constraint.into());
1437 Value::Struct(s)
1438}
1439
1440fn flex_props_to_value(props: i_slint_core::layout::FlexItemProps) -> Value {
1441 let mut s = crate::api::Struct::default();
1442 s.set_field(
1443 "cross-axis-self-alignment".to_string(),
1444 Value::EnumerationValue(
1445 "CrossAxisAlignment".to_string(),
1446 format!("{:?}", props.cross_axis_self_alignment).to_lowercase(),
1447 ),
1448 );
1449 s.set_field("layout-order".to_string(), Value::Number(props.layout_order as f64));
1450 Value::Struct(s)
1451}
1452
1453fn with_grid_input_data(
1454 ctx: &mut EvalContext,
1455 cells_variable: &str,
1456 elements: &[itertools::Either<Expression, i_slint_compiler::llr::GridLayoutRepeatedElement>],
1457 sub_expression: &Expression,
1458) -> Value {
1459 let saved_new_row = ctx.locals.remove("new_row");
1466 let mut cells: Vec<Value> = Vec::with_capacity(elements.len());
1467 let mut repeated_indices: Vec<u32> = Vec::new();
1468 let mut repeater_steps: Vec<u32> = Vec::new();
1469
1470 for el in elements {
1471 match el {
1472 itertools::Either::Left(expr) => cells.push(eval_expression(ctx, expr)),
1473 itertools::Either::Right(repeater) => {
1474 ctx.locals.insert(SmolStr::new_static("new_row"), Value::Bool(repeater.new_row));
1475 let offset = cells.len() as u32;
1476 let is_row_repeater = repeater.row_child_templates.is_some();
1477 let (instances, step) = push_repeater_grid_input_data(
1478 ctx,
1479 repeater.repeater_index,
1480 repeater.new_row,
1481 repeater.row_child_templates.as_deref(),
1482 &mut cells,
1483 );
1484 if !is_row_repeater && instances > 0 {
1485 ctx.locals.insert(SmolStr::new_static("new_row"), Value::Bool(false));
1486 }
1487 repeated_indices.push(offset);
1488 repeated_indices.push(instances);
1489 repeater_steps.push(step);
1490 }
1491 }
1492 }
1493 restore_local(ctx, "new_row", saved_new_row);
1494
1495 let prev_cells =
1496 ctx.locals.insert(SmolStr::from(cells_variable), Value::Model(model_from_vec(cells)));
1497 let prev_ri = ctx.locals.insert(
1498 SmolStr::new_static("repeated_indices"),
1499 Value::Model(model_from_vec(
1500 repeated_indices.into_iter().map(|i| Value::Number(i as f64)).collect(),
1501 )),
1502 );
1503 let prev_rs = ctx.locals.insert(
1504 SmolStr::new_static("repeater_steps"),
1505 Value::Model(model_from_vec(
1506 repeater_steps.into_iter().map(|i| Value::Number(i as f64)).collect(),
1507 )),
1508 );
1509
1510 let result = eval_expression(ctx, sub_expression);
1511
1512 restore_local(ctx, cells_variable, prev_cells);
1513 restore_local(ctx, "repeated_indices", prev_ri);
1514 restore_local(ctx, "repeater_steps", prev_rs);
1515 result
1516}
1517
1518pub(crate) fn restore_local(ctx: &mut EvalContext, name: &str, prev: Option<Value>) {
1519 if let Some(prev) = prev {
1520 ctx.locals.insert(SmolStr::from(name), prev);
1521 } else {
1522 ctx.locals.remove(name);
1523 }
1524}
1525
1526fn push_repeater_grid_input_data(
1527 ctx: &mut EvalContext,
1528 repeater_idx: i_slint_compiler::llr::RepeatedElementIdx,
1529 new_row: bool,
1530 row_child_templates: Option<&[i_slint_compiler::llr::RowChildTemplateInfo]>,
1531 cells: &mut Vec<Value>,
1532) -> (u32, u32) {
1533 use i_slint_compiler::llr::RowChildTemplateInfo;
1534 use i_slint_core::model::VecModel;
1535 use std::rc::Rc;
1536 let Some(current) = ctx.current.as_ref() else { return (0, 0) };
1537 let repeater = ¤t.repeaters[repeater_idx];
1538 repeater.track_instance_changes();
1539
1540 let is_row_repeater = row_child_templates.is_some();
1541 let static_count =
1542 row_child_templates.map(i_slint_compiler::llr::static_child_count).unwrap_or(1);
1543
1544 let instances = repeater.instances_vec();
1545 let instance_count = instances.len() as u32;
1546
1547 let step = if let Some(templates) = row_child_templates {
1551 instances
1552 .iter()
1553 .map(|inst| total_row_child_count(&inst.root_sub_component, templates))
1554 .max()
1555 .unwrap_or(static_count)
1556 } else {
1557 1
1558 };
1559
1560 let mut current_new_row = new_row;
1561
1562 for instance in &instances {
1563 let inner_sub = instance.root_sub_component.clone();
1564 let cu = inner_sub.compilation_unit.clone();
1565 let sc = &cu.sub_components[inner_sub.sub_component_idx];
1566
1567 let mut statics: Vec<Value> = vec![Value::Void; static_count];
1571 if let Some(expr) = &sc.grid_layout_input_for_repeated {
1572 let expr = expr.borrow();
1573 let mut inner_ctx = EvalContext::new(inner_sub.clone());
1574 let result_model: Rc<VecModel<Value>> = Rc::new(VecModel::default());
1575 for _ in 0..static_count {
1576 result_model.push(Value::Void);
1577 }
1578 inner_ctx.locals.insert(
1579 SmolStr::new_static("result"),
1580 Value::Model(i_slint_core::model::ModelRc::from(result_model.clone())),
1581 );
1582 inner_ctx.locals.insert(SmolStr::new_static("new_row"), Value::Bool(current_new_row));
1583 eval_expression(&mut inner_ctx, &expr);
1584 for (slot, i) in statics.iter_mut().zip(0..result_model.row_count()) {
1585 if let Some(v) = result_model.row_data(i) {
1586 *slot = v;
1587 }
1588 }
1589 }
1590
1591 if let Some(templates) = row_child_templates {
1592 let mut written = 0usize;
1596 let mut static_idx = 0usize;
1597 for entry in templates {
1598 if written >= step {
1599 break;
1600 }
1601 match entry {
1602 RowChildTemplateInfo::Static { .. } => {
1603 let mut v = statics.get(static_idx).cloned().unwrap_or(Value::Void);
1604 static_idx += 1;
1605 override_new_row(&mut v, written == 0 && current_new_row);
1606 cells.push(v);
1607 written += 1;
1608 }
1609 RowChildTemplateInfo::Repeated { repeater_index, .. } => {
1610 let inner_rep = &inner_sub.repeaters[*repeater_index];
1611 inner_rep.track_instance_changes();
1612 for inner_inst in inner_rep.instances_vec() {
1616 if written >= step {
1617 break;
1618 }
1619 for mut v in eval_grid_input_for_repeated(
1620 &inner_inst.root_sub_component,
1621 written == 0 && current_new_row,
1622 ) {
1623 if written >= step {
1624 break;
1625 }
1626 override_new_row(&mut v, written == 0 && current_new_row);
1627 cells.push(v);
1628 written += 1;
1629 }
1630 }
1631 }
1632 }
1633 }
1634 while written < step {
1635 cells.push(auto_grid_input_data());
1636 written += 1;
1637 }
1638 } else {
1639 cells.push(statics.pop().unwrap_or_else(auto_grid_input_data));
1641 }
1642
1643 if !is_row_repeater {
1644 current_new_row = false;
1645 }
1646 }
1647 (instance_count, step as u32)
1648}
1649
1650fn eval_grid_input_for_repeated(
1655 sub: &Pin<Rc<crate::instance::SubComponentInstance>>,
1656 new_row: bool,
1657) -> Vec<Value> {
1658 use i_slint_core::model::{Model, VecModel};
1659 let cu = sub.compilation_unit.clone();
1660 let sc = &cu.sub_components[sub.sub_component_idx];
1661 let count = sc
1662 .row_child_templates
1663 .as_ref()
1664 .map(|t| i_slint_compiler::llr::static_child_count(t))
1665 .unwrap_or(1)
1666 .max(1);
1667 let Some(expr) = &sc.grid_layout_input_for_repeated else {
1668 return vec![auto_grid_input_data()];
1669 };
1670 let expr = expr.borrow();
1671 let mut ctx = EvalContext::new(sub.clone());
1672 let result_model: Rc<VecModel<Value>> = Rc::new(VecModel::default());
1673 for _ in 0..count {
1674 result_model.push(Value::Void);
1675 }
1676 ctx.locals.insert(
1677 SmolStr::new_static("result"),
1678 Value::Model(i_slint_core::model::ModelRc::from(result_model.clone())),
1679 );
1680 ctx.locals.insert(SmolStr::new_static("new_row"), Value::Bool(new_row));
1681 eval_expression(&mut ctx, &expr);
1682 (0..result_model.row_count())
1683 .map(|i| result_model.row_data(i).unwrap_or_else(auto_grid_input_data))
1684 .collect()
1685}
1686
1687fn auto_grid_input_data() -> Value {
1690 let mut s = crate::api::Struct::default();
1691 s.set_field("new-row".into(), Value::Bool(false));
1692 s.set_field("row".into(), Value::Number(i_slint_common::ROW_COL_AUTO as f64));
1693 s.set_field("col".into(), Value::Number(i_slint_common::ROW_COL_AUTO as f64));
1694 s.set_field("rowspan".into(), Value::Number(1.0));
1695 s.set_field("colspan".into(), Value::Number(1.0));
1696 Value::Struct(s)
1697}
1698
1699fn override_new_row(v: &mut Value, new_row: bool) {
1700 if let Value::Struct(s) = v {
1701 s.set_field("new-row".into(), Value::Bool(new_row));
1702 }
1703}
1704
1705fn model_from_vec(values: Vec<Value>) -> ModelRc<Value> {
1706 ModelRc::new(SharedVectorModel::from(values.into_iter().collect::<SharedVector<_>>()))
1707}
1708
1709fn binary_op(op: char, lhs: Value, rhs: Value) -> Value {
1710 let (lhs, rhs) = match (lhs, rhs) {
1713 (Value::Void, Value::Number(b)) => (Value::Number(0.), Value::Number(b)),
1714 (Value::Number(a), Value::Void) => (Value::Number(a), Value::Number(0.)),
1715 (Value::Void, Value::Bool(b)) => (Value::Bool(false), Value::Bool(b)),
1716 (Value::Bool(a), Value::Void) => (Value::Bool(a), Value::Bool(false)),
1717 (Value::Void, Value::String(b)) => (Value::String(Default::default()), Value::String(b)),
1718 (Value::String(a), Value::Void) => (Value::String(a), Value::String(Default::default())),
1719 (Value::Void, Value::Void) if matches!(op, '&' | '|') => {
1721 (Value::Bool(false), Value::Bool(false))
1722 }
1723 (Value::Void, Value::Void) => (Value::Number(0.), Value::Number(0.)),
1724 (a, b) => (a, b),
1725 };
1726 match (op, lhs, rhs) {
1727 ('+', Value::String(mut a), Value::String(b)) => {
1728 a.push_str(b.as_str());
1729 Value::String(a)
1730 }
1731 ('+', Value::Number(a), Value::Number(b)) => Value::Number(a + b),
1732 ('+', a @ Value::Struct(_), b @ Value::Struct(_)) => {
1733 let la: Option<i_slint_core::layout::LayoutInfo> = a.try_into().ok();
1734 let lb: Option<i_slint_core::layout::LayoutInfo> = b.try_into().ok();
1735 if let (Some(a), Some(b)) = (la, lb) {
1736 a.merge(&b).into()
1737 } else {
1738 panic!("unsupported struct + struct");
1739 }
1740 }
1741 ('-', Value::Number(a), Value::Number(b)) => Value::Number(a - b),
1742 ('/', Value::Number(a), Value::Number(b)) => Value::Number(a / b),
1743 ('*', Value::Number(a), Value::Number(b)) => Value::Number(a * b),
1744 ('<', Value::Number(a), Value::Number(b)) => Value::Bool(a < b),
1745 ('>', Value::Number(a), Value::Number(b)) => Value::Bool(a > b),
1746 ('≤', Value::Number(a), Value::Number(b)) => Value::Bool(a <= b),
1747 ('≥', Value::Number(a), Value::Number(b)) => Value::Bool(a >= b),
1748 ('<', Value::String(a), Value::String(b)) => Value::Bool(a < b),
1749 ('>', Value::String(a), Value::String(b)) => Value::Bool(a > b),
1750 ('≤', Value::String(a), Value::String(b)) => Value::Bool(a <= b),
1751 ('≥', Value::String(a), Value::String(b)) => Value::Bool(a >= b),
1752 ('=', a, b) => Value::Bool(a == b),
1753 ('!', a, b) => Value::Bool(a != b),
1754 ('&', Value::Bool(a), Value::Bool(b)) => Value::Bool(a && b),
1755 ('|', Value::Bool(a), Value::Bool(b)) => Value::Bool(a || b),
1756 (op, a, b) => panic!("unsupported {a:?} {op} {b:?}"),
1757 }
1758}
1759
1760fn eval_stops(ctx: &mut EvalContext, stops: &[(Expression, Expression)]) -> Vec<GradientStop> {
1761 stops
1762 .iter()
1763 .map(|(color, stop)| GradientStop {
1764 color: eval_expression(ctx, color).try_into().unwrap_or_default(),
1765 position: eval_expression(ctx, stop).try_into().unwrap_or_default(),
1766 })
1767 .collect()
1768}
1769
1770fn load_image_reference(
1771 resource_ref: &i_slint_compiler::expression_tree::ImageReference,
1772) -> i_slint_core::graphics::Image {
1773 use i_slint_compiler::expression_tree::ImageReference as Ref;
1774 let image = match resource_ref {
1775 Ref::None => Ok(Default::default()),
1776 Ref::DataUri(data_uri) => i_slint_compiler::data_uri::decode_data_uri(data_uri)
1777 .ok()
1778 .and_then(|(data, extension)| {
1779 i_slint_core::graphics::load_image_from_data_uri(data_uri, &data, &extension).ok()
1780 })
1781 .ok_or_else(Default::default),
1782 Ref::Url(url) if url.scheme() == "builtin" => {
1783 let path = std::path::Path::new(url.as_str());
1787 i_slint_compiler::fileaccess::load_file(path)
1788 .and_then(|virtual_file| virtual_file.builtin_contents)
1789 .map(|contents| {
1790 let extension = path.extension().unwrap().to_str().unwrap();
1791 i_slint_core::graphics::load_image_from_embedded_data(
1792 i_slint_core::slice::Slice::from_slice(contents),
1793 i_slint_core::slice::Slice::from_slice(extension.as_bytes()),
1794 )
1795 })
1796 .ok_or_else(Default::default)
1797 }
1798 Ref::Path(path) => {
1799 i_slint_core::graphics::Image::load_from_path(std::path::Path::new(path.as_str()))
1800 }
1801 Ref::Url(url) => {
1802 #[cfg(target_arch = "wasm32")]
1803 {
1804 i_slint_core::graphics::load_as_html_image(url.as_str())
1805 }
1806 #[cfg(not(target_arch = "wasm32"))]
1808 {
1809 let _ = url;
1810 Err(Default::default())
1811 }
1812 }
1813 Ref::EmbeddedData { .. } | Ref::EmbeddedTexture { .. } => Ok(Default::default()),
1814 };
1815 image.unwrap_or_else(|_| {
1816 eprintln!("Could not load image {resource_ref:?}");
1817 Default::default()
1818 })
1819}
1820
1821fn layout_cache_access(
1822 ctx: &mut EvalContext,
1823 cache: Value,
1824 index: usize,
1825 repeater_index: Option<&Expression>,
1826 entries_per_item: usize,
1827) -> Value {
1828 match cache {
1829 Value::LayoutCache(cache) => {
1830 if let Some(ri) = repeater_index {
1831 let offset: usize = eval_expression(ctx, ri).try_into().unwrap_or_default();
1832 Value::Number(
1833 cache
1834 .get((cache[index] as usize) + offset * entries_per_item)
1835 .copied()
1836 .unwrap_or(0.)
1837 .into(),
1838 )
1839 } else {
1840 Value::Number(cache[index].into())
1841 }
1842 }
1843 Value::ArrayOfU16(cache) => {
1844 if let Some(ri) = repeater_index {
1845 let offset: usize = eval_expression(ctx, ri).try_into().unwrap_or_default();
1846 Value::Number(
1847 cache
1848 .get((cache[index] as usize) + offset * entries_per_item)
1849 .copied()
1850 .unwrap_or(0)
1851 .into(),
1852 )
1853 } else {
1854 Value::Number(cache[index].into())
1855 }
1856 }
1857 _ => Value::Number(0.),
1858 }
1859}
1860
1861fn grid_repeater_cache_access(
1866 cache: Value,
1867 index: usize,
1868 repeater_index: usize,
1869 stride: usize,
1870 child_offset: usize,
1871 inner_offset: usize,
1872) -> Value {
1873 let get = |data_idx: usize, slice_len: usize, read: &dyn Fn(usize) -> f64| {
1874 if data_idx < slice_len { Value::Number(read(data_idx)) } else { Value::Number(0.) }
1875 };
1876 match cache {
1877 Value::LayoutCache(cache) => {
1878 let base = cache.get(index).copied().unwrap_or(0.) as usize;
1879 let data_idx = base + repeater_index * stride + child_offset + inner_offset;
1880 get(data_idx, cache.len(), &|i| cache[i] as f64)
1881 }
1882 Value::ArrayOfU16(cache) => {
1883 let base = cache.get(index).copied().unwrap_or(0) as usize;
1884 let data_idx = base + repeater_index * stride + child_offset + inner_offset;
1885 get(data_idx, cache.len(), &|i| cache[i] as f64)
1886 }
1887 _ => Value::Number(0.),
1888 }
1889}
1890
1891fn log_message_location(
1895 source_location: &Option<SourceLocation>,
1896) -> Option<i_slint_core::debug_log::LogMessageLocation<'_>> {
1897 let location = source_location.as_ref()?;
1898 let source_file = location.source_file.as_ref()?;
1899 let (line, column) = source_file
1900 .line_column(location.span.offset, i_slint_compiler::diagnostics::ByteFormat::Utf8);
1901 Some(i_slint_core::debug_log::LogMessageLocation {
1902 path: source_file.path().to_str()?,
1903 line,
1904 column,
1905 })
1906}
1907
1908struct StringModelWrapper(ModelRc<Value>);
1910impl i_slint_core::translations::FormatArgs for StringModelWrapper {
1911 type Output<'a> = SharedString;
1912 fn from_index(&self, index: usize) -> Option<SharedString> {
1913 self.0.row_data(index).and_then(|v| v.try_into().ok())
1914 }
1915}
1916
1917#[cfg(feature = "bundle-translations")]
1920fn eval_translation_reference(
1921 ctx: &mut EvalContext,
1922 format_args: &Expression,
1923 string_index: usize,
1924 plural: Option<&Expression>,
1925) -> Value {
1926 let unit = ctx.compilation_unit.clone();
1927 let Some(translations) = unit.translations.as_ref() else {
1928 return Value::String(Default::default());
1929 };
1930 let Value::Model(args) = eval_expression(ctx, format_args) else {
1931 return Value::String(Default::default());
1932 };
1933 let args = StringModelWrapper(args);
1934 let Some(plural) = plural else {
1935 return Value::String(i_slint_core::translations::translate_from_bundle(
1936 &translations.strings[string_index],
1937 &args,
1938 ));
1939 };
1940
1941 let n: i32 = eval_expression(ctx, plural).try_into().unwrap_or(0);
1942 let forms = translations.plurals[string_index].iter().map(|f| f.as_deref()).collect::<Vec<_>>();
1943 let globals = ctx.globals.clone();
1944 Value::String(i_slint_core::translations::translate_from_bundle_with_plural_form(
1945 &forms,
1946 |language_index| {
1947 let rule = translations.plural_rules.get(language_index)?.as_ref()?;
1948 let mut rule_ctx = EvalContext::for_global(globals, unit.clone());
1950 rule_ctx.function_arguments = vec![Value::Number(n as f64)];
1951 rule_ctx.function_arg_types = vec![Type::Int32];
1952 let form: i32 = eval_expression(&mut rule_ctx, rule).try_into().ok()?;
1953 usize::try_from(form).ok()
1954 },
1955 &args,
1956 n,
1957 ))
1958}
1959
1960fn call_builtin_function(
1961 ctx: &mut EvalContext,
1962 f: BuiltinFunction,
1963 arguments: &[Expression],
1964 source_location: &Option<SourceLocation>,
1965) -> Value {
1966 let to_num = |ctx: &mut EvalContext, e: &Expression| -> f64 {
1967 eval_expression(ctx, e).try_into().unwrap_or_default()
1968 };
1969 let to_string = |ctx: &mut EvalContext, e: &Expression| -> SharedString {
1970 eval_expression(ctx, e).try_into().unwrap_or_default()
1971 };
1972
1973 match f {
1974 BuiltinFunction::Mod => {
1975 Value::Number(to_num(ctx, &arguments[0]).rem_euclid(to_num(ctx, &arguments[1])))
1976 }
1977 BuiltinFunction::Round => Value::Number(to_num(ctx, &arguments[0]).round()),
1978 BuiltinFunction::Ceil => Value::Number(to_num(ctx, &arguments[0]).ceil()),
1979 BuiltinFunction::Floor => Value::Number(to_num(ctx, &arguments[0]).floor()),
1980 BuiltinFunction::Sqrt => Value::Number(to_num(ctx, &arguments[0]).sqrt()),
1981 BuiltinFunction::Abs => Value::Number(to_num(ctx, &arguments[0]).abs()),
1982 BuiltinFunction::Sin => Value::Number(to_num(ctx, &arguments[0]).to_radians().sin()),
1983 BuiltinFunction::Cos => Value::Number(to_num(ctx, &arguments[0]).to_radians().cos()),
1984 BuiltinFunction::Tan => Value::Number(to_num(ctx, &arguments[0]).to_radians().tan()),
1985 BuiltinFunction::ASin => Value::Number(to_num(ctx, &arguments[0]).asin().to_degrees()),
1986 BuiltinFunction::ACos => Value::Number(to_num(ctx, &arguments[0]).acos().to_degrees()),
1987 BuiltinFunction::ATan => Value::Number(to_num(ctx, &arguments[0]).atan().to_degrees()),
1988 BuiltinFunction::ATan2 => {
1989 Value::Number(to_num(ctx, &arguments[0]).atan2(to_num(ctx, &arguments[1])).to_degrees())
1990 }
1991 BuiltinFunction::Log => {
1992 Value::Number(to_num(ctx, &arguments[0]).log(to_num(ctx, &arguments[1])))
1993 }
1994 BuiltinFunction::Ln => Value::Number(to_num(ctx, &arguments[0]).ln()),
1995 BuiltinFunction::Pow => {
1996 Value::Number(to_num(ctx, &arguments[0]).powf(to_num(ctx, &arguments[1])))
1997 }
1998 BuiltinFunction::Exp => Value::Number(to_num(ctx, &arguments[0]).exp()),
1999 BuiltinFunction::ToFixed => {
2000 let n = to_num(ctx, &arguments[0]);
2001 let digits: i32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or_default();
2002 Value::String(i_slint_core::string::shared_string_from_number_fixed(
2003 n,
2004 digits.max(0) as usize,
2005 ))
2006 }
2007 BuiltinFunction::ToPrecision => {
2008 let n = to_num(ctx, &arguments[0]);
2009 let p: i32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or_default();
2010 Value::String(i_slint_core::string::shared_string_from_number_precision(
2011 n,
2012 p.max(0) as usize,
2013 ))
2014 }
2015 BuiltinFunction::StringStartsWith => Value::Bool(
2016 to_string(ctx, &arguments[0])
2017 .as_str()
2018 .starts_with(to_string(ctx, &arguments[1]).as_str()),
2019 ),
2020 BuiltinFunction::StringEndsWith => Value::Bool(
2021 to_string(ctx, &arguments[0])
2022 .as_str()
2023 .ends_with(to_string(ctx, &arguments[1]).as_str()),
2024 ),
2025 BuiltinFunction::ToStringUnlocalized => {
2026 let n = to_num(ctx, &arguments[0]);
2027 Value::String(i_slint_core::string::shared_string_from_number_unlocalized(n))
2028 }
2029 BuiltinFunction::DefaultWindowTitle => {
2030 Value::String(i_slint_core::window::default_window_title())
2031 }
2032 BuiltinFunction::DecimalSeparator => Value::String(
2033 find_window_adapter(ctx)
2034 .map(|adapter| {
2035 i_slint_core::window::WindowInner::from_pub(adapter.window())
2036 .context()
2037 .locale_decimal_separator()
2038 })
2039 .unwrap_or_default()
2040 .into(),
2041 ),
2042 BuiltinFunction::MacosBringAllWindowsToFront => {
2043 i_slint_core::macos_bring_all_windows_to_front();
2044 Value::Void
2045 }
2046 BuiltinFunction::ColorToStyledText => {
2047 let color: i_slint_core::Color =
2048 eval_expression(ctx, &arguments[0]).try_into().unwrap_or_default();
2049 Value::StyledText(i_slint_core::styled_text::color_to_styled_text(color))
2050 }
2051 BuiltinFunction::SetupSystemTrayIcon => {
2052 crate::popup::setup_system_tray_icon(ctx, arguments)
2053 }
2054 BuiltinFunction::StringIsFloat => Value::Bool(
2055 i_slint_core::string::string_to_float(to_string(ctx, &arguments[0]).as_str()).is_some(),
2056 ),
2057 BuiltinFunction::StringToFloat => Value::Number(
2058 i_slint_core::string::string_to_float(to_string(ctx, &arguments[0]).as_str())
2059 .unwrap_or_default() as f64,
2060 ),
2061 BuiltinFunction::StringIsEmpty => Value::Bool(to_string(ctx, &arguments[0]).is_empty()),
2062 BuiltinFunction::StringCharacterCount => Value::Number(
2063 unicode_segmentation::UnicodeSegmentation::graphemes(
2064 to_string(ctx, &arguments[0]).as_str(),
2065 true,
2066 )
2067 .count() as f64,
2068 ),
2069 BuiltinFunction::StringToLowercase => {
2070 Value::String(to_string(ctx, &arguments[0]).to_lowercase().into())
2071 }
2072 BuiltinFunction::StringToUppercase => {
2073 Value::String(to_string(ctx, &arguments[0]).to_uppercase().into())
2074 }
2075 BuiltinFunction::StringReplaceAll => {
2076 if arguments.len() != 3 {
2077 panic!("internal error: incorrect argument count to StringReplaceAll")
2078 }
2079
2080 if let (Value::String(s), Value::String(from), Value::String(to)) = (
2081 eval_expression(ctx, &arguments[0]),
2082 eval_expression(ctx, &arguments[1]),
2083 eval_expression(ctx, &arguments[2]),
2084 ) {
2085 Value::String(i_slint_core::string::shared_string_replace_all(
2086 &s,
2087 from.as_str(),
2088 to.as_str(),
2089 ))
2090 } else {
2091 panic!("Not all arguments are strings");
2092 }
2093 }
2094 BuiltinFunction::ColorRgbaStruct => {
2095 if let Value::Brush(brush) = eval_expression(ctx, &arguments[0]) {
2096 let color = brush.color();
2097 let values = [
2098 ("red".to_string(), Value::Number(color.red().into())),
2099 ("green".to_string(), Value::Number(color.green().into())),
2100 ("blue".to_string(), Value::Number(color.blue().into())),
2101 ("alpha".to_string(), Value::Number(color.alpha().into())),
2102 ]
2103 .into_iter()
2104 .collect();
2105 Value::Struct(values)
2106 } else {
2107 Value::Void
2108 }
2109 }
2110 BuiltinFunction::ColorHsvaStruct => {
2111 if let Value::Brush(brush) = eval_expression(ctx, &arguments[0]) {
2112 let color = brush.color().to_hsva();
2113 let values = [
2114 ("hue".to_string(), Value::Number(color.hue.into())),
2115 ("saturation".to_string(), Value::Number(color.saturation.into())),
2116 ("value".to_string(), Value::Number(color.value.into())),
2117 ("alpha".to_string(), Value::Number(color.alpha.into())),
2118 ]
2119 .into_iter()
2120 .collect();
2121 Value::Struct(values)
2122 } else {
2123 Value::Void
2124 }
2125 }
2126 BuiltinFunction::ColorOklchStruct => {
2127 if let Value::Brush(brush) = eval_expression(ctx, &arguments[0]) {
2128 let color = brush.color().to_oklch();
2129 let values = [
2130 ("lightness".to_string(), Value::Number(color.lightness.into())),
2131 ("chroma".to_string(), Value::Number(color.chroma.into())),
2132 ("hue".to_string(), Value::Number(color.hue.into())),
2133 ("alpha".to_string(), Value::Number(color.alpha.into())),
2134 ]
2135 .into_iter()
2136 .collect();
2137 Value::Struct(values)
2138 } else {
2139 Value::Void
2140 }
2141 }
2142 BuiltinFunction::ColorBrighter => {
2143 if let Value::Brush(brush) = eval_expression(ctx, &arguments[0]) {
2144 brush.brighter(to_num(ctx, &arguments[1]) as f32).into()
2145 } else {
2146 Value::Void
2147 }
2148 }
2149 BuiltinFunction::ColorDarker => {
2150 if let Value::Brush(brush) = eval_expression(ctx, &arguments[0]) {
2151 brush.darker(to_num(ctx, &arguments[1]) as f32).into()
2152 } else {
2153 Value::Void
2154 }
2155 }
2156 BuiltinFunction::ColorTransparentize => {
2157 if let Value::Brush(brush) = eval_expression(ctx, &arguments[0]) {
2158 brush.transparentize(to_num(ctx, &arguments[1]) as f32).into()
2159 } else {
2160 Value::Void
2161 }
2162 }
2163 BuiltinFunction::ColorWithAlpha => {
2164 if let Value::Brush(brush) = eval_expression(ctx, &arguments[0]) {
2165 brush.with_alpha(to_num(ctx, &arguments[1]) as f32).into()
2166 } else {
2167 Value::Void
2168 }
2169 }
2170 BuiltinFunction::ColorMix => {
2171 let a = eval_expression(ctx, &arguments[0]);
2172 let b = eval_expression(ctx, &arguments[1]);
2173 let factor = to_num(ctx, &arguments[2]) as f32;
2174 if let (
2175 Value::Brush(i_slint_core::Brush::SolidColor(ca)),
2176 Value::Brush(i_slint_core::Brush::SolidColor(cb)),
2177 ) = (a, b)
2178 {
2179 ca.mix(&cb, factor).into()
2180 } else {
2181 Value::Void
2182 }
2183 }
2184 BuiltinFunction::ArrayPush => {
2185 if arguments.len() != 2 {
2186 panic!("internal error: incorrect argument count to ArrayPush")
2187 }
2188
2189 let model = match eval_expression(ctx, &arguments[0]) {
2190 Value::Model(m) => m,
2191 _ => panic!("First argument not an array: {:?}", arguments[0]),
2192 };
2193 let value = eval_expression(ctx, &arguments[1]);
2194
2195 i_slint_core::model::report_model_error(
2196 "push",
2197 log_message_location(source_location),
2198 model.push_row(value),
2199 );
2200
2201 Value::Void
2202 }
2203 BuiltinFunction::ArrayRemove => {
2204 if arguments.len() != 2 {
2205 panic!("internal error: incorrect argument count to ArrayRemove")
2206 }
2207
2208 let model = match eval_expression(ctx, &arguments[0]) {
2209 Value::Model(m) => m,
2210 _ => panic!("First argument not an array: {:?}", arguments[0]),
2211 };
2212 let index = match eval_expression(ctx, &arguments[1]) {
2213 Value::Number(i) => i,
2214 _ => panic!("Second argument not an integer: {:?}", arguments[1]),
2215 };
2216
2217 let result = match usize::try_from(index as i64) {
2218 Ok(index) => model.remove_row(index),
2219 Err(_) => Err(i_slint_core::model::ModelError::out_of_bounds(model.row_count())),
2220 };
2221 i_slint_core::model::report_model_error(
2222 "remove",
2223 log_message_location(source_location),
2224 result,
2225 );
2226
2227 Value::Void
2228 }
2229
2230 BuiltinFunction::ArrayInsert => {
2231 if arguments.len() != 3 {
2232 panic!("internal error: incorrect argument count to ArrayInsert")
2233 }
2234
2235 let model = match eval_expression(ctx, &arguments[0]) {
2236 Value::Model(m) => m,
2237 _ => panic!("First argument not an array: {:?}", arguments[0]),
2238 };
2239 let index = match eval_expression(ctx, &arguments[1]) {
2240 Value::Number(i) => i,
2241 _ => panic!("Second argument not an integer: {:?}", arguments[1]),
2242 };
2243
2244 let value = eval_expression(ctx, &arguments[2]);
2245 let result = match usize::try_from(index as i64) {
2246 Ok(index) => model.insert_row(index, value),
2247 Err(_) => Err(i_slint_core::model::ModelError::out_of_bounds(model.row_count())),
2248 };
2249 i_slint_core::model::report_model_error(
2250 "insert",
2251 log_message_location(source_location),
2252 result,
2253 );
2254
2255 Value::Void
2256 }
2257 BuiltinFunction::Rgb => {
2258 let r: i32 = eval_expression(ctx, &arguments[0]).try_into().unwrap_or(0);
2259 let g: i32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or(0);
2260 let b: i32 = eval_expression(ctx, &arguments[2]).try_into().unwrap_or(0);
2261 let a: f32 = eval_expression(ctx, &arguments[3]).try_into().unwrap_or(1.0);
2262 let r: u8 = r.clamp(0, 255) as u8;
2263 let g: u8 = g.clamp(0, 255) as u8;
2264 let b: u8 = b.clamp(0, 255) as u8;
2265 let a: u8 = (255. * a).clamp(0., 255.) as u8;
2266 Value::Brush(i_slint_core::Brush::SolidColor(i_slint_core::Color::from_argb_u8(
2267 a, r, g, b,
2268 )))
2269 }
2270 BuiltinFunction::Hsv => {
2271 let h: f32 = eval_expression(ctx, &arguments[0]).try_into().unwrap_or(0.0);
2272 let s: f32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or(0.0);
2273 let v: f32 = eval_expression(ctx, &arguments[2]).try_into().unwrap_or(0.0);
2274 let a: f32 = eval_expression(ctx, &arguments[3]).try_into().unwrap_or(1.0);
2275 let a = a.clamp(0., 1.);
2276 Value::Brush(i_slint_core::Brush::SolidColor(i_slint_core::Color::from_hsva(
2277 h, s, v, a,
2278 )))
2279 }
2280 BuiltinFunction::Oklch => {
2281 let l: f32 = eval_expression(ctx, &arguments[0]).try_into().unwrap_or(0.0);
2282 let c: f32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or(0.0);
2283 let h: f32 = eval_expression(ctx, &arguments[2]).try_into().unwrap_or(0.0);
2284 let a: f32 = eval_expression(ctx, &arguments[3]).try_into().unwrap_or(1.0);
2285 Value::Brush(i_slint_core::Brush::SolidColor(i_slint_core::Color::from_oklch(
2286 l.clamp(0.0, 1.0),
2287 c,
2288 h,
2289 a.clamp(0.0, 1.0),
2290 )))
2291 }
2292 BuiltinFunction::AnimationTick => {
2293 Value::Number(i_slint_core::animations::animation_tick() as f64)
2294 }
2295 BuiltinFunction::GetWindowScaleFactor => {
2296 let factor = root_instance(ctx)
2297 .and_then(|inst| inst.window_adapter_or_default())
2298 .map(|adapter| {
2299 i_slint_core::window::WindowInner::from_pub(adapter.window()).scale_factor()
2300 as f64
2301 })
2302 .unwrap_or(1.0);
2303 Value::Number(factor)
2304 }
2305 BuiltinFunction::GetWindowDefaultFontSize => {
2306 let size = root_instance(ctx)
2312 .map(|inst| {
2313 i_slint_core::items::WindowItem::resolved_default_font_size(
2314 vtable::VRc::into_dyn(inst),
2315 )
2316 .get() as f64
2317 })
2318 .unwrap_or(12.0);
2319 Value::Number(size)
2320 }
2321 BuiltinFunction::DetectOperatingSystem => i_slint_core::detect_operating_system().into(),
2322 BuiltinFunction::Use24HourFormat => {
2323 Value::Bool(i_slint_core::date_time::use_24_hour_format())
2324 }
2325 BuiltinFunction::ColorScheme => {
2326 let scheme = root_instance(ctx)
2327 .map(vtable::VRc::into_dyn)
2328 .and_then(|root| {
2329 i_slint_core::window::context_for_root(&root)
2330 .map(|ctx| ctx.color_scheme(Some(&root)))
2331 })
2332 .unwrap_or(i_slint_core::items::ColorScheme::Unknown);
2333 scheme.into()
2334 }
2335 BuiltinFunction::AccentColor => {
2336 let color = root_instance(ctx)
2337 .map(vtable::VRc::into_dyn)
2338 .map(|root| i_slint_core::window::accent_color(&root))
2339 .unwrap_or_default();
2340 Value::Brush(i_slint_core::Brush::SolidColor(color))
2341 }
2342 BuiltinFunction::SupportsNativeMenuBar => {
2343 let supports = find_window_adapter(ctx).is_some_and(|a| {
2344 a.internal(i_slint_core::InternalToken)
2345 .is_some_and(|x| x.supports_native_menu_bar())
2346 });
2347 Value::Bool(supports)
2348 }
2349 BuiltinFunction::TextInputFocused => {
2350 let focused = ctx
2351 .current
2352 .as_ref()
2353 .and_then(|c| c.root.get())
2354 .and_then(|w| w.upgrade())
2355 .and_then(|inst| inst.window_adapter_or_default())
2356 .map(|adapter| {
2357 i_slint_core::window::WindowInner::from_pub(adapter.window())
2358 .text_input_focused()
2359 })
2360 .unwrap_or(false);
2361 Value::Bool(focused)
2362 }
2363 BuiltinFunction::SetTextInputFocused => {
2364 let value = arguments
2365 .first()
2366 .map(|e| eval_expression(ctx, e))
2367 .and_then(|v| bool::try_from(v).ok())
2368 .unwrap_or(false);
2369 if let Some(adapter) = ctx
2370 .current
2371 .as_ref()
2372 .and_then(|c| c.root.get())
2373 .and_then(|w| w.upgrade())
2374 .and_then(|inst| inst.window_adapter_or_default())
2375 {
2376 i_slint_core::window::WindowInner::from_pub(adapter.window())
2377 .set_text_input_focused(value);
2378 }
2379 Value::Void
2380 }
2381 BuiltinFunction::UpdateTimers => {
2382 Value::Void
2385 }
2386 BuiltinFunction::RestartTimer => {
2387 if let [
2392 Expression::PropertyReference(MemberReference::Relative {
2393 parent_level,
2394 local_reference,
2395 }),
2396 ] = arguments
2397 && let LocalMemberIndex::Timer(timer_idx) = &local_reference.reference
2398 && let Some(instance) = try_walk_to(ctx, *parent_level, local_reference)
2399 && let Some(timer) = instance.timers.get(usize::from(*timer_idx))
2400 {
2401 timer.restart();
2402 }
2403 Value::Void
2404 }
2405 BuiltinFunction::KeysToString => {
2406 let v = arguments.first().map(|e| eval_expression(ctx, e));
2407 if let Some(Value::Keys(keys)) = v {
2408 Value::String(keys.to_string().into())
2409 } else {
2410 Value::String(Default::default())
2411 }
2412 }
2413 BuiltinFunction::SetSelectionOffsets => {
2414 use i_slint_core::items::TextInput;
2416 let [Expression::PropertyReference(mr), anchor_expr, focus_expr] = arguments else {
2417 return Value::Void;
2418 };
2419 let anchor: i32 = eval_expression(ctx, anchor_expr).try_into().unwrap_or(0);
2420 let focus: i32 = eval_expression(ctx, focus_expr).try_into().unwrap_or(0);
2421 let Some((parent_inst, flat_idx)) = resolve_item_rc_from_ref(ctx, mr) else {
2422 return Value::Void;
2423 };
2424 let Some(adapter) = parent_inst.window_adapter_or_default() else {
2425 return Value::Void;
2426 };
2427 let parent_dyn = vtable::VRc::into_dyn(parent_inst);
2428 let item_rc = i_slint_core::items::ItemRc::new(parent_dyn, flat_idx as u32);
2429 if let Some(text_input) = vtable::VRef::downcast_pin::<TextInput>(item_rc.borrow()) {
2430 text_input.set_selection_offsets(&adapter, &item_rc, anchor, focus);
2431 }
2432 Value::Void
2433 }
2434 BuiltinFunction::RegisterCustomFontByPath => {
2435 if let Value::String(s) = eval_expression(ctx, &arguments[0])
2436 && let Some(root) = find_root_instance(ctx)
2437 {
2438 let result =
2441 root.try_window_adapter().map_err(|e| e.to_string()).and_then(|adapter| {
2442 adapter
2443 .renderer()
2444 .register_font_from_path(&std::path::PathBuf::from(s.as_str()))
2445 .map_err(|e| format!("Cannot load custom font {}: {e}", s.as_str()))
2446 });
2447 if let Err(err) = result {
2448 i_slint_core::debug_log!("{err}");
2449 }
2450 }
2451 Value::Void
2452 }
2453 BuiltinFunction::SetupMenuBar => crate::popup::setup_menubar(ctx, arguments),
2454 BuiltinFunction::ItemFontMetrics => {
2455 if let Some(Expression::PropertyReference(mr)) = arguments.first()
2456 && let Some((inst, flat_idx)) = resolve_item_rc_from_ref(ctx, mr)
2457 && let Some(adapter) = inst.window_adapter_or_default()
2458 {
2459 let item_rc =
2460 i_slint_core::items::ItemRc::new(vtable::VRc::into_dyn(inst), flat_idx as u32);
2461 let metrics = i_slint_core::items::slint_text_item_fontmetrics(
2462 &adapter,
2463 item_rc.borrow(),
2464 &item_rc,
2465 );
2466 return metrics.into();
2467 }
2468 i_slint_core::items::FontMetrics::default().into()
2469 }
2470 BuiltinFunction::ItemAbsolutePosition => {
2471 if let Some(Expression::PropertyReference(mr)) = arguments.first()
2472 && let Some((inst, flat_idx)) = resolve_item_rc_from_ref(ctx, mr)
2473 {
2474 let item_rc =
2475 i_slint_core::items::ItemRc::new(vtable::VRc::into_dyn(inst), flat_idx as u32);
2476 return item_rc.map_to_window(item_rc.geometry().origin).to_untyped().into();
2480 }
2481 i_slint_core::api::LogicalPosition::default().into()
2482 }
2483 BuiltinFunction::PathPointAt => {
2484 if let Some(Expression::PropertyReference(mr)) = arguments.first()
2485 && let Some((inst, flat_idx)) = resolve_item_rc_from_ref(ctx, mr)
2486 {
2487 let item_rc =
2488 i_slint_core::items::ItemRc::new(vtable::VRc::into_dyn(inst), flat_idx as u32);
2489 let t: f32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or_default();
2490 return item_rc
2491 .downcast::<i_slint_core::items::Path>()
2492 .unwrap()
2493 .as_pin_ref()
2494 .point_at(&item_rc, t)
2495 .to_untyped()
2496 .into();
2497 }
2498 panic!("internal error: argument to PathPointAt must be an element")
2499 }
2500 BuiltinFunction::PathAngleAt => {
2501 if let Some(Expression::PropertyReference(mr)) = arguments.first()
2502 && let Some((inst, flat_idx)) = resolve_item_rc_from_ref(ctx, mr)
2503 {
2504 let item_rc =
2505 i_slint_core::items::ItemRc::new(vtable::VRc::into_dyn(inst), flat_idx as u32);
2506 let t: f32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or_default();
2507 return item_rc
2508 .downcast::<i_slint_core::items::Path>()
2509 .unwrap()
2510 .as_pin_ref()
2511 .angle_at(&item_rc, t)
2512 .into();
2513 }
2514 panic!("internal error: argument to PathAngleAt must be an element")
2515 }
2516 BuiltinFunction::ArrayAny | BuiltinFunction::ArrayAll => {
2517 let is_all = matches!(f, BuiltinFunction::ArrayAll);
2518 let model: i_slint_core::model::ModelRc<Value> =
2519 eval_expression(ctx, &arguments[0]).try_into().unwrap();
2520 let Expression::Closure { arg_name, expression } = &arguments[1] else {
2521 panic!("internal error: Array.any/all expects a closure as second argument")
2522 };
2523 let mut predicate =
2524 |row_value| eval_array_row_predicate(arg_name, expression, ctx, row_value);
2525 Value::Bool(if is_all {
2526 i_slint_core::model::model_all(&model, &mut predicate)
2527 } else {
2528 i_slint_core::model::model_any(&model, &mut predicate)
2529 })
2530 }
2531 BuiltinFunction::ArrayFindIndex => {
2532 let model: i_slint_core::model::ModelRc<Value> =
2533 eval_expression(ctx, &arguments[0]).try_into().unwrap();
2534 let Expression::Closure { arg_name, expression } = &arguments[1] else {
2535 panic!("internal error: Array.find-index expects a closure as second argument")
2536 };
2537 Value::Number(i_slint_core::model::model_find_index(&model, |row_value| {
2538 eval_array_row_predicate(arg_name, expression, ctx, row_value)
2539 }) as f64)
2540 }
2541 BuiltinFunction::ImplicitLayoutInfo(orient) => {
2542 let constraint: f32 = arguments
2546 .get(1)
2547 .map(|e| eval_expression(ctx, e).try_into().unwrap_or(-1.))
2548 .unwrap_or(-1.);
2549 if let Some(Expression::PropertyReference(mr)) = arguments.first()
2550 && let Some((inst, flat_idx)) = resolve_item_rc_from_ref(ctx, mr)
2551 && let Some(adapter) = inst.window_adapter_or_default()
2552 {
2553 let item_rc =
2554 i_slint_core::items::ItemRc::new(vtable::VRc::into_dyn(inst), flat_idx as u32);
2555 return item_rc
2556 .borrow()
2557 .as_ref()
2558 .layout_info(
2559 llr_to_core_orientation(orient),
2560 constraint as _,
2561 &adapter,
2562 &item_rc,
2563 )
2564 .into();
2565 }
2566 i_slint_core::layout::LayoutInfo::default().into()
2567 }
2568 BuiltinFunction::Debug => {
2569 use i_slint_core::debug_log::*;
2570 let msg = to_string(ctx, &arguments[0]);
2571 let root = ctx
2572 .current
2573 .as_ref()
2574 .and_then(|c| c.root.get())
2575 .and_then(|w| w.upgrade())
2576 .map(vtable::VRc::into_dyn);
2577 if let Some(context) = root.as_ref().and_then(i_slint_core::window::context_for_root) {
2578 context.dispatch_log_message(LogMessage::new(
2579 LogMessageSource::SlintCode,
2580 log_message_location(source_location),
2581 format_args!("{msg}"),
2582 ));
2583 } else {
2584 log_message(LogMessage::new(
2585 LogMessageSource::SlintCode,
2586 log_message_location(source_location),
2587 format_args!("{msg}"),
2588 ));
2589 }
2590 Value::Void
2591 }
2592 BuiltinFunction::ArrayLength => match eval_expression(ctx, &arguments[0]) {
2593 Value::Model(m) => {
2596 m.model_tracker().track_row_count_changes();
2597 Value::Number(m.row_count() as f64)
2598 }
2599 _ => Value::Number(0.),
2600 },
2601 BuiltinFunction::ImageSize => {
2602 if let Value::Image(img) = eval_expression(ctx, &arguments[0]) {
2603 let size = img.size();
2604 let mut s = crate::api::Struct::default();
2605 s.set_field("width".to_string(), Value::Number(size.width as f64));
2606 s.set_field("height".to_string(), Value::Number(size.height as f64));
2607 Value::Struct(s)
2608 } else {
2609 Value::Void
2610 }
2611 }
2612 BuiltinFunction::ParseMarkdown => {
2613 let format_string: SharedString =
2614 eval_expression(ctx, &arguments[0]).try_into().unwrap_or_default();
2615 let args = eval_expression(ctx, &arguments[1]);
2616 let args: Vec<i_slint_core::styled_text::StyledText> = if let Value::Model(m) = args {
2617 (0..m.row_count())
2618 .filter_map(|i| match m.row_data(i)? {
2619 Value::StyledText(t) => Some(t),
2620 _ => None,
2621 })
2622 .collect()
2623 } else {
2624 Vec::new()
2625 };
2626 Value::StyledText(i_slint_core::styled_text::parse_markdown(&format_string, &args))
2627 }
2628 BuiltinFunction::StringToStyledText => {
2629 let string: SharedString =
2630 eval_expression(ctx, &arguments[0]).try_into().unwrap_or_default();
2631 Value::StyledText(i_slint_core::styled_text::string_to_styled_text(string.to_string()))
2632 }
2633 BuiltinFunction::Translate => {
2634 let original: SharedString = to_string(ctx, &arguments[0]);
2635 let context: SharedString = to_string(ctx, &arguments[1]);
2636 let domain: SharedString = to_string(ctx, &arguments[2]);
2637 let args = eval_expression(ctx, &arguments[3]);
2638 let Value::Model(args) = args else {
2639 return Value::String(original);
2640 };
2641 let n: i32 = eval_expression(ctx, &arguments[4]).try_into().unwrap_or(0);
2642 let plural: SharedString = to_string(ctx, &arguments[5]);
2643 Value::String(i_slint_core::translations::translate(
2644 &original,
2645 &context,
2646 &domain,
2647 &StringModelWrapper(args),
2648 n,
2649 &plural,
2650 ))
2651 }
2652 BuiltinFunction::ShowPopupWindow => crate::popup::show_popup_window(ctx, arguments),
2653 BuiltinFunction::ClosePopupWindow => crate::popup::close_popup_window(ctx, arguments),
2654 BuiltinFunction::SetFocusItem => {
2655 if let Some(Expression::PropertyReference(mr)) = arguments.first()
2656 && let Some((inst, flat_idx)) = resolve_item_rc_from_ref(ctx, mr)
2657 && let Some(adapter) = find_window_adapter(ctx)
2658 {
2659 let dyn_rc = vtable::VRc::into_dyn(inst);
2660 let item_rc = i_slint_core::items::ItemRc::new(dyn_rc, flat_idx as u32);
2661 i_slint_core::window::WindowInner::from_pub(adapter.window()).set_focus_item(
2662 &item_rc,
2663 true,
2664 i_slint_core::input::FocusReason::Programmatic,
2665 );
2666 }
2667 Value::Void
2668 }
2669 BuiltinFunction::ClearFocusItem => {
2670 if let Some(Expression::PropertyReference(mr)) = arguments.first()
2671 && let Some((inst, flat_idx)) = resolve_item_rc_from_ref(ctx, mr)
2672 && let Some(adapter) = find_window_adapter(ctx)
2673 {
2674 let dyn_rc = vtable::VRc::into_dyn(inst);
2675 let item_rc = i_slint_core::items::ItemRc::new(dyn_rc, flat_idx as u32);
2676 i_slint_core::window::WindowInner::from_pub(adapter.window()).set_focus_item(
2677 &item_rc,
2678 false,
2679 i_slint_core::input::FocusReason::Programmatic,
2680 );
2681 }
2682 Value::Void
2683 }
2684 BuiltinFunction::MonthDayCount => {
2685 let m: u32 = eval_expression(ctx, &arguments[0]).try_into().unwrap_or(0);
2686 let y: i32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or(0);
2687 Value::Number(i_slint_core::date_time::month_day_count(m, y).unwrap_or(0) as f64)
2688 }
2689 BuiltinFunction::MonthOffset => {
2690 let m: u32 = eval_expression(ctx, &arguments[0]).try_into().unwrap_or(0);
2691 let y: i32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or(0);
2692 Value::Number(i_slint_core::date_time::month_offset(m, y) as f64)
2693 }
2694 BuiltinFunction::FormatDate => {
2695 let f: SharedString = to_string(ctx, &arguments[0]);
2696 let d: u32 = eval_expression(ctx, &arguments[1]).try_into().unwrap_or(0);
2697 let m: u32 = eval_expression(ctx, &arguments[2]).try_into().unwrap_or(0);
2698 let y: i32 = eval_expression(ctx, &arguments[3]).try_into().unwrap_or(0);
2699 Value::String(i_slint_core::date_time::format_date(&f, d, m, y))
2700 }
2701 BuiltinFunction::DateNow => {
2702 Value::Model(i_slint_core::model::ModelRc::new(i_slint_core::model::VecModel::from(
2703 i_slint_core::date_time::date_now()
2704 .into_iter()
2705 .map(|x| Value::Number(x as f64))
2706 .collect::<Vec<_>>(),
2707 )))
2708 }
2709 BuiltinFunction::ValidDate => {
2710 let d: SharedString = to_string(ctx, &arguments[0]);
2711 let f: SharedString = to_string(ctx, &arguments[1]);
2712 Value::Bool(i_slint_core::date_time::parse_date(d.as_str(), f.as_str()).is_some())
2713 }
2714 BuiltinFunction::ParseDate => {
2715 let d: SharedString = to_string(ctx, &arguments[0]);
2716 let f: SharedString = to_string(ctx, &arguments[1]);
2717 Value::Model(i_slint_core::model::ModelRc::new(i_slint_core::model::VecModel::from(
2718 i_slint_core::date_time::parse_date(d.as_str(), f.as_str())
2719 .map(|v| v.into_iter().map(|x| Value::Number(x as f64)).collect::<Vec<_>>())
2720 .unwrap_or_default(),
2721 )))
2722 }
2723 BuiltinFunction::ShowPopupMenu | BuiltinFunction::ShowPopupMenuInternal => {
2724 crate::popup::show_popup_menu(ctx, arguments)
2725 }
2726 BuiltinFunction::OpenUrl => {
2727 let url = to_string(ctx, &arguments[0]);
2728 let result = find_window_adapter(ctx)
2729 .map(|adapter| i_slint_core::open_url(&url, adapter.window()).is_ok())
2730 .unwrap_or(false);
2731 Value::Bool(result)
2732 }
2733 BuiltinFunction::RegisterCustomFontByMemory | BuiltinFunction::RegisterBitmapFont => {
2734 Value::Void
2736 }
2737 BuiltinFunction::StartTimer | BuiltinFunction::StopTimer => {
2738 Value::Void
2740 }
2741 }
2742}
2743
2744pub(crate) fn resolve_item_rc_from_ref(
2748 ctx: &EvalContext,
2749 mr: &MemberReference,
2750) -> Option<(vtable::VRc<i_slint_core::item_tree::ItemTreeVTable, crate::instance::Instance>, usize)>
2751{
2752 let MemberReference::Relative { parent_level, local_reference } = mr else { return None };
2753 let LocalMemberIndex::Native { item_index, .. } = &local_reference.reference else {
2754 return None;
2755 };
2756 let owner = try_walk_to(ctx, *parent_level, local_reference)?;
2757 let parent_inst = owner.root.get().and_then(|w| w.upgrade())?;
2758 let full_path = crate::item_tree_vtable::sub_component_path_of(&owner, &parent_inst);
2759 let flat_idx = find_flat_item_index(&parent_inst.item_table, &full_path, *item_index)?;
2760 Some((parent_inst, flat_idx))
2761}
2762
2763pub(crate) fn find_root_instance(
2767 ctx: &EvalContext,
2768) -> Option<vtable::VRc<i_slint_core::item_tree::ItemTreeVTable, crate::instance::Instance>> {
2769 let current = ctx.current.as_ref()?;
2770 let mut sub = current.clone();
2771 loop {
2772 if let Some(root) = sub.root.get()
2773 && let Some(inst) = root.upgrade()
2774 && inst.public_component_index.is_some()
2775 {
2776 return Some(inst);
2777 }
2778 let parent = sub.parent.upgrade()?;
2779 sub = Pin::new(parent);
2780 }
2781}
2782
2783pub(crate) fn find_window_adapter(
2785 ctx: &EvalContext,
2786) -> Option<i_slint_core::window::WindowAdapterRc> {
2787 find_root_instance(ctx)?.window_adapter_or_default()
2788}
2789
2790fn call_item_member_function(ctx: &EvalContext, function: &MemberReference) -> Value {
2794 use i_slint_core::items::{ContextMenu, SwipeGestureHandler, TextInput, WindowItem};
2795 let MemberReference::Relative { local_reference, .. } = function else {
2796 return Value::Void;
2797 };
2798 let LocalMemberIndex::Native { prop_name, .. } = &local_reference.reference else {
2799 return Value::Void;
2800 };
2801 let Some((parent_inst, flat_idx)) = resolve_item_rc_from_ref(ctx, function) else {
2802 return Value::Void;
2803 };
2804 let Some(adapter) = parent_inst.window_adapter_or_default() else { return Value::Void };
2805 let parent_dyn = vtable::VRc::into_dyn(parent_inst);
2806 let item_rc = i_slint_core::items::ItemRc::new(parent_dyn, flat_idx as u32);
2807 let item_ref = item_rc.borrow();
2808
2809 macro_rules! dispatch {
2812 ($item:expr, $name:expr; $($slint_name:literal => $rust_method:ident $(=> $into:ty)?),* $(,)?) => {
2813 match $name {
2814 $(
2815 $slint_name => {
2816 let res = $item.$rust_method(&adapter, &item_rc);
2817 $(let res: $into = res.into();)?
2818 return res.into();
2819 }
2820 )*
2821 _ => {}
2822 }
2823 };
2824 }
2825
2826 if let Some(text_input) = vtable::VRef::downcast_pin::<TextInput>(item_ref) {
2827 dispatch!(text_input, prop_name.as_str();
2828 "select-all" => select_all => (),
2829 "clear-selection" => clear_selection => (),
2830 "select-word" => select_word => (),
2831 "cut" => cut => (),
2832 "copy" => copy => (),
2833 "paste" => paste => (),
2834 "undo" => undo => (),
2835 "redo" => redo => (),
2836 );
2837 }
2838 if let Some(swipe) = vtable::VRef::downcast_pin::<SwipeGestureHandler>(item_rc.borrow()) {
2839 dispatch!(swipe, prop_name.as_str();
2840 "cancel" => cancel => (),
2841 );
2842 }
2843 if let Some(menu) = vtable::VRef::downcast_pin::<ContextMenu>(item_rc.borrow()) {
2844 dispatch!(menu, prop_name.as_str();
2845 "close" => close => (),
2846 "is-open" => is_open,
2847 );
2848 }
2849 if let Some(window) = vtable::VRef::downcast_pin::<WindowItem>(item_rc.borrow()) {
2850 match prop_name.as_str() {
2851 "hide" => {
2852 window.hide(&adapter, &item_rc);
2853 return Value::Void;
2854 }
2855 "close" => return Value::Bool(window.close(&adapter, &item_rc)),
2856 _ => {}
2857 }
2858 }
2859 unimplemented!("ItemMemberFunctionCall `{prop_name}`")
2860}