1use crate::api::{SetPropertyError, Struct, Value};
5use crate::dynamic_item_tree::{CallbackHandler, InstanceRef};
6use core::pin::Pin;
7use corelib::graphics::{GradientStop, LinearGradientBrush, PathElement, RadialGradientBrush};
8use corelib::items::{ColorScheme, ItemRef, MenuEntry, PropertyAnimation};
9use corelib::menus::{Menu, MenuFromItemTree, MenuVTable};
10use corelib::model::{Model, ModelExt, ModelRc, VecModel};
11use corelib::rtti::AnimatedBindingKind;
12use corelib::window::WindowInner;
13use corelib::{Brush, Color, PathData, SharedString, SharedVector};
14use i_slint_compiler::expression_tree::{
15 BuiltinFunction, Callable, EasingCurve, Expression, MinMaxOp, Path as ExprPath,
16 PathElement as ExprPathElement,
17};
18use i_slint_compiler::langtype::Type;
19use i_slint_compiler::namedreference::NamedReference;
20use i_slint_compiler::object_tree::ElementRc;
21use i_slint_core as corelib;
22use i_slint_core::input::FocusReason;
23use i_slint_core::items::ItemRc;
24use smol_str::SmolStr;
25use std::collections::HashMap;
26use std::rc::Rc;
27
28pub trait ErasedPropertyInfo {
29 fn get(&self, item: Pin<ItemRef>) -> Value;
30 fn set(
31 &self,
32 item: Pin<ItemRef>,
33 value: Value,
34 animation: Option<PropertyAnimation>,
35 ) -> Result<(), ()>;
36 fn set_binding(
37 &self,
38 item: Pin<ItemRef>,
39 binding: Box<dyn Fn() -> Value>,
40 animation: AnimatedBindingKind,
41 );
42 fn offset(&self) -> usize;
43
44 unsafe fn link_two_ways(&self, item: Pin<ItemRef>, property2: *const ());
47}
48
49impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedPropertyInfo
50 for &'static dyn corelib::rtti::PropertyInfo<Item, Value>
51{
52 fn get(&self, item: Pin<ItemRef>) -> Value {
53 (*self).get(ItemRef::downcast_pin(item).unwrap()).unwrap()
54 }
55 fn set(
56 &self,
57 item: Pin<ItemRef>,
58 value: Value,
59 animation: Option<PropertyAnimation>,
60 ) -> Result<(), ()> {
61 (*self).set(ItemRef::downcast_pin(item).unwrap(), value, animation)
62 }
63 fn set_binding(
64 &self,
65 item: Pin<ItemRef>,
66 binding: Box<dyn Fn() -> Value>,
67 animation: AnimatedBindingKind,
68 ) {
69 (*self).set_binding(ItemRef::downcast_pin(item).unwrap(), binding, animation).unwrap();
70 }
71 fn offset(&self) -> usize {
72 (*self).offset()
73 }
74 unsafe fn link_two_ways(&self, item: Pin<ItemRef>, property2: *const ()) {
75 (*self).link_two_ways(ItemRef::downcast_pin(item).unwrap(), property2)
77 }
78}
79
80pub trait ErasedCallbackInfo {
81 fn call(&self, item: Pin<ItemRef>, args: &[Value]) -> Value;
82 fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>);
83}
84
85impl<Item: vtable::HasStaticVTable<corelib::items::ItemVTable>> ErasedCallbackInfo
86 for &'static dyn corelib::rtti::CallbackInfo<Item, Value>
87{
88 fn call(&self, item: Pin<ItemRef>, args: &[Value]) -> Value {
89 (*self).call(ItemRef::downcast_pin(item).unwrap(), args).unwrap()
90 }
91
92 fn set_handler(&self, item: Pin<ItemRef>, handler: Box<dyn Fn(&[Value]) -> Value>) {
93 (*self).set_handler(ItemRef::downcast_pin(item).unwrap(), handler).unwrap()
94 }
95}
96
97impl corelib::rtti::ValueType for Value {}
98
99#[derive(Clone)]
100pub(crate) enum ComponentInstance<'a, 'id> {
101 InstanceRef(InstanceRef<'a, 'id>),
102 GlobalComponent(Pin<Rc<dyn crate::global_component::GlobalComponent>>),
103}
104
105pub struct EvalLocalContext<'a, 'id> {
107 local_variables: HashMap<SmolStr, Value>,
108 function_arguments: Vec<Value>,
109 pub(crate) component_instance: InstanceRef<'a, 'id>,
110 return_value: Option<Value>,
112}
113
114impl<'a, 'id> EvalLocalContext<'a, 'id> {
115 pub fn from_component_instance(component: InstanceRef<'a, 'id>) -> Self {
116 Self {
117 local_variables: Default::default(),
118 function_arguments: Default::default(),
119 component_instance: component,
120 return_value: None,
121 }
122 }
123
124 pub fn from_function_arguments(
126 component: InstanceRef<'a, 'id>,
127 function_arguments: Vec<Value>,
128 ) -> Self {
129 Self {
130 component_instance: component,
131 function_arguments,
132 local_variables: Default::default(),
133 return_value: None,
134 }
135 }
136}
137
138pub fn eval_expression(expression: &Expression, local_context: &mut EvalLocalContext) -> Value {
140 if let Some(r) = &local_context.return_value {
141 return r.clone();
142 }
143 match expression {
144 Expression::Invalid => panic!("invalid expression while evaluating"),
145 Expression::Uncompiled(_) => panic!("uncompiled expression while evaluating"),
146 Expression::StringLiteral(s) => Value::String(s.as_str().into()),
147 Expression::NumberLiteral(n, unit) => Value::Number(unit.normalize(*n)),
148 Expression::BoolLiteral(b) => Value::Bool(*b),
149 Expression::ElementReference(_) => todo!("Element references are only supported in the context of built-in function calls at the moment"),
150 Expression::PropertyReference(nr) => {
151 load_property_helper(&ComponentInstance::InstanceRef(local_context.component_instance), &nr.element(), nr.name()).unwrap()
152 }
153 Expression::RepeaterIndexReference { element } => load_property_helper(&ComponentInstance::InstanceRef(local_context.component_instance),
154 &element.upgrade().unwrap().borrow().base_type.as_component().root_element,
155 crate::dynamic_item_tree::SPECIAL_PROPERTY_INDEX,
156 )
157 .unwrap(),
158 Expression::RepeaterModelReference { element } => load_property_helper(&ComponentInstance::InstanceRef(local_context.component_instance),
159 &element.upgrade().unwrap().borrow().base_type.as_component().root_element,
160 crate::dynamic_item_tree::SPECIAL_PROPERTY_MODEL_DATA,
161 )
162 .unwrap(),
163 Expression::FunctionParameterReference { index, .. } => {
164 local_context.function_arguments[*index].clone()
165 }
166 Expression::StructFieldAccess { base, name } => {
167 if let Value::Struct(o) = eval_expression(base, local_context) {
168 o.get_field(name).cloned().unwrap_or(Value::Void)
169 } else {
170 Value::Void
171 }
172 }
173 Expression::ArrayIndex { array, index } => {
174 let array = eval_expression(array, local_context);
175 let index = eval_expression(index, local_context);
176 match (array, index) {
177 (Value::Model(model), Value::Number(index)) => {
178 model.row_data_tracked(index as isize as usize).unwrap_or_else(|| default_value_for_type(&expression.ty()))
179 }
180 _ => {
181 Value::Void
182 }
183 }
184 }
185 Expression::Cast { from, to } => {
186 let v = eval_expression(from, local_context);
187 match (v, to) {
188 (Value::Number(n), Type::Int32) => Value::Number(n.trunc()),
189 (Value::Number(n), Type::String) => {
190 Value::String(i_slint_core::string::shared_string_from_number(n))
191 }
192 (Value::Number(n), Type::Color) => Color::from_argb_encoded(n as u32).into(),
193 (Value::Brush(brush), Type::Color) => brush.color().into(),
194 (v, _) => v,
195 }
196 }
197 Expression::CodeBlock(sub) => {
198 let mut v = Value::Void;
199 for e in sub {
200 v = eval_expression(e, local_context);
201 if let Some(r) = &local_context.return_value {
202 return r.clone();
203 }
204 }
205 v
206 }
207 Expression::FunctionCall { function, arguments, source_location } => match &function {
208 Callable::Function(nr) => {
209 let is_item_member = nr.element().borrow().native_class().is_some_and(|n| n.properties.contains_key(nr.name()));
210 if is_item_member {
211 call_item_member_function(nr, local_context)
212 } else {
213 let args = arguments.iter().map(|e| eval_expression(e, local_context)).collect::<Vec<_>>();
214 call_function(&ComponentInstance::InstanceRef(local_context.component_instance), &nr.element(), nr.name(), args).unwrap()
215 }
216 }
217 Callable::Callback(nr) => {
218 let args = arguments.iter().map(|e| eval_expression(e, local_context)).collect::<Vec<_>>();
219 invoke_callback(&ComponentInstance::InstanceRef(local_context.component_instance), &nr.element(), nr.name(), &args).unwrap()
220 }
221 Callable::Builtin(f) => call_builtin_function(f.clone(), arguments, local_context, source_location),
222 }
223 Expression::SelfAssignment { lhs, rhs, op, .. } => {
224 let rhs = eval_expression(rhs, local_context);
225 eval_assignment(lhs, *op, rhs, local_context);
226 Value::Void
227 }
228 Expression::BinaryExpression { lhs, rhs, op } => {
229 let lhs = eval_expression(lhs, local_context);
230 let rhs = eval_expression(rhs, local_context);
231
232 match (op, lhs, rhs) {
233 ('+', Value::String(mut a), Value::String(b)) => { a.push_str(b.as_str()); Value::String(a) },
234 ('+', Value::Number(a), Value::Number(b)) => Value::Number(a + b),
235 ('+', a @ Value::Struct(_), b @ Value::Struct(_)) => {
236 let a : Option<corelib::layout::LayoutInfo> = a.try_into().ok();
237 let b : Option<corelib::layout::LayoutInfo> = b.try_into().ok();
238 if let (Some(a), Some(b)) = (a, b) {
239 a.merge(&b).into()
240 } else {
241 panic!("unsupported {a:?} {op} {b:?}");
242 }
243 }
244 ('-', Value::Number(a), Value::Number(b)) => Value::Number(a - b),
245 ('/', Value::Number(a), Value::Number(b)) => Value::Number(a / b),
246 ('*', Value::Number(a), Value::Number(b)) => Value::Number(a * b),
247 ('<', Value::Number(a), Value::Number(b)) => Value::Bool(a < b),
248 ('>', Value::Number(a), Value::Number(b)) => Value::Bool(a > b),
249 ('≤', Value::Number(a), Value::Number(b)) => Value::Bool(a <= b),
250 ('≥', Value::Number(a), Value::Number(b)) => Value::Bool(a >= b),
251 ('<', Value::String(a), Value::String(b)) => Value::Bool(a < b),
252 ('>', Value::String(a), Value::String(b)) => Value::Bool(a > b),
253 ('≤', Value::String(a), Value::String(b)) => Value::Bool(a <= b),
254 ('≥', Value::String(a), Value::String(b)) => Value::Bool(a >= b),
255 ('=', a, b) => Value::Bool(a == b),
256 ('!', a, b) => Value::Bool(a != b),
257 ('&', Value::Bool(a), Value::Bool(b)) => Value::Bool(a && b),
258 ('|', Value::Bool(a), Value::Bool(b)) => Value::Bool(a || b),
259 (op, lhs, rhs) => panic!("unsupported {lhs:?} {op} {rhs:?}"),
260 }
261 }
262 Expression::UnaryOp { sub, op } => {
263 let sub = eval_expression(sub, local_context);
264 match (sub, op) {
265 (Value::Number(a), '+') => Value::Number(a),
266 (Value::Number(a), '-') => Value::Number(-a),
267 (Value::Bool(a), '!') => Value::Bool(!a),
268 (sub, op) => panic!("unsupported {op} {sub:?}"),
269 }
270 }
271 Expression::ImageReference{ resource_ref, nine_slice, .. } => {
272 let mut image = match resource_ref {
273 i_slint_compiler::expression_tree::ImageReference::None => {
274 Ok(Default::default())
275 }
276 i_slint_compiler::expression_tree::ImageReference::AbsolutePath(path) => {
277 let path = std::path::Path::new(path);
278 if path.starts_with("builtin:/") {
279 i_slint_compiler::fileaccess::load_file(path).and_then(|virtual_file| virtual_file.builtin_contents).map(|virtual_file| {
280 let extension = path.extension().unwrap().to_str().unwrap();
281 corelib::graphics::load_image_from_embedded_data(
282 corelib::slice::Slice::from_slice(virtual_file),
283 corelib::slice::Slice::from_slice(extension.as_bytes())
284 )
285 }).ok_or_else(Default::default)
286 } else {
287 corelib::graphics::Image::load_from_path(path)
288 }
289 }
290 i_slint_compiler::expression_tree::ImageReference::EmbeddedData { .. } => {
291 todo!()
292 }
293 i_slint_compiler::expression_tree::ImageReference::EmbeddedTexture { .. } => {
294 todo!()
295 }
296 }.unwrap_or_else(|_| {
297 eprintln!("Could not load image {resource_ref:?}" );
298 Default::default()
299 });
300 if let Some(n) = nine_slice {
301 image.set_nine_slice_edges(n[0], n[1], n[2], n[3]);
302 }
303 Value::Image(image)
304 }
305 Expression::Condition { condition, true_expr, false_expr } => {
306 match eval_expression(condition, local_context).try_into()
307 as Result<bool, _>
308 {
309 Ok(true) => eval_expression(true_expr, local_context),
310 Ok(false) => eval_expression(false_expr, local_context),
311 _ => local_context.return_value.clone().expect("conditional expression did not evaluate to boolean"),
312 }
313 }
314 Expression::Array { values, .. } => Value::Model(
315 ModelRc::new(corelib::model::SharedVectorModel::from(
316 values.iter().map(|e| eval_expression(e, local_context)).collect::<SharedVector<_>>()
317 )
318 )),
319 Expression::Struct { values, .. } => Value::Struct(
320 values
321 .iter()
322 .map(|(k, v)| (k.to_string(), eval_expression(v, local_context)))
323 .collect(),
324 ),
325 Expression::PathData(data) => {
326 Value::PathData(convert_path(data, local_context))
327 }
328 Expression::StoreLocalVariable { name, value } => {
329 let value = eval_expression(value, local_context);
330 local_context.local_variables.insert(name.clone(), value);
331 Value::Void
332 }
333 Expression::ReadLocalVariable { name, .. } => {
334 local_context.local_variables.get(name).unwrap().clone()
335 }
336 Expression::EasingCurve(curve) => Value::EasingCurve(match curve {
337 EasingCurve::Linear => corelib::animations::EasingCurve::Linear,
338 EasingCurve::EaseInElastic => corelib::animations::EasingCurve::EaseInElastic,
339 EasingCurve::EaseOutElastic => corelib::animations::EasingCurve::EaseOutElastic,
340 EasingCurve::EaseInOutElastic => corelib::animations::EasingCurve::EaseInOutElastic,
341 EasingCurve::EaseInBounce => corelib::animations::EasingCurve::EaseInBounce,
342 EasingCurve::EaseOutBounce => corelib::animations::EasingCurve::EaseOutBounce,
343 EasingCurve::EaseInOutBounce => corelib::animations::EasingCurve::EaseInOutBounce,
344 EasingCurve::CubicBezier(a, b, c, d) => {
345 corelib::animations::EasingCurve::CubicBezier([*a, *b, *c, *d])
346 }
347 }),
348 Expression::LinearGradient{angle, stops} => {
349 let angle = eval_expression(angle, local_context);
350 Value::Brush(Brush::LinearGradient(LinearGradientBrush::new(angle.try_into().unwrap(), stops.iter().map(|(color, stop)| {
351 let color = eval_expression(color, local_context).try_into().unwrap();
352 let position = eval_expression(stop, local_context).try_into().unwrap();
353 GradientStop{ color, position }
354 }))))
355 }
356 Expression::RadialGradient{stops} => {
357 Value::Brush(Brush::RadialGradient(RadialGradientBrush::new_circle(stops.iter().map(|(color, stop)| {
358 let color = eval_expression(color, local_context).try_into().unwrap();
359 let position = eval_expression(stop, local_context).try_into().unwrap();
360 GradientStop{ color, position }
361 }))))
362 }
363 Expression::EnumerationValue(value) => {
364 Value::EnumerationValue(value.enumeration.name.to_string(), value.to_string())
365 }
366 Expression::ReturnStatement(x) => {
367 let val = x.as_ref().map_or(Value::Void, |x| eval_expression(x, local_context));
368 if local_context.return_value.is_none() {
369 local_context.return_value = Some(val);
370 }
371 local_context.return_value.clone().unwrap()
372 }
373 Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
374 let cache = load_property_helper(&ComponentInstance::InstanceRef(local_context.component_instance), &layout_cache_prop.element(), layout_cache_prop.name()).unwrap();
375 if let Value::LayoutCache(cache) = cache {
376 if let Some(ri) = repeater_index {
377 let offset : usize = eval_expression(ri, local_context).try_into().unwrap();
378 Value::Number(cache.get((cache[*index] as usize) + offset * 2).copied().unwrap_or(0.).into())
379 } else {
380 Value::Number(cache[*index].into())
381 }
382 } else {
383 panic!("invalid layout cache")
384 }
385 }
386 Expression::ComputeLayoutInfo(lay, o) => crate::eval_layout::compute_layout_info(lay, *o, local_context),
387 Expression::SolveLayout(lay, o) => crate::eval_layout::solve_layout(lay, *o, local_context),
388 Expression::MinMax { ty: _, op, lhs, rhs } => {
389 let Value::Number(lhs) = eval_expression(lhs, local_context) else {
390 return local_context.return_value.clone().expect("minmax lhs expression did not evaluate to number");
391 };
392 let Value::Number(rhs) = eval_expression(rhs, local_context) else {
393 return local_context.return_value.clone().expect("minmax rhs expression did not evaluate to number");
394 };
395 match op {
396 MinMaxOp::Min => Value::Number(lhs.min(rhs)),
397 MinMaxOp::Max => Value::Number(lhs.max(rhs)),
398 }
399 }
400 Expression::EmptyComponentFactory => Value::ComponentFactory(Default::default()),
401 Expression::DebugHook { expression, .. } => eval_expression(expression, local_context),
402 }
403}
404
405fn call_builtin_function(
406 f: BuiltinFunction,
407 arguments: &[Expression],
408 local_context: &mut EvalLocalContext,
409 source_location: &Option<i_slint_compiler::diagnostics::SourceLocation>,
410) -> Value {
411 match f {
412 BuiltinFunction::GetWindowScaleFactor => Value::Number(
413 local_context.component_instance.access_window(|window| window.scale_factor()) as _,
414 ),
415 BuiltinFunction::GetWindowDefaultFontSize => {
416 Value::Number(local_context.component_instance.access_window(|window| {
417 window.window_item().unwrap().as_pin_ref().default_font_size().get()
418 }) as _)
419 }
420 BuiltinFunction::AnimationTick => {
421 Value::Number(i_slint_core::animations::animation_tick() as f64)
422 }
423 BuiltinFunction::Debug => {
424 let to_print: SharedString =
425 eval_expression(&arguments[0], local_context).try_into().unwrap();
426 local_context.component_instance.description.debug_handler.borrow()(
427 source_location.as_ref(),
428 &to_print,
429 );
430 Value::Void
431 }
432 BuiltinFunction::Mod => {
433 let mut to_num = |e| -> f64 { eval_expression(e, local_context).try_into().unwrap() };
434 Value::Number(to_num(&arguments[0]).rem_euclid(to_num(&arguments[1])))
435 }
436 BuiltinFunction::Round => {
437 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
438 Value::Number(x.round())
439 }
440 BuiltinFunction::Ceil => {
441 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
442 Value::Number(x.ceil())
443 }
444 BuiltinFunction::Floor => {
445 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
446 Value::Number(x.floor())
447 }
448 BuiltinFunction::Sqrt => {
449 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
450 Value::Number(x.sqrt())
451 }
452 BuiltinFunction::Abs => {
453 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
454 Value::Number(x.abs())
455 }
456 BuiltinFunction::Sin => {
457 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
458 Value::Number(x.to_radians().sin())
459 }
460 BuiltinFunction::Cos => {
461 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
462 Value::Number(x.to_radians().cos())
463 }
464 BuiltinFunction::Tan => {
465 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
466 Value::Number(x.to_radians().tan())
467 }
468 BuiltinFunction::ASin => {
469 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
470 Value::Number(x.asin().to_degrees())
471 }
472 BuiltinFunction::ACos => {
473 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
474 Value::Number(x.acos().to_degrees())
475 }
476 BuiltinFunction::ATan => {
477 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
478 Value::Number(x.atan().to_degrees())
479 }
480 BuiltinFunction::ATan2 => {
481 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
482 let y: f64 = eval_expression(&arguments[1], local_context).try_into().unwrap();
483 Value::Number(x.atan2(y).to_degrees())
484 }
485 BuiltinFunction::Log => {
486 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
487 let y: f64 = eval_expression(&arguments[1], local_context).try_into().unwrap();
488 Value::Number(x.log(y))
489 }
490 BuiltinFunction::Ln => {
491 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
492 Value::Number(x.ln())
493 }
494 BuiltinFunction::Pow => {
495 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
496 let y: f64 = eval_expression(&arguments[1], local_context).try_into().unwrap();
497 Value::Number(x.powf(y))
498 }
499 BuiltinFunction::Exp => {
500 let x: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
501 Value::Number(x.exp())
502 }
503 BuiltinFunction::ToFixed => {
504 let n: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
505 let digits: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
506 let digits: usize = digits.max(0) as usize;
507 Value::String(i_slint_core::string::shared_string_from_number_fixed(n, digits))
508 }
509 BuiltinFunction::ToPrecision => {
510 let n: f64 = eval_expression(&arguments[0], local_context).try_into().unwrap();
511 let precision: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
512 let precision: usize = precision.max(0) as usize;
513 Value::String(i_slint_core::string::shared_string_from_number_precision(n, precision))
514 }
515 BuiltinFunction::SetFocusItem => {
516 if arguments.len() != 1 {
517 panic!("internal error: incorrect argument count to SetFocusItem")
518 }
519 let component = local_context.component_instance;
520 if let Expression::ElementReference(focus_item) = &arguments[0] {
521 generativity::make_guard!(guard);
522
523 let focus_item = focus_item.upgrade().unwrap();
524 let enclosing_component =
525 enclosing_component_for_element(&focus_item, component, guard);
526 let description = enclosing_component.description;
527
528 let item_info = &description.items[focus_item.borrow().id.as_str()];
529
530 let focus_item_comp =
531 enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
532
533 component.access_window(|window| {
534 window.set_focus_item(
535 &corelib::items::ItemRc::new(
536 vtable::VRc::into_dyn(focus_item_comp),
537 item_info.item_index(),
538 ),
539 true,
540 FocusReason::Programmatic,
541 )
542 });
543 Value::Void
544 } else {
545 panic!("internal error: argument to SetFocusItem must be an element")
546 }
547 }
548 BuiltinFunction::ClearFocusItem => {
549 if arguments.len() != 1 {
550 panic!("internal error: incorrect argument count to SetFocusItem")
551 }
552 let component = local_context.component_instance;
553 if let Expression::ElementReference(focus_item) = &arguments[0] {
554 generativity::make_guard!(guard);
555
556 let focus_item = focus_item.upgrade().unwrap();
557 let enclosing_component =
558 enclosing_component_for_element(&focus_item, component, guard);
559 let description = enclosing_component.description;
560
561 let item_info = &description.items[focus_item.borrow().id.as_str()];
562
563 let focus_item_comp =
564 enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
565
566 component.access_window(|window| {
567 window.set_focus_item(
568 &corelib::items::ItemRc::new(
569 vtable::VRc::into_dyn(focus_item_comp),
570 item_info.item_index(),
571 ),
572 false,
573 FocusReason::Programmatic,
574 )
575 });
576 Value::Void
577 } else {
578 panic!("internal error: argument to ClearFocusItem must be an element")
579 }
580 }
581 BuiltinFunction::ShowPopupWindow => {
582 if arguments.len() != 1 {
583 panic!("internal error: incorrect argument count to ShowPopupWindow")
584 }
585 let component = local_context.component_instance;
586 if let Expression::ElementReference(popup_window) = &arguments[0] {
587 let popup_window = popup_window.upgrade().unwrap();
588 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
589 let parent_component = pop_comp
590 .parent_element
591 .upgrade()
592 .unwrap()
593 .borrow()
594 .enclosing_component
595 .upgrade()
596 .unwrap();
597 let popup_list = parent_component.popup_windows.borrow();
598 let popup =
599 popup_list.iter().find(|p| Rc::ptr_eq(&p.component, &pop_comp)).unwrap();
600
601 generativity::make_guard!(guard);
602 let enclosing_component =
603 enclosing_component_for_element(&popup.parent_element, component, guard);
604 let parent_item_info = &enclosing_component.description.items
605 [popup.parent_element.borrow().id.as_str()];
606 let parent_item_comp =
607 enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
608 let parent_item = corelib::items::ItemRc::new(
609 vtable::VRc::into_dyn(parent_item_comp),
610 parent_item_info.item_index(),
611 );
612
613 let close_policy = Value::EnumerationValue(
614 popup.close_policy.enumeration.name.to_string(),
615 popup.close_policy.to_string(),
616 )
617 .try_into()
618 .expect("Invalid internal enumeration representation for close policy");
619
620 crate::dynamic_item_tree::show_popup(
621 popup_window,
622 component,
623 popup,
624 |instance_ref| {
625 let comp = ComponentInstance::InstanceRef(instance_ref);
626 let x = load_property_helper(&comp, &popup.x.element(), popup.x.name())
627 .unwrap();
628 let y = load_property_helper(&comp, &popup.y.element(), popup.y.name())
629 .unwrap();
630 corelib::api::LogicalPosition::new(
631 x.try_into().unwrap(),
632 y.try_into().unwrap(),
633 )
634 },
635 close_policy,
636 enclosing_component.self_weak().get().unwrap().clone(),
637 component.window_adapter(),
638 &parent_item,
639 );
640 Value::Void
641 } else {
642 panic!("internal error: argument to ShowPopupWindow must be an element")
643 }
644 }
645 BuiltinFunction::ClosePopupWindow => {
646 let component = local_context.component_instance;
647 if let Expression::ElementReference(popup_window) = &arguments[0] {
648 let popup_window = popup_window.upgrade().unwrap();
649 let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
650 let parent_component = pop_comp
651 .parent_element
652 .upgrade()
653 .unwrap()
654 .borrow()
655 .enclosing_component
656 .upgrade()
657 .unwrap();
658 let popup_list = parent_component.popup_windows.borrow();
659 let popup =
660 popup_list.iter().find(|p| Rc::ptr_eq(&p.component, &pop_comp)).unwrap();
661
662 generativity::make_guard!(guard);
663 let enclosing_component =
664 enclosing_component_for_element(&popup.parent_element, component, guard);
665 crate::dynamic_item_tree::close_popup(
666 popup_window,
667 enclosing_component,
668 enclosing_component.window_adapter(),
669 );
670
671 Value::Void
672 } else {
673 panic!("internal error: argument to ClosePopupWindow must be an element")
674 }
675 }
676 BuiltinFunction::ShowPopupMenu => {
677 let [Expression::ElementReference(element), entries, position] = arguments else {
678 panic!("internal error: incorrect argument count to ShowPopupMenu")
679 };
680 let position = eval_expression(position, local_context)
681 .try_into()
682 .expect("internal error: popup menu position argument should be a point");
683
684 let component = local_context.component_instance;
685 let elem = element.upgrade().unwrap();
686 generativity::make_guard!(guard);
687 let enclosing_component = enclosing_component_for_element(&elem, component, guard);
688 let description = enclosing_component.description;
689 let item_info = &description.items[elem.borrow().id.as_str()];
690 let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
691 let item_rc = corelib::items::ItemRc::new(
692 vtable::VRc::into_dyn(item_comp),
693 item_info.item_index(),
694 );
695
696 if component.access_window(|window| window.show_native_popup_menu(&item_rc, position)) {
697 return Value::Void;
698 }
699
700 generativity::make_guard!(guard);
701 let compiled = enclosing_component.description.popup_menu_description.unerase(guard);
702 let inst = crate::dynamic_item_tree::instantiate(
703 compiled.clone(),
704 Some(enclosing_component.self_weak().get().unwrap().clone()),
705 None,
706 Some(&crate::dynamic_item_tree::WindowOptions::UseExistingWindow(
707 component.window_adapter(),
708 )),
709 Default::default(),
710 );
711
712 generativity::make_guard!(guard);
713 let inst_ref = inst.unerase(guard);
714 if let Expression::ElementReference(e) = entries {
715 let menu_item_tree =
716 e.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
717 let (entries, sub_menu, activated) =
718 menu_item_tree_properties(crate::dynamic_item_tree::make_menu_item_tree(
719 &menu_item_tree,
720 &enclosing_component,
721 ));
722 compiled.set_binding(inst_ref.borrow(), "entries", entries).unwrap();
723 compiled.set_callback_handler(inst_ref.borrow(), "sub-menu", sub_menu).unwrap();
724 compiled.set_callback_handler(inst_ref.borrow(), "activated", activated).unwrap();
725 } else {
726 let entries = eval_expression(entries, local_context);
727 compiled.set_property(inst_ref.borrow(), "entries", entries).unwrap();
728 let item_weak = item_rc.downgrade();
729 compiled
730 .set_callback_handler(
731 inst_ref.borrow(),
732 "sub-menu",
733 Box::new(move |args: &[Value]| -> Value {
734 item_weak
735 .upgrade()
736 .unwrap()
737 .downcast::<corelib::items::ContextMenu>()
738 .unwrap()
739 .sub_menu
740 .call(&(args[0].clone().try_into().unwrap(),))
741 .into()
742 }),
743 )
744 .unwrap();
745 let item_weak = item_rc.downgrade();
746 compiled
747 .set_callback_handler(
748 inst_ref.borrow(),
749 "activated",
750 Box::new(move |args: &[Value]| -> Value {
751 item_weak
752 .upgrade()
753 .unwrap()
754 .downcast::<corelib::items::ContextMenu>()
755 .unwrap()
756 .activated
757 .call(&(args[0].clone().try_into().unwrap(),));
758 Value::Void
759 }),
760 )
761 .unwrap();
762 }
763 let item_weak = item_rc.downgrade();
764 compiled
765 .set_callback_handler(
766 inst_ref.borrow(),
767 "close",
768 Box::new(move |_args: &[Value]| -> Value {
769 let Some(item_rc) = item_weak.upgrade() else { return Value::Void };
770 if let Some(id) = item_rc
771 .downcast::<corelib::items::ContextMenu>()
772 .unwrap()
773 .popup_id
774 .take()
775 {
776 WindowInner::from_pub(item_rc.window_adapter().unwrap().window())
777 .close_popup(id);
778 }
779 Value::Void
780 }),
781 )
782 .unwrap();
783 component.access_window(|window| {
784 let context_menu_elem = item_rc.downcast::<corelib::items::ContextMenu>().unwrap();
785 if let Some(old_id) = context_menu_elem.popup_id.take() {
786 window.close_popup(old_id)
787 }
788 let id = window.show_popup(
789 &vtable::VRc::into_dyn(inst.clone()),
790 position,
791 corelib::items::PopupClosePolicy::CloseOnClickOutside,
792 &item_rc,
793 true,
794 );
795 context_menu_elem.popup_id.set(Some(id));
796 });
797 inst.run_setup_code();
798 Value::Void
799 }
800 BuiltinFunction::SetSelectionOffsets => {
801 if arguments.len() != 3 {
802 panic!("internal error: incorrect argument count to select range function call")
803 }
804 let component = local_context.component_instance;
805 if let Expression::ElementReference(element) = &arguments[0] {
806 generativity::make_guard!(guard);
807
808 let elem = element.upgrade().unwrap();
809 let enclosing_component = enclosing_component_for_element(&elem, component, guard);
810 let description = enclosing_component.description;
811 let item_info = &description.items[elem.borrow().id.as_str()];
812 let item_ref =
813 unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
814
815 let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
816 let item_rc = corelib::items::ItemRc::new(
817 vtable::VRc::into_dyn(item_comp),
818 item_info.item_index(),
819 );
820
821 let window_adapter = component.window_adapter();
822
823 if let Some(textinput) =
825 ItemRef::downcast_pin::<corelib::items::TextInput>(item_ref)
826 {
827 let start: i32 =
828 eval_expression(&arguments[1], local_context).try_into().expect(
829 "internal error: second argument to set-selection-offsets must be an integer",
830 );
831 let end: i32 = eval_expression(&arguments[2], local_context).try_into().expect(
832 "internal error: third argument to set-selection-offsets must be an integer",
833 );
834
835 textinput.set_selection_offsets(&window_adapter, &item_rc, start, end);
836 } else {
837 panic!(
838 "internal error: member function called on element that doesn't have it: {}",
839 elem.borrow().original_name()
840 )
841 }
842
843 Value::Void
844 } else {
845 panic!("internal error: first argument to set-selection-offsets must be an element")
846 }
847 }
848 BuiltinFunction::ItemFontMetrics => {
849 if arguments.len() != 1 {
850 panic!(
851 "internal error: incorrect argument count to item font metrics function call"
852 )
853 }
854 let component = local_context.component_instance;
855 if let Expression::ElementReference(element) = &arguments[0] {
856 generativity::make_guard!(guard);
857
858 let elem = element.upgrade().unwrap();
859 let enclosing_component = enclosing_component_for_element(&elem, component, guard);
860 let description = enclosing_component.description;
861 let item_info = &description.items[elem.borrow().id.as_str()];
862 let item_ref =
863 unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
864 let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
865 let item_rc = corelib::items::ItemRc::new(
866 vtable::VRc::into_dyn(item_comp),
867 item_info.item_index(),
868 );
869 let window_adapter = component.window_adapter();
870 let metrics = i_slint_core::items::slint_text_item_fontmetrics(
871 &window_adapter,
872 item_ref,
873 &item_rc,
874 );
875 metrics.into()
876 } else {
877 panic!("internal error: argument to item-font-metrics must be an element")
878 }
879 }
880 BuiltinFunction::StringIsFloat => {
881 if arguments.len() != 1 {
882 panic!("internal error: incorrect argument count to StringIsFloat")
883 }
884 if let Value::String(s) = eval_expression(&arguments[0], local_context) {
885 Value::Bool(<f64 as core::str::FromStr>::from_str(s.as_str()).is_ok())
886 } else {
887 panic!("Argument not a string");
888 }
889 }
890 BuiltinFunction::StringToFloat => {
891 if arguments.len() != 1 {
892 panic!("internal error: incorrect argument count to StringToFloat")
893 }
894 if let Value::String(s) = eval_expression(&arguments[0], local_context) {
895 Value::Number(core::str::FromStr::from_str(s.as_str()).unwrap_or(0.))
896 } else {
897 panic!("Argument not a string");
898 }
899 }
900 BuiltinFunction::StringIsEmpty => {
901 if arguments.len() != 1 {
902 panic!("internal error: incorrect argument count to StringIsEmpty")
903 }
904 if let Value::String(s) = eval_expression(&arguments[0], local_context) {
905 Value::Bool(s.is_empty())
906 } else {
907 panic!("Argument not a string");
908 }
909 }
910 BuiltinFunction::StringCharacterCount => {
911 if arguments.len() != 1 {
912 panic!("internal error: incorrect argument count to StringCharacterCount")
913 }
914 if let Value::String(s) = eval_expression(&arguments[0], local_context) {
915 Value::Number(
916 unicode_segmentation::UnicodeSegmentation::graphemes(s.as_str(), true).count()
917 as f64,
918 )
919 } else {
920 panic!("Argument not a string");
921 }
922 }
923 BuiltinFunction::StringToLowercase => {
924 if arguments.len() != 1 {
925 panic!("internal error: incorrect argument count to StringToLowercase")
926 }
927 if let Value::String(s) = eval_expression(&arguments[0], local_context) {
928 Value::String(s.to_lowercase().into())
929 } else {
930 panic!("Argument not a string");
931 }
932 }
933 BuiltinFunction::StringToUppercase => {
934 if arguments.len() != 1 {
935 panic!("internal error: incorrect argument count to StringToUppercase")
936 }
937 if let Value::String(s) = eval_expression(&arguments[0], local_context) {
938 Value::String(s.to_uppercase().into())
939 } else {
940 panic!("Argument not a string");
941 }
942 }
943 BuiltinFunction::ColorRgbaStruct => {
944 if arguments.len() != 1 {
945 panic!("internal error: incorrect argument count to ColorRGBAComponents")
946 }
947 if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
948 let color = brush.color();
949 let values = IntoIterator::into_iter([
950 ("red".to_string(), Value::Number(color.red().into())),
951 ("green".to_string(), Value::Number(color.green().into())),
952 ("blue".to_string(), Value::Number(color.blue().into())),
953 ("alpha".to_string(), Value::Number(color.alpha().into())),
954 ])
955 .collect();
956 Value::Struct(values)
957 } else {
958 panic!("First argument not a color");
959 }
960 }
961 BuiltinFunction::ColorHsvaStruct => {
962 if arguments.len() != 1 {
963 panic!("internal error: incorrect argument count to ColorHSVAComponents")
964 }
965 if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
966 let color = brush.color().to_hsva();
967 let values = IntoIterator::into_iter([
968 ("hue".to_string(), Value::Number(color.hue.into())),
969 ("saturation".to_string(), Value::Number(color.saturation.into())),
970 ("value".to_string(), Value::Number(color.value.into())),
971 ("alpha".to_string(), Value::Number(color.alpha.into())),
972 ])
973 .collect();
974 Value::Struct(values)
975 } else {
976 panic!("First argument not a color");
977 }
978 }
979 BuiltinFunction::ColorBrighter => {
980 if arguments.len() != 2 {
981 panic!("internal error: incorrect argument count to ColorBrighter")
982 }
983 if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
984 if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
985 brush.brighter(factor as _).into()
986 } else {
987 panic!("Second argument not a number");
988 }
989 } else {
990 panic!("First argument not a color");
991 }
992 }
993 BuiltinFunction::ColorDarker => {
994 if arguments.len() != 2 {
995 panic!("internal error: incorrect argument count to ColorDarker")
996 }
997 if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
998 if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
999 brush.darker(factor as _).into()
1000 } else {
1001 panic!("Second argument not a number");
1002 }
1003 } else {
1004 panic!("First argument not a color");
1005 }
1006 }
1007 BuiltinFunction::ColorTransparentize => {
1008 if arguments.len() != 2 {
1009 panic!("internal error: incorrect argument count to ColorFaded")
1010 }
1011 if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1012 if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1013 brush.transparentize(factor as _).into()
1014 } else {
1015 panic!("Second argument not a number");
1016 }
1017 } else {
1018 panic!("First argument not a color");
1019 }
1020 }
1021 BuiltinFunction::ColorMix => {
1022 if arguments.len() != 3 {
1023 panic!("internal error: incorrect argument count to ColorMix")
1024 }
1025
1026 let arg0 = eval_expression(&arguments[0], local_context);
1027 let arg1 = eval_expression(&arguments[1], local_context);
1028 let arg2 = eval_expression(&arguments[2], local_context);
1029
1030 if !matches!(arg0, Value::Brush(Brush::SolidColor(_))) {
1031 panic!("First argument not a color");
1032 }
1033 if !matches!(arg1, Value::Brush(Brush::SolidColor(_))) {
1034 panic!("Second argument not a color");
1035 }
1036 if !matches!(arg2, Value::Number(_)) {
1037 panic!("Third argument not a number");
1038 }
1039
1040 let (
1041 Value::Brush(Brush::SolidColor(color_a)),
1042 Value::Brush(Brush::SolidColor(color_b)),
1043 Value::Number(factor),
1044 ) = (arg0, arg1, arg2)
1045 else {
1046 unreachable!()
1047 };
1048
1049 color_a.mix(&color_b, factor as _).into()
1050 }
1051 BuiltinFunction::ColorWithAlpha => {
1052 if arguments.len() != 2 {
1053 panic!("internal error: incorrect argument count to ColorWithAlpha")
1054 }
1055 if let Value::Brush(brush) = eval_expression(&arguments[0], local_context) {
1056 if let Value::Number(factor) = eval_expression(&arguments[1], local_context) {
1057 brush.with_alpha(factor as _).into()
1058 } else {
1059 panic!("Second argument not a number");
1060 }
1061 } else {
1062 panic!("First argument not a color");
1063 }
1064 }
1065 BuiltinFunction::ImageSize => {
1066 if arguments.len() != 1 {
1067 panic!("internal error: incorrect argument count to ImageSize")
1068 }
1069 if let Value::Image(img) = eval_expression(&arguments[0], local_context) {
1070 let size = img.size();
1071 let values = IntoIterator::into_iter([
1072 ("width".to_string(), Value::Number(size.width as f64)),
1073 ("height".to_string(), Value::Number(size.height as f64)),
1074 ])
1075 .collect();
1076 Value::Struct(values)
1077 } else {
1078 panic!("First argument not an image");
1079 }
1080 }
1081 BuiltinFunction::ArrayLength => {
1082 if arguments.len() != 1 {
1083 panic!("internal error: incorrect argument count to ArrayLength")
1084 }
1085 match eval_expression(&arguments[0], local_context) {
1086 Value::Model(model) => {
1087 model.model_tracker().track_row_count_changes();
1088 Value::Number(model.row_count() as f64)
1089 }
1090 _ => {
1091 panic!("First argument not an array");
1092 }
1093 }
1094 }
1095 BuiltinFunction::Rgb => {
1096 let r: i32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1097 let g: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1098 let b: i32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1099 let a: f32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1100 let r: u8 = r.clamp(0, 255) as u8;
1101 let g: u8 = g.clamp(0, 255) as u8;
1102 let b: u8 = b.clamp(0, 255) as u8;
1103 let a: u8 = (255. * a).clamp(0., 255.) as u8;
1104 Value::Brush(Brush::SolidColor(Color::from_argb_u8(a, r, g, b)))
1105 }
1106 BuiltinFunction::Hsv => {
1107 let h: f32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1108 let s: f32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1109 let v: f32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1110 let a: f32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1111 let a = (1. * a).clamp(0., 1.);
1112 Value::Brush(Brush::SolidColor(Color::from_hsva(h, s, v, a)))
1113 }
1114 BuiltinFunction::ColorScheme => local_context
1115 .component_instance
1116 .window_adapter()
1117 .internal(corelib::InternalToken)
1118 .map_or(ColorScheme::Unknown, |x| x.color_scheme())
1119 .into(),
1120 BuiltinFunction::SupportsNativeMenuBar => local_context
1121 .component_instance
1122 .window_adapter()
1123 .internal(corelib::InternalToken)
1124 .is_some_and(|x| x.supports_native_menu_bar())
1125 .into(),
1126 BuiltinFunction::SetupNativeMenuBar => {
1127 let component = local_context.component_instance;
1128 if let [Expression::PropertyReference(entries_nr), Expression::PropertyReference(sub_menu_nr), Expression::PropertyReference(activated_nr), Expression::ElementReference(item_tree_root), Expression::BoolLiteral(no_native)] =
1129 arguments
1130 {
1131 let menu_item_tree = item_tree_root
1132 .upgrade()
1133 .unwrap()
1134 .borrow()
1135 .enclosing_component
1136 .upgrade()
1137 .unwrap();
1138 let menu_item_tree =
1139 crate::dynamic_item_tree::make_menu_item_tree(&menu_item_tree, &component);
1140
1141 if let Some(w) = component.window_adapter().internal(i_slint_core::InternalToken) {
1142 if !no_native && w.supports_native_menu_bar() {
1143 w.setup_menubar(vtable::VBox::new(menu_item_tree));
1144 return Value::Void;
1145 }
1146 }
1147
1148 let (entries, sub_menu, activated) = menu_item_tree_properties(menu_item_tree);
1149
1150 assert_eq!(
1151 entries_nr.element().borrow().id,
1152 component.description.original.root_element.borrow().id,
1153 "entries need to be in the main element"
1154 );
1155 local_context
1156 .component_instance
1157 .description
1158 .set_binding(component.borrow(), entries_nr.name(), entries)
1159 .unwrap();
1160 let i = &ComponentInstance::InstanceRef(local_context.component_instance);
1161 set_callback_handler(i, &sub_menu_nr.element(), sub_menu_nr.name(), sub_menu)
1162 .unwrap();
1163 set_callback_handler(i, &activated_nr.element(), activated_nr.name(), activated)
1164 .unwrap();
1165
1166 return Value::Void;
1167 }
1168 let [entries, Expression::PropertyReference(sub_menu), Expression::PropertyReference(activated)] =
1169 arguments
1170 else {
1171 panic!("internal error: incorrect arguments to SetupNativeMenuBar: {arguments:?}")
1172 };
1173 if let Some(w) = component.window_adapter().internal(i_slint_core::InternalToken) {
1174 if w.supports_native_menu_bar() {
1175 w.setup_menubar(vtable::VBox::new(MenuWrapper {
1176 entries: entries.clone(),
1177 sub_menu: sub_menu.clone(),
1178 activated: activated.clone(),
1179 item_tree: component.self_weak().get().unwrap().clone(),
1180 }));
1181 }
1182 }
1183 Value::Void
1184 }
1185 BuiltinFunction::MonthDayCount => {
1186 let m: u32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1187 let y: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1188 Value::Number(i_slint_core::date_time::month_day_count(m, y).unwrap_or(0) as f64)
1189 }
1190 BuiltinFunction::MonthOffset => {
1191 let m: u32 = eval_expression(&arguments[0], local_context).try_into().unwrap();
1192 let y: i32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1193
1194 Value::Number(i_slint_core::date_time::month_offset(m, y) as f64)
1195 }
1196 BuiltinFunction::FormatDate => {
1197 let f: SharedString = eval_expression(&arguments[0], local_context).try_into().unwrap();
1198 let d: u32 = eval_expression(&arguments[1], local_context).try_into().unwrap();
1199 let m: u32 = eval_expression(&arguments[2], local_context).try_into().unwrap();
1200 let y: i32 = eval_expression(&arguments[3], local_context).try_into().unwrap();
1201
1202 Value::String(i_slint_core::date_time::format_date(&f, d, m, y))
1203 }
1204 BuiltinFunction::DateNow => Value::Model(ModelRc::new(VecModel::from(
1205 i_slint_core::date_time::date_now()
1206 .into_iter()
1207 .map(|x| Value::Number(x as f64))
1208 .collect::<Vec<_>>(),
1209 ))),
1210 BuiltinFunction::ValidDate => {
1211 let d: SharedString = eval_expression(&arguments[0], local_context).try_into().unwrap();
1212 let f: SharedString = eval_expression(&arguments[1], local_context).try_into().unwrap();
1213 Value::Bool(i_slint_core::date_time::parse_date(d.as_str(), f.as_str()).is_some())
1214 }
1215 BuiltinFunction::ParseDate => {
1216 let d: SharedString = eval_expression(&arguments[0], local_context).try_into().unwrap();
1217 let f: SharedString = eval_expression(&arguments[1], local_context).try_into().unwrap();
1218
1219 Value::Model(ModelRc::new(
1220 i_slint_core::date_time::parse_date(d.as_str(), f.as_str())
1221 .map(|x| {
1222 VecModel::from(
1223 x.into_iter().map(|x| Value::Number(x as f64)).collect::<Vec<_>>(),
1224 )
1225 })
1226 .unwrap_or_default(),
1227 ))
1228 }
1229 BuiltinFunction::TextInputFocused => Value::Bool(
1230 local_context.component_instance.access_window(|window| window.text_input_focused())
1231 as _,
1232 ),
1233 BuiltinFunction::SetTextInputFocused => {
1234 local_context.component_instance.access_window(|window| {
1235 window.set_text_input_focused(
1236 eval_expression(&arguments[0], local_context).try_into().unwrap(),
1237 )
1238 });
1239 Value::Void
1240 }
1241 BuiltinFunction::ImplicitLayoutInfo(orient) => {
1242 let component = local_context.component_instance;
1243 if let [Expression::ElementReference(item)] = arguments {
1244 generativity::make_guard!(guard);
1245
1246 let item = item.upgrade().unwrap();
1247 let enclosing_component = enclosing_component_for_element(&item, component, guard);
1248 let description = enclosing_component.description;
1249 let item_info = &description.items[item.borrow().id.as_str()];
1250 let item_ref =
1251 unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1252 let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1253 let window_adapter = component.window_adapter();
1254 item_ref
1255 .as_ref()
1256 .layout_info(
1257 crate::eval_layout::to_runtime(orient),
1258 &window_adapter,
1259 &ItemRc::new(vtable::VRc::into_dyn(item_comp), item_info.item_index()),
1260 )
1261 .into()
1262 } else {
1263 panic!("internal error: incorrect arguments to ImplicitLayoutInfo {arguments:?}");
1264 }
1265 }
1266 BuiltinFunction::ItemAbsolutePosition => {
1267 if arguments.len() != 1 {
1268 panic!("internal error: incorrect argument count to ItemAbsolutePosition")
1269 }
1270
1271 let component = local_context.component_instance;
1272
1273 if let Expression::ElementReference(item) = &arguments[0] {
1274 generativity::make_guard!(guard);
1275
1276 let item = item.upgrade().unwrap();
1277 let enclosing_component = enclosing_component_for_element(&item, component, guard);
1278 let description = enclosing_component.description;
1279
1280 let item_info = &description.items[item.borrow().id.as_str()];
1281
1282 let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1283
1284 let item_rc = corelib::items::ItemRc::new(
1285 vtable::VRc::into_dyn(item_comp),
1286 item_info.item_index(),
1287 );
1288
1289 item_rc.map_to_window(Default::default()).to_untyped().into()
1290 } else {
1291 panic!("internal error: argument to SetFocusItem must be an element")
1292 }
1293 }
1294 BuiltinFunction::RegisterCustomFontByPath => {
1295 if arguments.len() != 1 {
1296 panic!("internal error: incorrect argument count to RegisterCustomFontByPath")
1297 }
1298 let component = local_context.component_instance;
1299 if let Value::String(s) = eval_expression(&arguments[0], local_context) {
1300 if let Some(err) = component
1301 .window_adapter()
1302 .renderer()
1303 .register_font_from_path(&std::path::PathBuf::from(s.as_str()))
1304 .err()
1305 {
1306 corelib::debug_log!("Error loading custom font {}: {}", s.as_str(), err);
1307 }
1308 Value::Void
1309 } else {
1310 panic!("Argument not a string");
1311 }
1312 }
1313 BuiltinFunction::RegisterCustomFontByMemory | BuiltinFunction::RegisterBitmapFont => {
1314 unimplemented!()
1315 }
1316 BuiltinFunction::Translate => {
1317 let original: SharedString =
1318 eval_expression(&arguments[0], local_context).try_into().unwrap();
1319 let context: SharedString =
1320 eval_expression(&arguments[1], local_context).try_into().unwrap();
1321 let domain: SharedString =
1322 eval_expression(&arguments[2], local_context).try_into().unwrap();
1323 let args = eval_expression(&arguments[3], local_context);
1324 let Value::Model(args) = args else { panic!("Args to translate not a model {args:?}") };
1325 struct StringModelWrapper(ModelRc<Value>);
1326 impl corelib::translations::FormatArgs for StringModelWrapper {
1327 type Output<'a> = SharedString;
1328 fn from_index(&self, index: usize) -> Option<SharedString> {
1329 self.0.row_data(index).map(|x| x.try_into().unwrap())
1330 }
1331 }
1332 Value::String(corelib::translations::translate(
1333 &original,
1334 &context,
1335 &domain,
1336 &StringModelWrapper(args),
1337 eval_expression(&arguments[4], local_context).try_into().unwrap(),
1338 &SharedString::try_from(eval_expression(&arguments[5], local_context)).unwrap(),
1339 ))
1340 }
1341 BuiltinFunction::Use24HourFormat => Value::Bool(corelib::date_time::use_24_hour_format()),
1342 BuiltinFunction::UpdateTimers => {
1343 crate::dynamic_item_tree::update_timers(local_context.component_instance);
1344 Value::Void
1345 }
1346 BuiltinFunction::DetectOperatingSystem => i_slint_core::detect_operating_system().into(),
1347 }
1348}
1349
1350fn call_item_member_function(nr: &NamedReference, local_context: &mut EvalLocalContext) -> Value {
1351 let component = local_context.component_instance;
1352 let elem = nr.element();
1353 let name = nr.name().as_str();
1354 generativity::make_guard!(guard);
1355 let enclosing_component = enclosing_component_for_element(&elem, component, guard);
1356 let description = enclosing_component.description;
1357 let item_info = &description.items[elem.borrow().id.as_str()];
1358 let item_ref = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1359
1360 let item_comp = enclosing_component.self_weak().get().unwrap().upgrade().unwrap();
1361 let item_rc =
1362 corelib::items::ItemRc::new(vtable::VRc::into_dyn(item_comp), item_info.item_index());
1363
1364 let window_adapter = component.window_adapter();
1365
1366 if let Some(textinput) = ItemRef::downcast_pin::<corelib::items::TextInput>(item_ref) {
1368 match name {
1369 "select-all" => textinput.select_all(&window_adapter, &item_rc),
1370 "clear-selection" => textinput.clear_selection(&window_adapter, &item_rc),
1371 "cut" => textinput.cut(&window_adapter, &item_rc),
1372 "copy" => textinput.copy(&window_adapter, &item_rc),
1373 "paste" => textinput.paste(&window_adapter, &item_rc),
1374 _ => panic!("internal: Unknown member function {name} called on TextInput"),
1375 }
1376 } else if let Some(s) = ItemRef::downcast_pin::<corelib::items::SwipeGestureHandler>(item_ref) {
1377 match name {
1378 "cancel" => s.cancel(&window_adapter, &item_rc),
1379 _ => panic!("internal: Unknown member function {name} called on SwipeGestureHandler"),
1380 }
1381 } else if let Some(s) = ItemRef::downcast_pin::<corelib::items::ContextMenu>(item_ref) {
1382 match name {
1383 "close" => s.close(&window_adapter, &item_rc),
1384 "is-open" => return Value::Bool(s.is_open(&window_adapter, &item_rc)),
1385 _ => {
1386 panic!("internal: Unknown member function {name} called on ContextMenu")
1387 }
1388 }
1389 } else {
1390 panic!(
1391 "internal error: member function {name} called on element that doesn't have it: {}",
1392 elem.borrow().original_name()
1393 )
1394 }
1395
1396 Value::Void
1397}
1398
1399fn eval_assignment(lhs: &Expression, op: char, rhs: Value, local_context: &mut EvalLocalContext) {
1400 let eval = |lhs| match (lhs, &rhs, op) {
1401 (Value::String(ref mut a), Value::String(b), '+') => {
1402 a.push_str(b.as_str());
1403 Value::String(a.clone())
1404 }
1405 (Value::Number(a), Value::Number(b), '+') => Value::Number(a + b),
1406 (Value::Number(a), Value::Number(b), '-') => Value::Number(a - b),
1407 (Value::Number(a), Value::Number(b), '/') => Value::Number(a / b),
1408 (Value::Number(a), Value::Number(b), '*') => Value::Number(a * b),
1409 (lhs, rhs, op) => panic!("unsupported {lhs:?} {op} {rhs:?}"),
1410 };
1411 match lhs {
1412 Expression::PropertyReference(nr) => {
1413 let element = nr.element();
1414 generativity::make_guard!(guard);
1415 let enclosing_component = enclosing_component_instance_for_element(
1416 &element,
1417 &ComponentInstance::InstanceRef(local_context.component_instance),
1418 guard,
1419 );
1420
1421 match enclosing_component {
1422 ComponentInstance::InstanceRef(enclosing_component) => {
1423 if op == '=' {
1424 store_property(enclosing_component, &element, nr.name(), rhs).unwrap();
1425 return;
1426 }
1427
1428 let component = element.borrow().enclosing_component.upgrade().unwrap();
1429 if element.borrow().id == component.root_element.borrow().id {
1430 if let Some(x) =
1431 enclosing_component.description.custom_properties.get(nr.name())
1432 {
1433 unsafe {
1434 let p = Pin::new_unchecked(
1435 &*enclosing_component.as_ptr().add(x.offset),
1436 );
1437 x.prop.set(p, eval(x.prop.get(p).unwrap()), None).unwrap();
1438 }
1439 return;
1440 }
1441 };
1442 let item_info =
1443 &enclosing_component.description.items[element.borrow().id.as_str()];
1444 let item =
1445 unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1446 let p = &item_info.rtti.properties[nr.name().as_str()];
1447 p.set(item, eval(p.get(item)), None).unwrap();
1448 }
1449 ComponentInstance::GlobalComponent(global) => {
1450 let val = if op == '=' {
1451 rhs
1452 } else {
1453 eval(global.as_ref().get_property(nr.name()).unwrap())
1454 };
1455 global.as_ref().set_property(nr.name(), val).unwrap();
1456 }
1457 }
1458 }
1459 Expression::StructFieldAccess { base, name } => {
1460 if let Value::Struct(mut o) = eval_expression(base, local_context) {
1461 let mut r = o.get_field(name).unwrap().clone();
1462 r = if op == '=' { rhs } else { eval(std::mem::take(&mut r)) };
1463 o.set_field(name.to_string(), r);
1464 eval_assignment(base, '=', Value::Struct(o), local_context)
1465 }
1466 }
1467 Expression::RepeaterModelReference { element } => {
1468 let element = element.upgrade().unwrap();
1469 let component_instance = local_context.component_instance;
1470 generativity::make_guard!(g1);
1471 let enclosing_component =
1472 enclosing_component_for_element(&element, component_instance, g1);
1473 let static_guard =
1476 unsafe { generativity::Guard::new(generativity::Id::<'static>::new()) };
1477 let repeater = crate::dynamic_item_tree::get_repeater_by_name(
1478 enclosing_component,
1479 element.borrow().id.as_str(),
1480 static_guard,
1481 );
1482 repeater.0.model_set_row_data(
1483 eval_expression(
1484 &Expression::RepeaterIndexReference { element: Rc::downgrade(&element) },
1485 local_context,
1486 )
1487 .try_into()
1488 .unwrap(),
1489 if op == '=' {
1490 rhs
1491 } else {
1492 eval(eval_expression(
1493 &Expression::RepeaterModelReference { element: Rc::downgrade(&element) },
1494 local_context,
1495 ))
1496 },
1497 )
1498 }
1499 Expression::ArrayIndex { array, index } => {
1500 let array = eval_expression(array, local_context);
1501 let index = eval_expression(index, local_context);
1502 match (array, index) {
1503 (Value::Model(model), Value::Number(index)) => {
1504 if index >= 0. && (index as usize) < model.row_count() {
1505 let index = index as usize;
1506 if op == '=' {
1507 model.set_row_data(index, rhs);
1508 } else {
1509 model.set_row_data(
1510 index,
1511 eval(
1512 model
1513 .row_data(index)
1514 .unwrap_or_else(|| default_value_for_type(&lhs.ty())),
1515 ),
1516 );
1517 }
1518 }
1519 }
1520 _ => {
1521 eprintln!("Attempting to write into an array that cannot be written");
1522 }
1523 }
1524 }
1525 _ => panic!("typechecking should make sure this was a PropertyReference"),
1526 }
1527}
1528
1529pub fn load_property(component: InstanceRef, element: &ElementRc, name: &str) -> Result<Value, ()> {
1530 load_property_helper(&ComponentInstance::InstanceRef(component), element, name)
1531}
1532
1533fn load_property_helper(
1534 component_instance: &ComponentInstance,
1535 element: &ElementRc,
1536 name: &str,
1537) -> Result<Value, ()> {
1538 generativity::make_guard!(guard);
1539 match enclosing_component_instance_for_element(element, component_instance, guard) {
1540 ComponentInstance::InstanceRef(enclosing_component) => {
1541 let element = element.borrow();
1542 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1543 {
1544 if let Some(x) = enclosing_component.description.custom_properties.get(name) {
1545 return unsafe {
1546 x.prop.get(Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset)))
1547 };
1548 } else if enclosing_component.description.original.is_global() {
1549 return Err(());
1550 }
1551 };
1552 let item_info = enclosing_component
1553 .description
1554 .items
1555 .get(element.id.as_str())
1556 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, name));
1557 core::mem::drop(element);
1558 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1559 Ok(item_info.rtti.properties.get(name).ok_or(())?.get(item))
1560 }
1561 ComponentInstance::GlobalComponent(glob) => glob.as_ref().get_property(name),
1562 }
1563}
1564
1565pub fn store_property(
1566 component_instance: InstanceRef,
1567 element: &ElementRc,
1568 name: &str,
1569 value: Value,
1570) -> Result<(), SetPropertyError> {
1571 generativity::make_guard!(guard);
1572 match enclosing_component_instance_for_element(
1573 element,
1574 &ComponentInstance::InstanceRef(component_instance),
1575 guard,
1576 ) {
1577 ComponentInstance::InstanceRef(enclosing_component) => {
1578 let maybe_animation = match element.borrow().bindings.get(name) {
1579 Some(b) => crate::dynamic_item_tree::animation_for_property(
1580 enclosing_component,
1581 &b.borrow().animation,
1582 ),
1583 None => {
1584 crate::dynamic_item_tree::animation_for_property(enclosing_component, &None)
1585 }
1586 };
1587
1588 let component = element.borrow().enclosing_component.upgrade().unwrap();
1589 if element.borrow().id == component.root_element.borrow().id {
1590 if let Some(x) = enclosing_component.description.custom_properties.get(name) {
1591 if let Some(orig_decl) = enclosing_component
1592 .description
1593 .original
1594 .root_element
1595 .borrow()
1596 .property_declarations
1597 .get(name)
1598 {
1599 if !check_value_type(&value, &orig_decl.property_type) {
1601 return Err(SetPropertyError::WrongType);
1602 }
1603 }
1604 unsafe {
1605 let p = Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset));
1606 return x
1607 .prop
1608 .set(p, value, maybe_animation.as_animation())
1609 .map_err(|()| SetPropertyError::WrongType);
1610 }
1611 } else if enclosing_component.description.original.is_global() {
1612 return Err(SetPropertyError::NoSuchProperty);
1613 }
1614 };
1615 let item_info = &enclosing_component.description.items[element.borrow().id.as_str()];
1616 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1617 let p = &item_info.rtti.properties.get(name).ok_or(SetPropertyError::NoSuchProperty)?;
1618 p.set(item, value, maybe_animation.as_animation())
1619 .map_err(|()| SetPropertyError::WrongType)?;
1620 }
1621 ComponentInstance::GlobalComponent(glob) => {
1622 glob.as_ref().set_property(name, value)?;
1623 }
1624 }
1625 Ok(())
1626}
1627
1628fn check_value_type(value: &Value, ty: &Type) -> bool {
1630 match ty {
1631 Type::Void => true,
1632 Type::Invalid
1633 | Type::InferredProperty
1634 | Type::InferredCallback
1635 | Type::Callback { .. }
1636 | Type::Function { .. }
1637 | Type::ElementReference => panic!("not valid property type"),
1638 Type::Float32 => matches!(value, Value::Number(_)),
1639 Type::Int32 => matches!(value, Value::Number(_)),
1640 Type::String => matches!(value, Value::String(_)),
1641 Type::Color => matches!(value, Value::Brush(_)),
1642 Type::UnitProduct(_)
1643 | Type::Duration
1644 | Type::PhysicalLength
1645 | Type::LogicalLength
1646 | Type::Rem
1647 | Type::Angle
1648 | Type::Percent => matches!(value, Value::Number(_)),
1649 Type::Image => matches!(value, Value::Image(_)),
1650 Type::Bool => matches!(value, Value::Bool(_)),
1651 Type::Model => {
1652 matches!(value, Value::Model(_) | Value::Bool(_) | Value::Number(_))
1653 }
1654 Type::PathData => matches!(value, Value::PathData(_)),
1655 Type::Easing => matches!(value, Value::EasingCurve(_)),
1656 Type::Brush => matches!(value, Value::Brush(_)),
1657 Type::Array(inner) => {
1658 matches!(value, Value::Model(m) if m.iter().all(|v| check_value_type(&v, inner)))
1659 }
1660 Type::Struct(s) => {
1661 matches!(value, Value::Struct(str) if str.iter().all(|(k, v)| s.fields.get(k).is_some_and(|ty| check_value_type(v, ty))))
1662 }
1663 Type::Enumeration(en) => {
1664 matches!(value, Value::EnumerationValue(name, _) if name == en.name.as_str())
1665 }
1666 Type::LayoutCache => matches!(value, Value::LayoutCache(_)),
1667 Type::ComponentFactory => matches!(value, Value::ComponentFactory(_)),
1668 }
1669}
1670
1671pub(crate) fn invoke_callback(
1672 component_instance: &ComponentInstance,
1673 element: &ElementRc,
1674 callback_name: &SmolStr,
1675 args: &[Value],
1676) -> Option<Value> {
1677 generativity::make_guard!(guard);
1678 match enclosing_component_instance_for_element(element, component_instance, guard) {
1679 ComponentInstance::InstanceRef(enclosing_component) => {
1680 let description = enclosing_component.description;
1681 let element = element.borrow();
1682 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1683 {
1684 if let Some(callback_offset) = description.custom_callbacks.get(callback_name) {
1685 let callback = callback_offset.apply(&*enclosing_component.instance);
1686 let res = callback.call(args);
1687 return Some(if res != Value::Void {
1688 res
1689 } else if let Some(Type::Callback(callback)) = description
1690 .original
1691 .root_element
1692 .borrow()
1693 .property_declarations
1694 .get(callback_name)
1695 .map(|d| &d.property_type)
1696 {
1697 default_value_for_type(&callback.return_type)
1701 } else {
1702 res
1703 });
1704 } else if enclosing_component.description.original.is_global() {
1705 return None;
1706 }
1707 };
1708 let item_info = &description.items[element.id.as_str()];
1709 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1710 item_info
1711 .rtti
1712 .callbacks
1713 .get(callback_name.as_str())
1714 .map(|callback| callback.call(item, args))
1715 }
1716 ComponentInstance::GlobalComponent(global) => {
1717 Some(global.as_ref().invoke_callback(callback_name, args).unwrap())
1718 }
1719 }
1720}
1721
1722pub(crate) fn set_callback_handler(
1723 component_instance: &ComponentInstance,
1724 element: &ElementRc,
1725 callback_name: &str,
1726 handler: CallbackHandler,
1727) -> Result<(), ()> {
1728 generativity::make_guard!(guard);
1729 match enclosing_component_instance_for_element(element, component_instance, guard) {
1730 ComponentInstance::InstanceRef(enclosing_component) => {
1731 let description = enclosing_component.description;
1732 let element = element.borrow();
1733 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1734 {
1735 if let Some(callback_offset) = description.custom_callbacks.get(callback_name) {
1736 let callback = callback_offset.apply(&*enclosing_component.instance);
1737 callback.set_handler(handler);
1738 return Ok(());
1739 } else if enclosing_component.description.original.is_global() {
1740 return Err(());
1741 }
1742 };
1743 let item_info = &description.items[element.id.as_str()];
1744 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1745 if let Some(callback) = item_info.rtti.callbacks.get(callback_name) {
1746 callback.set_handler(item, handler);
1747 Ok(())
1748 } else {
1749 Err(())
1750 }
1751 }
1752 ComponentInstance::GlobalComponent(global) => {
1753 global.as_ref().set_callback_handler(callback_name, handler)
1754 }
1755 }
1756}
1757
1758pub(crate) fn call_function(
1762 component_instance: &ComponentInstance,
1763 element: &ElementRc,
1764 function_name: &str,
1765 args: Vec<Value>,
1766) -> Option<Value> {
1767 generativity::make_guard!(guard);
1768 match enclosing_component_instance_for_element(element, component_instance, guard) {
1769 ComponentInstance::InstanceRef(c) => {
1770 let mut ctx = EvalLocalContext::from_function_arguments(c, args);
1771 eval_expression(
1772 &element.borrow().bindings.get(function_name)?.borrow().expression,
1773 &mut ctx,
1774 )
1775 .into()
1776 }
1777 ComponentInstance::GlobalComponent(g) => g.as_ref().eval_function(function_name, args).ok(),
1778 }
1779}
1780
1781pub fn enclosing_component_for_element<'a, 'old_id, 'new_id>(
1784 element: &'a ElementRc,
1785 component: InstanceRef<'a, 'old_id>,
1786 _guard: generativity::Guard<'new_id>,
1787) -> InstanceRef<'a, 'new_id> {
1788 let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
1789 if Rc::ptr_eq(enclosing, &component.description.original) {
1790 unsafe {
1792 std::mem::transmute::<InstanceRef<'a, 'old_id>, InstanceRef<'a, 'new_id>>(component)
1793 }
1794 } else {
1795 assert!(!enclosing.is_global());
1796 let static_guard = unsafe { generativity::Guard::new(generativity::Id::<'static>::new()) };
1800
1801 let parent_instance = component.parent_instance(static_guard).unwrap();
1802 enclosing_component_for_element(element, parent_instance, _guard)
1803 }
1804}
1805
1806pub(crate) fn enclosing_component_instance_for_element<'a, 'new_id>(
1809 element: &'a ElementRc,
1810 component_instance: &ComponentInstance<'a, '_>,
1811 guard: generativity::Guard<'new_id>,
1812) -> ComponentInstance<'a, 'new_id> {
1813 let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
1814 match component_instance {
1815 ComponentInstance::InstanceRef(component) => {
1816 if enclosing.is_global() && !Rc::ptr_eq(enclosing, &component.description.original) {
1817 let root = component.toplevel_instance(guard);
1818 ComponentInstance::GlobalComponent(
1819 root.description
1820 .extra_data_offset
1821 .apply(root.instance.get_ref())
1822 .globals
1823 .get()
1824 .unwrap()
1825 .get(enclosing.root_element.borrow().id.as_str())
1826 .unwrap(),
1827 )
1828 } else {
1829 ComponentInstance::InstanceRef(enclosing_component_for_element(
1830 element, *component, guard,
1831 ))
1832 }
1833 }
1834 ComponentInstance::GlobalComponent(global) => {
1835 ComponentInstance::GlobalComponent(global.clone())
1837 }
1838 }
1839}
1840
1841pub fn new_struct_with_bindings<ElementType: 'static + Default + corelib::rtti::BuiltinItem>(
1842 bindings: &i_slint_compiler::object_tree::BindingsMap,
1843 local_context: &mut EvalLocalContext,
1844) -> ElementType {
1845 let mut element = ElementType::default();
1846 for (prop, info) in ElementType::fields::<Value>().into_iter() {
1847 if let Some(binding) = &bindings.get(prop) {
1848 let value = eval_expression(&binding.borrow(), local_context);
1849 info.set_field(&mut element, value).unwrap();
1850 }
1851 }
1852 element
1853}
1854
1855fn convert_from_lyon_path<'a>(
1856 events_it: impl IntoIterator<Item = &'a i_slint_compiler::expression_tree::Expression>,
1857 points_it: impl IntoIterator<Item = &'a i_slint_compiler::expression_tree::Expression>,
1858 local_context: &mut EvalLocalContext,
1859) -> PathData {
1860 let events = events_it
1861 .into_iter()
1862 .map(|event_expr| eval_expression(event_expr, local_context).try_into().unwrap())
1863 .collect::<SharedVector<_>>();
1864
1865 let points = points_it
1866 .into_iter()
1867 .map(|point_expr| {
1868 let point_value = eval_expression(point_expr, local_context);
1869 let point_struct: Struct = point_value.try_into().unwrap();
1870 let mut point = i_slint_core::graphics::Point::default();
1871 let x: f64 = point_struct.get_field("x").unwrap().clone().try_into().unwrap();
1872 let y: f64 = point_struct.get_field("y").unwrap().clone().try_into().unwrap();
1873 point.x = x as _;
1874 point.y = y as _;
1875 point
1876 })
1877 .collect::<SharedVector<_>>();
1878
1879 PathData::Events(events, points)
1880}
1881
1882pub fn convert_path(path: &ExprPath, local_context: &mut EvalLocalContext) -> PathData {
1883 match path {
1884 ExprPath::Elements(elements) => PathData::Elements(
1885 elements
1886 .iter()
1887 .map(|element| convert_path_element(element, local_context))
1888 .collect::<SharedVector<PathElement>>(),
1889 ),
1890 ExprPath::Events(events, points) => {
1891 convert_from_lyon_path(events.iter(), points.iter(), local_context)
1892 }
1893 ExprPath::Commands(commands) => {
1894 if let Value::String(commands) = eval_expression(commands, local_context) {
1895 PathData::Commands(commands)
1896 } else {
1897 panic!("binding to path commands does not evaluate to string");
1898 }
1899 }
1900 }
1901}
1902
1903fn convert_path_element(
1904 expr_element: &ExprPathElement,
1905 local_context: &mut EvalLocalContext,
1906) -> PathElement {
1907 match expr_element.element_type.native_class.class_name.as_str() {
1908 "MoveTo" => {
1909 PathElement::MoveTo(new_struct_with_bindings(&expr_element.bindings, local_context))
1910 }
1911 "LineTo" => {
1912 PathElement::LineTo(new_struct_with_bindings(&expr_element.bindings, local_context))
1913 }
1914 "ArcTo" => {
1915 PathElement::ArcTo(new_struct_with_bindings(&expr_element.bindings, local_context))
1916 }
1917 "CubicTo" => {
1918 PathElement::CubicTo(new_struct_with_bindings(&expr_element.bindings, local_context))
1919 }
1920 "QuadraticTo" => PathElement::QuadraticTo(new_struct_with_bindings(
1921 &expr_element.bindings,
1922 local_context,
1923 )),
1924 "Close" => PathElement::Close,
1925 _ => panic!(
1926 "Cannot create unsupported path element {}",
1927 expr_element.element_type.native_class.class_name
1928 ),
1929 }
1930}
1931
1932pub fn default_value_for_type(ty: &Type) -> Value {
1934 match ty {
1935 Type::Float32 | Type::Int32 => Value::Number(0.),
1936 Type::String => Value::String(Default::default()),
1937 Type::Color | Type::Brush => Value::Brush(Default::default()),
1938 Type::Duration | Type::Angle | Type::PhysicalLength | Type::LogicalLength | Type::Rem => {
1939 Value::Number(0.)
1940 }
1941 Type::Image => Value::Image(Default::default()),
1942 Type::Bool => Value::Bool(false),
1943 Type::Callback { .. } => Value::Void,
1944 Type::Struct(s) => Value::Struct(
1945 s.fields
1946 .iter()
1947 .map(|(n, t)| (n.to_string(), default_value_for_type(t)))
1948 .collect::<Struct>(),
1949 ),
1950 Type::Array(_) | Type::Model => Value::Model(Default::default()),
1951 Type::Percent => Value::Number(0.),
1952 Type::Enumeration(e) => Value::EnumerationValue(
1953 e.name.to_string(),
1954 e.values.get(e.default_value).unwrap().to_string(),
1955 ),
1956 Type::Easing => Value::EasingCurve(Default::default()),
1957 Type::Void | Type::Invalid => Value::Void,
1958 Type::UnitProduct(_) => Value::Number(0.),
1959 Type::PathData => Value::PathData(Default::default()),
1960 Type::LayoutCache => Value::LayoutCache(Default::default()),
1961 Type::ComponentFactory => Value::ComponentFactory(Default::default()),
1962 Type::InferredProperty
1963 | Type::InferredCallback
1964 | Type::ElementReference
1965 | Type::Function { .. } => {
1966 panic!("There can't be such property")
1967 }
1968 }
1969}
1970
1971pub struct MenuWrapper {
1972 entries: Expression,
1973 sub_menu: NamedReference,
1974 activated: NamedReference,
1975 item_tree: crate::dynamic_item_tree::ErasedItemTreeBoxWeak,
1976}
1977i_slint_core::MenuVTable_static!(static MENU_WRAPPER_VTABLE for MenuWrapper);
1978impl Menu for MenuWrapper {
1979 fn sub_menu(&self, parent: Option<&MenuEntry>, result: &mut SharedVector<MenuEntry>) {
1980 let Some(s) = self.item_tree.upgrade() else { return };
1981 generativity::make_guard!(guard);
1982 let compo_box = s.unerase(guard);
1983 let instance_ref = compo_box.borrow_instance();
1984 let res = match parent {
1985 None => eval_expression(
1986 &self.entries,
1987 &mut EvalLocalContext::from_component_instance(instance_ref),
1988 ),
1989 Some(parent) => {
1990 let instance_ref = ComponentInstance::InstanceRef(instance_ref);
1991 invoke_callback(
1992 &instance_ref,
1993 &self.sub_menu.element(),
1994 self.sub_menu.name(),
1995 &[parent.clone().into()],
1996 )
1997 .unwrap()
1998 }
1999 };
2000 let Value::Model(model) = res else { panic!("Not a model of menu entries {res:?}") };
2001 *result = model.iter().map(|v| v.try_into().unwrap()).collect();
2002 }
2003 fn activate(&self, entry: &MenuEntry) {
2004 let Some(s) = self.item_tree.upgrade() else { return };
2005 generativity::make_guard!(guard);
2006 let compo_box = s.unerase(guard);
2007 let instance_ref = compo_box.borrow_instance();
2008 let instance_ref = ComponentInstance::InstanceRef(instance_ref);
2009 invoke_callback(
2010 &instance_ref,
2011 &self.activated.element(),
2012 self.activated.name(),
2013 &[entry.clone().into()],
2014 )
2015 .unwrap();
2016 }
2017}
2018
2019fn menu_item_tree_properties(
2020 menu: MenuFromItemTree,
2021) -> (Box<dyn Fn() -> Value>, CallbackHandler, CallbackHandler) {
2022 let context_menu_item_tree = Rc::new(menu);
2023 let context_menu_item_tree_ = context_menu_item_tree.clone();
2024 let entries = Box::new(move || {
2025 let mut entries = SharedVector::default();
2026 context_menu_item_tree_.sub_menu(None, &mut entries);
2027 Value::Model(ModelRc::new(VecModel::from(
2028 entries.into_iter().map(Value::from).collect::<Vec<_>>(),
2029 )))
2030 });
2031 let context_menu_item_tree_ = context_menu_item_tree.clone();
2032 let sub_menu = Box::new(move |args: &[Value]| -> Value {
2033 let mut entries = SharedVector::default();
2034 context_menu_item_tree_.sub_menu(Some(&args[0].clone().try_into().unwrap()), &mut entries);
2035 Value::Model(ModelRc::new(VecModel::from(
2036 entries.into_iter().map(Value::from).collect::<Vec<_>>(),
2037 )))
2038 });
2039 let activated = Box::new(move |args: &[Value]| -> Value {
2040 context_menu_item_tree.activate(&args[0].clone().try_into().unwrap());
2041 Value::Void
2042 });
2043 (entries, sub_menu, activated)
2044}