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