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