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