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