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