1use crate::api::{CompilationResult, ComponentDefinition, Value};
5use crate::global_component::CompiledGlobalCollection;
6use crate::{dynamic_type, eval};
7use core::ptr::NonNull;
8use dynamic_type::{Instance, InstanceBox};
9use i_slint_compiler::expression_tree::{Expression, NamedReference};
10use i_slint_compiler::langtype::{BuiltinPrivateStruct, StructName, Type};
11use i_slint_compiler::object_tree::{ElementRc, ElementWeak, TransitionDirection};
12use i_slint_compiler::{CompilerConfiguration, generator, object_tree, parser};
13use i_slint_compiler::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration};
14use i_slint_core::accessibility::{
15 AccessibilityAction, AccessibleStringProperty, SupportedAccessibilityAction,
16};
17use i_slint_core::api::LogicalPosition;
18use i_slint_core::component_factory::ComponentFactory;
19use i_slint_core::item_tree::{
20 IndexRange, ItemRc, ItemTree, ItemTreeNode, ItemTreeRef, ItemTreeRefPin, ItemTreeVTable,
21 ItemTreeWeak, ItemVisitorRefMut, ItemVisitorVTable, ItemWeak, TraversalOrder,
22 VisitChildrenResult,
23};
24use i_slint_core::items::{
25 AccessibleRole, ItemRef, ItemVTable, PopupClosePolicy, PropertyAnimation,
26};
27use i_slint_core::layout::{LayoutInfo, LayoutItemInfo, Orientation};
28use i_slint_core::lengths::{LogicalLength, LogicalRect};
29use i_slint_core::menus::MenuFromItemTree;
30use i_slint_core::model::{ModelRc, RepeatedItemTree, Repeater};
31use i_slint_core::platform::PlatformError;
32use i_slint_core::properties::{ChangeTracker, InterpolatedPropertyValue};
33use i_slint_core::rtti::{self, AnimatedBindingKind, FieldOffset, PropertyInfo};
34use i_slint_core::slice::Slice;
35use i_slint_core::styled_text::StyledText;
36use i_slint_core::timers::Timer;
37use i_slint_core::window::{WindowAdapterRc, WindowInner};
38use i_slint_core::{Brush, Color, Property, SharedString, SharedVector};
39#[cfg(feature = "internal")]
40use itertools::Either;
41use once_cell::unsync::{Lazy, OnceCell};
42use smol_str::{SmolStr, ToSmolStr};
43use std::collections::BTreeMap;
44use std::collections::HashMap;
45use std::num::NonZeroU32;
46use std::rc::Weak;
47use std::{pin::Pin, rc::Rc};
48
49pub const SPECIAL_PROPERTY_INDEX: &str = "$index";
50pub const SPECIAL_PROPERTY_MODEL_DATA: &str = "$model_data";
51
52pub(crate) type CallbackHandler = Box<dyn Fn(&[Value]) -> Value>;
53
54pub struct ItemTreeBox<'id> {
55 instance: InstanceBox<'id>,
56 description: Rc<ItemTreeDescription<'id>>,
57}
58
59impl<'id> ItemTreeBox<'id> {
60 pub fn borrow(&self) -> ItemTreeRefPin<'_> {
62 self.borrow_instance().borrow()
63 }
64
65 pub fn description(&self) -> Rc<ItemTreeDescription<'id>> {
67 self.description.clone()
68 }
69
70 pub fn borrow_instance<'a>(&'a self) -> InstanceRef<'a, 'id> {
71 InstanceRef { instance: self.instance.as_pin_ref(), description: &self.description }
72 }
73
74 pub fn window_adapter_ref(&self) -> Result<&WindowAdapterRc, PlatformError> {
75 let root_weak = vtable::VWeak::into_dyn(self.borrow_instance().root_weak().clone());
76 InstanceRef::get_or_init_window_adapter_ref(
77 &self.description,
78 root_weak,
79 true,
80 self.instance.as_pin_ref().get_ref(),
81 )
82 }
83}
84
85pub(crate) type ErasedItemTreeBoxWeak = vtable::VWeak<ItemTreeVTable, ErasedItemTreeBox>;
86
87pub(crate) struct ItemWithinItemTree {
88 offset: usize,
89 pub(crate) rtti: Rc<ItemRTTI>,
90 elem: ElementRc,
91}
92
93impl ItemWithinItemTree {
94 pub(crate) unsafe fn item_from_item_tree(
96 &self,
97 mem: *const u8,
98 ) -> Pin<vtable::VRef<'_, ItemVTable>> {
99 unsafe {
100 Pin::new_unchecked(vtable::VRef::from_raw(
101 NonNull::from(self.rtti.vtable),
102 NonNull::new(mem.add(self.offset) as _).unwrap(),
103 ))
104 }
105 }
106
107 pub(crate) fn item_index(&self) -> u32 {
108 *self.elem.borrow().item_index.get().unwrap()
109 }
110}
111
112pub(crate) struct PropertiesWithinComponent {
113 pub(crate) offset: usize,
114 pub(crate) prop: Box<dyn PropertyInfo<u8, Value>>,
115}
116
117pub(crate) struct RepeaterWithinItemTree<'par_id, 'sub_id> {
118 pub(crate) item_tree_to_repeat: Rc<ItemTreeDescription<'sub_id>>,
120 pub(crate) model: Expression,
122 offset: FieldOffset<Instance<'par_id>, Repeater<ErasedItemTreeBox>>,
124 is_conditional: bool,
127}
128
129impl RepeatedItemTree for ErasedItemTreeBox {
130 type Data = Value;
131
132 fn update(&self, index: usize, data: Self::Data) {
133 generativity::make_guard!(guard);
134 let s = self.unerase(guard);
135 let is_repeated = s.description.original.parent_element.upgrade().is_some_and(|p| {
136 p.borrow().repeated.as_ref().is_some_and(|r| !r.is_conditional_element)
137 });
138 if is_repeated {
139 s.description.set_property(s.borrow(), SPECIAL_PROPERTY_INDEX, index.into()).unwrap();
140 s.description.set_property(s.borrow(), SPECIAL_PROPERTY_MODEL_DATA, data).unwrap();
141 }
142 }
143
144 fn init(&self) {
145 self.run_setup_code();
146 }
147
148 fn listview_layout(self: Pin<&Self>, offset_y: &mut LogicalLength) -> LogicalLength {
149 generativity::make_guard!(guard);
150 let s = self.unerase(guard);
151
152 let geom = s.description.original.root_element.borrow().geometry_props.clone().unwrap();
153
154 crate::eval::store_property(
155 s.borrow_instance(),
156 &geom.y.element(),
157 geom.y.name(),
158 Value::Number(offset_y.get() as f64),
159 )
160 .expect("cannot set y");
161
162 let h: LogicalLength = crate::eval::load_property(
163 s.borrow_instance(),
164 &geom.height.element(),
165 geom.height.name(),
166 )
167 .expect("missing height")
168 .try_into()
169 .expect("height not the right type");
170
171 *offset_y += h;
172 LogicalLength::new(self.borrow().as_ref().layout_info(Orientation::Horizontal).min)
173 }
174
175 fn layout_item_info(
176 self: Pin<&Self>,
177 o: Orientation,
178 child_index: Option<usize>,
179 ) -> LayoutItemInfo {
180 generativity::make_guard!(guard);
181 let s = self.unerase(guard);
182
183 if let Some(index) = child_index {
184 let instance_ref = s.borrow_instance();
185 let root_element = &s.description.original.root_element;
186
187 let children = root_element.borrow().children.clone();
188 if let Some(child_elem) = children.get(index) {
189 let layout_info = crate::eval_layout::get_layout_info(
191 child_elem,
192 instance_ref,
193 &instance_ref.window_adapter(),
194 crate::eval_layout::from_runtime(o),
195 );
196 return LayoutItemInfo { constraint: layout_info };
197 } else {
198 panic!(
199 "child_index {} out of bounds for repeated item {}",
200 index,
201 s.description().id()
202 );
203 }
204 }
205
206 LayoutItemInfo { constraint: self.borrow().as_ref().layout_info(o) }
207 }
208}
209
210impl ItemTree for ErasedItemTreeBox {
211 fn visit_children_item(
212 self: Pin<&Self>,
213 index: isize,
214 order: TraversalOrder,
215 visitor: ItemVisitorRefMut,
216 ) -> VisitChildrenResult {
217 self.borrow().as_ref().visit_children_item(index, order, visitor)
218 }
219
220 fn layout_info(self: Pin<&Self>, orientation: Orientation) -> i_slint_core::layout::LayoutInfo {
221 self.borrow().as_ref().layout_info(orientation)
222 }
223
224 fn get_item_tree(self: Pin<&Self>) -> Slice<'_, ItemTreeNode> {
225 get_item_tree(self.get_ref().borrow())
226 }
227
228 fn get_item_ref(self: Pin<&Self>, index: u32) -> Pin<ItemRef<'_>> {
229 unsafe { get_item_ref(self.get_ref().borrow(), index) }
233 }
234
235 fn get_subtree_range(self: Pin<&Self>, index: u32) -> IndexRange {
236 self.borrow().as_ref().get_subtree_range(index)
237 }
238
239 fn get_subtree(self: Pin<&Self>, index: u32, subindex: usize, result: &mut ItemTreeWeak) {
240 self.borrow().as_ref().get_subtree(index, subindex, result);
241 }
242
243 fn parent_node(self: Pin<&Self>, result: &mut ItemWeak) {
244 self.borrow().as_ref().parent_node(result)
245 }
246
247 fn embed_component(
248 self: core::pin::Pin<&Self>,
249 parent_component: &ItemTreeWeak,
250 item_tree_index: u32,
251 ) -> bool {
252 self.borrow().as_ref().embed_component(parent_component, item_tree_index)
253 }
254
255 fn subtree_index(self: Pin<&Self>) -> usize {
256 self.borrow().as_ref().subtree_index()
257 }
258
259 fn item_geometry(self: Pin<&Self>, item_index: u32) -> i_slint_core::lengths::LogicalRect {
260 self.borrow().as_ref().item_geometry(item_index)
261 }
262
263 fn accessible_role(self: Pin<&Self>, index: u32) -> AccessibleRole {
264 self.borrow().as_ref().accessible_role(index)
265 }
266
267 fn accessible_string_property(
268 self: Pin<&Self>,
269 index: u32,
270 what: AccessibleStringProperty,
271 result: &mut SharedString,
272 ) -> bool {
273 self.borrow().as_ref().accessible_string_property(index, what, result)
274 }
275
276 fn window_adapter(self: Pin<&Self>, do_create: bool, result: &mut Option<WindowAdapterRc>) {
277 self.borrow().as_ref().window_adapter(do_create, result);
278 }
279
280 fn accessibility_action(self: core::pin::Pin<&Self>, index: u32, action: &AccessibilityAction) {
281 self.borrow().as_ref().accessibility_action(index, action)
282 }
283
284 fn supported_accessibility_actions(
285 self: core::pin::Pin<&Self>,
286 index: u32,
287 ) -> SupportedAccessibilityAction {
288 self.borrow().as_ref().supported_accessibility_actions(index)
289 }
290
291 fn item_element_infos(
292 self: core::pin::Pin<&Self>,
293 index: u32,
294 result: &mut SharedString,
295 ) -> bool {
296 self.borrow().as_ref().item_element_infos(index, result)
297 }
298}
299
300i_slint_core::ItemTreeVTable_static!(static COMPONENT_BOX_VT for ErasedItemTreeBox);
301
302impl Drop for ErasedItemTreeBox {
303 fn drop(&mut self) {
304 generativity::make_guard!(guard);
305 let unerase = self.unerase(guard);
306 let instance_ref = unerase.borrow_instance();
307
308 let maybe_window_adapter = instance_ref
309 .description
310 .extra_data_offset
311 .apply(instance_ref.as_ref())
312 .globals
313 .get()
314 .and_then(|globals| globals.window_adapter())
315 .and_then(|wa| wa.get());
316 if let Some(window_adapter) = maybe_window_adapter {
317 i_slint_core::item_tree::unregister_item_tree(
318 instance_ref.instance,
319 vtable::VRef::new(self),
320 instance_ref.description.item_array.as_slice(),
321 &window_adapter,
322 );
323 }
324 }
325}
326
327pub type DynamicComponentVRc = vtable::VRc<ItemTreeVTable, ErasedItemTreeBox>;
328
329#[derive(Default)]
330pub(crate) struct ComponentExtraData {
331 pub(crate) globals: OnceCell<crate::global_component::GlobalStorage>,
332 pub(crate) self_weak: OnceCell<ErasedItemTreeBoxWeak>,
333 pub(crate) embedding_position: OnceCell<(ItemTreeWeak, u32)>,
334}
335
336struct ErasedRepeaterWithinComponent<'id>(RepeaterWithinItemTree<'id, 'static>);
337impl<'id, 'sub_id> From<RepeaterWithinItemTree<'id, 'sub_id>>
338 for ErasedRepeaterWithinComponent<'id>
339{
340 fn from(from: RepeaterWithinItemTree<'id, 'sub_id>) -> Self {
341 Self(unsafe {
344 core::mem::transmute::<
345 RepeaterWithinItemTree<'id, 'sub_id>,
346 RepeaterWithinItemTree<'id, 'static>,
347 >(from)
348 })
349 }
350}
351impl<'id> ErasedRepeaterWithinComponent<'id> {
352 pub fn unerase<'a, 'sub_id>(
353 &'a self,
354 _guard: generativity::Guard<'sub_id>,
355 ) -> &'a RepeaterWithinItemTree<'id, 'sub_id> {
356 unsafe {
358 core::mem::transmute::<
359 &'a RepeaterWithinItemTree<'id, 'static>,
360 &'a RepeaterWithinItemTree<'id, 'sub_id>,
361 >(&self.0)
362 }
363 }
364
365 unsafe fn get_untagged(&self) -> &RepeaterWithinItemTree<'id, 'static> {
369 &self.0
370 }
371}
372
373type Callback = i_slint_core::Callback<[Value], Value>;
374
375#[derive(Clone)]
376pub struct ErasedItemTreeDescription(Rc<ItemTreeDescription<'static>>);
377impl ErasedItemTreeDescription {
378 pub fn unerase<'a, 'id>(
379 &'a self,
380 _guard: generativity::Guard<'id>,
381 ) -> &'a Rc<ItemTreeDescription<'id>> {
382 unsafe {
384 core::mem::transmute::<
385 &'a Rc<ItemTreeDescription<'static>>,
386 &'a Rc<ItemTreeDescription<'id>>,
387 >(&self.0)
388 }
389 }
390}
391impl<'id> From<Rc<ItemTreeDescription<'id>>> for ErasedItemTreeDescription {
392 fn from(from: Rc<ItemTreeDescription<'id>>) -> Self {
393 Self(unsafe {
395 core::mem::transmute::<Rc<ItemTreeDescription<'id>>, Rc<ItemTreeDescription<'static>>>(
396 from,
397 )
398 })
399 }
400}
401
402#[repr(C)]
409pub struct ItemTreeDescription<'id> {
410 pub(crate) ct: ItemTreeVTable,
411 dynamic_type: Rc<dynamic_type::TypeInfo<'id>>,
413 item_tree: Vec<ItemTreeNode>,
414 item_array:
415 Vec<vtable::VOffset<crate::dynamic_type::Instance<'id>, ItemVTable, vtable::AllowPin>>,
416 pub(crate) items: HashMap<SmolStr, ItemWithinItemTree>,
417 pub(crate) custom_properties: HashMap<SmolStr, PropertiesWithinComponent>,
418 pub(crate) custom_callbacks: HashMap<SmolStr, FieldOffset<Instance<'id>, Callback>>,
419 repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
420 pub repeater_names: HashMap<SmolStr, usize>,
422 pub(crate) parent_item_tree_offset:
424 Option<FieldOffset<Instance<'id>, OnceCell<ErasedItemTreeBoxWeak>>>,
425 pub(crate) root_offset: FieldOffset<Instance<'id>, OnceCell<ErasedItemTreeBoxWeak>>,
426 pub(crate) extra_data_offset: FieldOffset<Instance<'id>, ComponentExtraData>,
428 pub(crate) original: Rc<object_tree::Component>,
430 pub(crate) original_elements: Vec<ElementRc>,
432 public_properties: BTreeMap<SmolStr, PropertyDeclaration>,
434 change_trackers: Option<(
435 FieldOffset<Instance<'id>, OnceCell<Vec<ChangeTracker>>>,
436 Vec<(NamedReference, Expression)>,
437 )>,
438 timers: Vec<FieldOffset<Instance<'id>, Timer>>,
439 popup_ids: std::cell::RefCell<HashMap<SmolStr, NonZeroU32>>,
441
442 pub(crate) popup_menu_description: PopupMenuDescription,
443
444 compiled_globals: Option<Rc<CompiledGlobalCollection>>,
446
447 #[cfg(feature = "internal-highlight")]
450 pub(crate) type_loader:
451 std::cell::OnceCell<std::rc::Rc<i_slint_compiler::typeloader::TypeLoader>>,
452 #[cfg(feature = "internal-highlight")]
455 pub(crate) raw_type_loader:
456 std::cell::OnceCell<Option<std::rc::Rc<i_slint_compiler::typeloader::TypeLoader>>>,
457
458 pub(crate) debug_handler: std::cell::RefCell<
459 Rc<dyn Fn(Option<&i_slint_compiler::diagnostics::SourceLocation>, &str)>,
460 >,
461}
462
463#[derive(Clone, derive_more::From)]
464pub(crate) enum PopupMenuDescription {
465 Rc(Rc<ErasedItemTreeDescription>),
466 Weak(Weak<ErasedItemTreeDescription>),
467}
468impl PopupMenuDescription {
469 pub fn unerase<'id>(&self, guard: generativity::Guard<'id>) -> Rc<ItemTreeDescription<'id>> {
470 match self {
471 PopupMenuDescription::Rc(rc) => rc.unerase(guard).clone(),
472 PopupMenuDescription::Weak(weak) => weak.upgrade().unwrap().unerase(guard).clone(),
473 }
474 }
475}
476
477fn internal_properties_to_public<'a>(
478 prop_iter: impl Iterator<Item = (&'a SmolStr, &'a PropertyDeclaration)> + 'a,
479) -> impl Iterator<
480 Item = (
481 SmolStr,
482 i_slint_compiler::langtype::Type,
483 i_slint_compiler::object_tree::PropertyVisibility,
484 ),
485> + 'a {
486 prop_iter.filter(|(_, v)| v.expose_in_public_api).map(|(s, v)| {
487 let name = v
488 .node
489 .as_ref()
490 .and_then(|n| {
491 n.child_node(parser::SyntaxKind::DeclaredIdentifier)
492 .and_then(|n| n.child_token(parser::SyntaxKind::Identifier))
493 })
494 .map(|n| n.to_smolstr())
495 .unwrap_or_else(|| s.to_smolstr());
496 (name, v.property_type.clone(), v.visibility)
497 })
498}
499
500#[derive(Default)]
501pub enum WindowOptions {
502 #[default]
503 CreateNewWindow,
504 UseExistingWindow(WindowAdapterRc),
505 Embed {
506 parent_item_tree: ItemTreeWeak,
507 parent_item_tree_index: u32,
508 },
509}
510
511impl ItemTreeDescription<'_> {
512 pub fn id(&self) -> &str {
514 self.original.id.as_str()
515 }
516
517 pub fn properties(
521 &self,
522 ) -> impl Iterator<
523 Item = (
524 SmolStr,
525 i_slint_compiler::langtype::Type,
526 i_slint_compiler::object_tree::PropertyVisibility,
527 ),
528 > + '_ {
529 internal_properties_to_public(self.public_properties.iter())
530 }
531
532 pub fn global_names(&self) -> impl Iterator<Item = SmolStr> + '_ {
534 self.compiled_globals
535 .as_ref()
536 .expect("Root component should have globals")
537 .compiled_globals
538 .iter()
539 .filter(|g| g.visible_in_public_api())
540 .flat_map(|g| g.names().into_iter())
541 }
542
543 pub fn global_properties(
544 &self,
545 name: &str,
546 ) -> Option<
547 impl Iterator<
548 Item = (
549 SmolStr,
550 i_slint_compiler::langtype::Type,
551 i_slint_compiler::object_tree::PropertyVisibility,
552 ),
553 > + '_,
554 > {
555 let g = self.compiled_globals.as_ref().expect("Root component should have globals");
556 g.exported_globals_by_name
557 .get(&crate::normalize_identifier(name))
558 .and_then(|global_idx| g.compiled_globals.get(*global_idx))
559 .map(|global| internal_properties_to_public(global.public_properties()))
560 }
561
562 pub fn create(
564 self: Rc<Self>,
565 options: WindowOptions,
566 ) -> Result<DynamicComponentVRc, PlatformError> {
567 i_slint_backend_selector::with_platform(|_b| {
568 Ok(())
570 })?;
571
572 let instance = instantiate(self, None, None, Some(&options), Default::default());
573 if let WindowOptions::UseExistingWindow(existing_adapter) = options {
574 WindowInner::from_pub(existing_adapter.window())
575 .set_component(&vtable::VRc::into_dyn(instance.clone()));
576 }
577 instance.run_setup_code();
578 Ok(instance)
579 }
580
581 pub fn set_property(
587 &self,
588 component: ItemTreeRefPin,
589 name: &str,
590 value: Value,
591 ) -> Result<(), crate::api::SetPropertyError> {
592 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
593 panic!("mismatch instance and vtable");
594 }
595 generativity::make_guard!(guard);
596 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
597 if let Some(alias) = self
598 .original
599 .root_element
600 .borrow()
601 .property_declarations
602 .get(name)
603 .and_then(|d| d.is_alias.as_ref())
604 {
605 eval::store_property(c, &alias.element(), alias.name(), value)
606 } else {
607 eval::store_property(c, &self.original.root_element, name, value)
608 }
609 }
610
611 pub fn set_binding(
616 &self,
617 component: ItemTreeRefPin,
618 name: &str,
619 binding: Box<dyn Fn() -> Value>,
620 ) -> Result<(), ()> {
621 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
622 return Err(());
623 }
624 let x = self.custom_properties.get(name).ok_or(())?;
625 unsafe {
626 x.prop
627 .set_binding(
628 Pin::new_unchecked(&*component.as_ptr().add(x.offset)),
629 binding,
630 i_slint_core::rtti::AnimatedBindingKind::NotAnimated,
631 )
632 .unwrap()
633 };
634 Ok(())
635 }
636
637 pub fn get_property(&self, component: ItemTreeRefPin, name: &str) -> Result<Value, ()> {
642 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
643 return Err(());
644 }
645 generativity::make_guard!(guard);
646 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
648 if let Some(alias) = self
649 .original
650 .root_element
651 .borrow()
652 .property_declarations
653 .get(name)
654 .and_then(|d| d.is_alias.as_ref())
655 {
656 eval::load_property(c, &alias.element(), alias.name())
657 } else {
658 eval::load_property(c, &self.original.root_element, name)
659 }
660 }
661
662 pub fn set_callback_handler(
667 &self,
668 component: Pin<ItemTreeRef>,
669 name: &str,
670 handler: CallbackHandler,
671 ) -> Result<(), ()> {
672 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
673 return Err(());
674 }
675 if let Some(alias) = self
676 .original
677 .root_element
678 .borrow()
679 .property_declarations
680 .get(name)
681 .and_then(|d| d.is_alias.as_ref())
682 {
683 generativity::make_guard!(guard);
684 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
686 let inst = eval::ComponentInstance::InstanceRef(c);
687 eval::set_callback_handler(&inst, &alias.element(), alias.name(), handler)?
688 } else {
689 let x = self.custom_callbacks.get(name).ok_or(())?;
690 let sig = x.apply(unsafe { &*(component.as_ptr() as *const dynamic_type::Instance) });
691 sig.set_handler(handler);
692 }
693 Ok(())
694 }
695
696 pub fn invoke(
701 &self,
702 component: ItemTreeRefPin,
703 name: &SmolStr,
704 args: &[Value],
705 ) -> Result<Value, ()> {
706 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
707 return Err(());
708 }
709 generativity::make_guard!(guard);
710 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
712 let borrow = self.original.root_element.borrow();
713 let decl = borrow.property_declarations.get(name).ok_or(())?;
714
715 let (elem, name) = if let Some(alias) = &decl.is_alias {
716 (alias.element(), alias.name())
717 } else {
718 (self.original.root_element.clone(), name)
719 };
720
721 let inst = eval::ComponentInstance::InstanceRef(c);
722
723 if matches!(&decl.property_type, Type::Function { .. }) {
724 eval::call_function(&inst, &elem, name, args.to_vec()).ok_or(())
725 } else {
726 eval::invoke_callback(&inst, &elem, name, args).ok_or(())
727 }
728 }
729
730 pub fn get_global(
732 &self,
733 component: ItemTreeRefPin,
734 global_name: &str,
735 ) -> Result<Pin<Rc<dyn crate::global_component::GlobalComponent>>, ()> {
736 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
737 return Err(());
738 }
739 generativity::make_guard!(guard);
740 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
742 let extra_data = c.description.extra_data_offset.apply(c.instance.get_ref());
743 let g = extra_data.globals.get().unwrap().get(global_name).clone();
744 g.ok_or(())
745 }
746
747 pub fn recursively_set_debug_handler(
748 &self,
749 handler: Rc<dyn Fn(Option<&i_slint_compiler::diagnostics::SourceLocation>, &str)>,
750 ) {
751 *self.debug_handler.borrow_mut() = handler.clone();
752
753 for r in &self.repeater {
754 generativity::make_guard!(guard);
755 r.unerase(guard).item_tree_to_repeat.recursively_set_debug_handler(handler.clone());
756 }
757 }
758}
759
760#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
761extern "C" fn visit_children_item(
762 component: ItemTreeRefPin,
763 index: isize,
764 order: TraversalOrder,
765 v: ItemVisitorRefMut,
766) -> VisitChildrenResult {
767 generativity::make_guard!(guard);
768 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
769 let comp_rc = instance_ref.self_weak().get().unwrap().upgrade().unwrap();
770 i_slint_core::item_tree::visit_item_tree(
771 instance_ref.instance,
772 &vtable::VRc::into_dyn(comp_rc),
773 get_item_tree(component).as_slice(),
774 index,
775 order,
776 v,
777 |_, order, visitor, index| {
778 if index as usize >= instance_ref.description.repeater.len() {
779 VisitChildrenResult::CONTINUE
781 } else {
782 let rep_in_comp =
785 unsafe { instance_ref.description.repeater[index as usize].get_untagged() };
786 ensure_repeater_updated(instance_ref, rep_in_comp);
787 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
788 repeater.visit(order, visitor)
789 }
790 },
791 )
792}
793
794fn ensure_repeater_updated<'id>(
796 instance_ref: InstanceRef<'_, 'id>,
797 rep_in_comp: &RepeaterWithinItemTree<'id, '_>,
798) {
799 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
800 let init = || {
801 let extra_data = instance_ref.description.extra_data_offset.apply(instance_ref.as_ref());
802 instantiate(
803 rep_in_comp.item_tree_to_repeat.clone(),
804 instance_ref.self_weak().get().cloned(),
805 None,
806 None,
807 extra_data.globals.get().unwrap().clone(),
808 )
809 };
810 if let Some(lv) = &rep_in_comp
811 .item_tree_to_repeat
812 .original
813 .parent_element
814 .upgrade()
815 .unwrap()
816 .borrow()
817 .repeated
818 .as_ref()
819 .unwrap()
820 .is_listview
821 {
822 let assume_property_logical_length =
823 |prop| unsafe { Pin::new_unchecked(&*(prop as *const Property<LogicalLength>)) };
824 let get_prop = |nr: &NamedReference| -> LogicalLength {
825 eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap().try_into().unwrap()
826 };
827 repeater.ensure_updated_listview(
828 init,
829 assume_property_logical_length(get_property_ptr(&lv.viewport_width, instance_ref)),
830 assume_property_logical_length(get_property_ptr(&lv.viewport_height, instance_ref)),
831 assume_property_logical_length(get_property_ptr(&lv.viewport_y, instance_ref)),
832 get_prop(&lv.listview_width),
833 assume_property_logical_length(get_property_ptr(&lv.listview_height, instance_ref)),
834 );
835 } else {
836 repeater.ensure_updated(init);
837 }
838}
839
840pub(crate) struct ItemRTTI {
842 vtable: &'static ItemVTable,
843 type_info: dynamic_type::StaticTypeInfo,
844 pub(crate) properties: HashMap<&'static str, Box<dyn eval::ErasedPropertyInfo>>,
845 pub(crate) callbacks: HashMap<&'static str, Box<dyn eval::ErasedCallbackInfo>>,
846}
847
848fn rtti_for<T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<ItemVTable>>()
849-> (&'static str, Rc<ItemRTTI>) {
850 let rtti = ItemRTTI {
851 vtable: T::static_vtable(),
852 type_info: dynamic_type::StaticTypeInfo::new::<T>(),
853 properties: T::properties()
854 .into_iter()
855 .map(|(k, v)| (k, Box::new(v) as Box<dyn eval::ErasedPropertyInfo>))
856 .collect(),
857 callbacks: T::callbacks()
858 .into_iter()
859 .map(|(k, v)| (k, Box::new(v) as Box<dyn eval::ErasedCallbackInfo>))
860 .collect(),
861 };
862 (T::name(), Rc::new(rtti))
863}
864
865pub async fn load(
869 source: String,
870 path: std::path::PathBuf,
871 mut compiler_config: CompilerConfiguration,
872) -> CompilationResult {
873 let is_native = match &compiler_config.style {
875 Some(s) => s == "native",
876 None => std::env::var("SLINT_STYLE").map_or(true, |s| s == "native"),
877 };
878 if is_native {
879 #[cfg(target_arch = "wasm32")]
881 let target = web_sys::window()
882 .and_then(|window| window.navigator().platform().ok())
883 .map_or("wasm", |platform| {
884 let platform = platform.to_ascii_lowercase();
885 if platform.contains("mac")
886 || platform.contains("iphone")
887 || platform.contains("ipad")
888 {
889 "apple"
890 } else if platform.contains("android") {
891 "android"
892 } else if platform.contains("win") {
893 "windows"
894 } else if platform.contains("linux") {
895 "linux"
896 } else {
897 "wasm"
898 }
899 });
900 #[cfg(not(target_arch = "wasm32"))]
901 let target = "";
902 compiler_config.style = Some(
903 i_slint_common::get_native_style(i_slint_backend_selector::HAS_NATIVE_STYLE, target)
904 .to_string(),
905 );
906 }
907
908 let diag = BuildDiagnostics::default();
909 #[cfg(feature = "internal-highlight")]
910 let (path, mut diag, loader, raw_type_loader) =
911 i_slint_compiler::load_root_file_with_raw_type_loader(
912 &path,
913 &path,
914 source,
915 diag,
916 compiler_config,
917 )
918 .await;
919 #[cfg(not(feature = "internal-highlight"))]
920 let (path, mut diag, loader) =
921 i_slint_compiler::load_root_file(&path, &path, source, diag, compiler_config).await;
922 if diag.has_errors() {
923 return CompilationResult {
924 components: HashMap::new(),
925 diagnostics: diag.into_iter().collect(),
926 #[cfg(feature = "internal")]
927 structs_and_enums: Vec::new(),
928 #[cfg(feature = "internal")]
929 named_exports: Vec::new(),
930 };
931 }
932
933 #[cfg(feature = "internal-highlight")]
934 let loader = Rc::new(loader);
935 #[cfg(feature = "internal-highlight")]
936 let raw_type_loader = raw_type_loader.map(Rc::new);
937
938 let doc = loader.get_document(&path).unwrap();
939
940 let compiled_globals = Rc::new(CompiledGlobalCollection::compile(doc));
941 let mut components = HashMap::new();
942
943 let popup_menu_description = if let Some(popup_menu_impl) = &doc.popup_menu_impl {
944 PopupMenuDescription::Rc(Rc::new_cyclic(|weak| {
945 generativity::make_guard!(guard);
946 ErasedItemTreeDescription::from(generate_item_tree(
947 popup_menu_impl,
948 Some(compiled_globals.clone()),
949 PopupMenuDescription::Weak(weak.clone()),
950 true,
951 guard,
952 ))
953 }))
954 } else {
955 PopupMenuDescription::Weak(Default::default())
956 };
957
958 for c in doc.exported_roots() {
959 generativity::make_guard!(guard);
960 #[allow(unused_mut)]
961 let mut it = generate_item_tree(
962 &c,
963 Some(compiled_globals.clone()),
964 popup_menu_description.clone(),
965 false,
966 guard,
967 );
968 #[cfg(feature = "internal-highlight")]
969 {
970 let _ = it.type_loader.set(loader.clone());
971 let _ = it.raw_type_loader.set(raw_type_loader.clone());
972 }
973 components.insert(c.id.to_string(), ComponentDefinition { inner: it.into() });
974 }
975
976 if components.is_empty() {
977 diag.push_error_with_span("No component found".into(), Default::default());
978 };
979
980 #[cfg(feature = "internal")]
981 let structs_and_enums = doc.used_types.borrow().structs_and_enums.clone();
982
983 #[cfg(feature = "internal")]
984 let named_exports = doc
985 .exports
986 .iter()
987 .filter_map(|export| match &export.1 {
988 Either::Left(component) if !component.is_global() => {
989 Some((&export.0.name, &component.id))
990 }
991 Either::Right(ty) => match &ty {
992 Type::Struct(s) if s.node().is_some() => {
993 if let StructName::User { name, .. } = &s.name {
994 Some((&export.0.name, name))
995 } else {
996 None
997 }
998 }
999 Type::Enumeration(en) => Some((&export.0.name, &en.name)),
1000 _ => None,
1001 },
1002 _ => None,
1003 })
1004 .filter(|(export_name, type_name)| *export_name != *type_name)
1005 .map(|(export_name, type_name)| (type_name.to_string(), export_name.to_string()))
1006 .collect::<Vec<_>>();
1007
1008 CompilationResult {
1009 diagnostics: diag.into_iter().collect(),
1010 components,
1011 #[cfg(feature = "internal")]
1012 structs_and_enums,
1013 #[cfg(feature = "internal")]
1014 named_exports,
1015 }
1016}
1017
1018fn generate_rtti() -> HashMap<&'static str, Rc<ItemRTTI>> {
1019 let mut rtti = HashMap::new();
1020 use i_slint_core::items::*;
1021 rtti.extend(
1022 [
1023 rtti_for::<ComponentContainer>(),
1024 rtti_for::<Empty>(),
1025 rtti_for::<ImageItem>(),
1026 rtti_for::<ClippedImage>(),
1027 rtti_for::<ComplexText>(),
1028 rtti_for::<StyledTextItem>(),
1029 rtti_for::<SimpleText>(),
1030 rtti_for::<Rectangle>(),
1031 rtti_for::<BasicBorderRectangle>(),
1032 rtti_for::<BorderRectangle>(),
1033 rtti_for::<TouchArea>(),
1034 rtti_for::<FocusScope>(),
1035 rtti_for::<SwipeGestureHandler>(),
1036 rtti_for::<Path>(),
1037 rtti_for::<Flickable>(),
1038 rtti_for::<WindowItem>(),
1039 rtti_for::<TextInput>(),
1040 rtti_for::<Clip>(),
1041 rtti_for::<BoxShadow>(),
1042 rtti_for::<Transform>(),
1043 rtti_for::<Opacity>(),
1044 rtti_for::<Layer>(),
1045 rtti_for::<DragArea>(),
1046 rtti_for::<DropArea>(),
1047 rtti_for::<ContextMenu>(),
1048 rtti_for::<MenuItem>(),
1049 ]
1050 .iter()
1051 .cloned(),
1052 );
1053
1054 trait NativeHelper {
1055 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>);
1056 }
1057 impl NativeHelper for () {
1058 fn push(_rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {}
1059 }
1060 impl<
1061 T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<ItemVTable>,
1062 Next: NativeHelper,
1063 > NativeHelper for (T, Next)
1064 {
1065 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {
1066 let info = rtti_for::<T>();
1067 rtti.insert(info.0, info.1);
1068 Next::push(rtti);
1069 }
1070 }
1071 i_slint_backend_selector::NativeWidgets::push(&mut rtti);
1072
1073 rtti
1074}
1075
1076pub(crate) fn generate_item_tree<'id>(
1077 component: &Rc<object_tree::Component>,
1078 compiled_globals: Option<Rc<CompiledGlobalCollection>>,
1079 popup_menu_description: PopupMenuDescription,
1080 is_popup_menu_impl: bool,
1081 guard: generativity::Guard<'id>,
1082) -> Rc<ItemTreeDescription<'id>> {
1083 thread_local! {
1086 static RTTI: Lazy<HashMap<&'static str, Rc<ItemRTTI>>> = Lazy::new(generate_rtti);
1087 }
1088
1089 struct TreeBuilder<'id> {
1090 tree_array: Vec<ItemTreeNode>,
1091 item_array:
1092 Vec<vtable::VOffset<crate::dynamic_type::Instance<'id>, ItemVTable, vtable::AllowPin>>,
1093 original_elements: Vec<ElementRc>,
1094 items_types: HashMap<SmolStr, ItemWithinItemTree>,
1095 type_builder: dynamic_type::TypeBuilder<'id>,
1096 repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
1097 repeater_names: HashMap<SmolStr, usize>,
1098 change_callbacks: Vec<(NamedReference, Expression)>,
1099 popup_menu_description: PopupMenuDescription,
1100 }
1101 impl generator::ItemTreeBuilder for TreeBuilder<'_> {
1102 type SubComponentState = ();
1103
1104 fn push_repeated_item(
1105 &mut self,
1106 item_rc: &ElementRc,
1107 repeater_count: u32,
1108 parent_index: u32,
1109 _component_state: &Self::SubComponentState,
1110 ) {
1111 self.tree_array.push(ItemTreeNode::DynamicTree { index: repeater_count, parent_index });
1112 self.original_elements.push(item_rc.clone());
1113 let item = item_rc.borrow();
1114 let base_component = item.base_type.as_component();
1115 self.repeater_names.insert(item.id.clone(), self.repeater.len());
1116 generativity::make_guard!(guard);
1117 let repeated_element_info = item.repeated.as_ref().unwrap();
1118 self.repeater.push(
1119 RepeaterWithinItemTree {
1120 item_tree_to_repeat: generate_item_tree(
1121 base_component,
1122 None,
1123 self.popup_menu_description.clone(),
1124 false,
1125 guard,
1126 ),
1127 offset: self.type_builder.add_field_type::<Repeater<ErasedItemTreeBox>>(),
1128 model: repeated_element_info.model.clone(),
1129 is_conditional: repeated_element_info.is_conditional_element,
1130 }
1131 .into(),
1132 );
1133 }
1134
1135 fn push_native_item(
1136 &mut self,
1137 rc_item: &ElementRc,
1138 child_offset: u32,
1139 parent_index: u32,
1140 _component_state: &Self::SubComponentState,
1141 ) {
1142 let item = rc_item.borrow();
1143 let rt = RTTI.with(|rtti| {
1144 rtti.get(&*item.base_type.as_native().class_name)
1145 .unwrap_or_else(|| {
1146 panic!(
1147 "Native type not registered: {}",
1148 item.base_type.as_native().class_name
1149 )
1150 })
1151 .clone()
1152 });
1153
1154 let offset = self.type_builder.add_field(rt.type_info);
1155
1156 self.tree_array.push(ItemTreeNode::Item {
1157 is_accessible: !item.accessibility_props.0.is_empty(),
1158 children_index: child_offset,
1159 children_count: item.children.len() as u32,
1160 parent_index,
1161 item_array_index: self.item_array.len() as u32,
1162 });
1163 self.item_array.push(unsafe { vtable::VOffset::from_raw(rt.vtable, offset) });
1164 self.original_elements.push(rc_item.clone());
1165 debug_assert_eq!(self.original_elements.len(), self.tree_array.len());
1166 self.items_types.insert(
1167 item.id.clone(),
1168 ItemWithinItemTree { offset, rtti: rt, elem: rc_item.clone() },
1169 );
1170 for (prop, expr) in &item.change_callbacks {
1171 self.change_callbacks.push((
1172 NamedReference::new(rc_item, prop.clone()),
1173 Expression::CodeBlock(expr.borrow().clone()),
1174 ));
1175 }
1176 }
1177
1178 fn enter_component(
1179 &mut self,
1180 _item: &ElementRc,
1181 _sub_component: &Rc<object_tree::Component>,
1182 _children_offset: u32,
1183 _component_state: &Self::SubComponentState,
1184 ) -> Self::SubComponentState {
1185 }
1187
1188 fn enter_component_children(
1189 &mut self,
1190 _item: &ElementRc,
1191 _repeater_count: u32,
1192 _component_state: &Self::SubComponentState,
1193 _sub_component_state: &Self::SubComponentState,
1194 ) {
1195 todo!()
1196 }
1197 }
1198
1199 let mut builder = TreeBuilder {
1200 tree_array: Vec::new(),
1201 item_array: Vec::new(),
1202 original_elements: Vec::new(),
1203 items_types: HashMap::new(),
1204 type_builder: dynamic_type::TypeBuilder::new(guard),
1205 repeater: Vec::new(),
1206 repeater_names: HashMap::new(),
1207 change_callbacks: Vec::new(),
1208 popup_menu_description,
1209 };
1210
1211 if !component.is_global() {
1212 generator::build_item_tree(component, &(), &mut builder);
1213 } else {
1214 for (prop, expr) in component.root_element.borrow().change_callbacks.iter() {
1215 builder.change_callbacks.push((
1216 NamedReference::new(&component.root_element, prop.clone()),
1217 Expression::CodeBlock(expr.borrow().clone()),
1218 ));
1219 }
1220 }
1221
1222 let mut custom_properties = HashMap::new();
1223 let mut custom_callbacks = HashMap::new();
1224 fn property_info<T>() -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1225 where
1226 T: PartialEq + Clone + Default + std::convert::TryInto<Value> + 'static,
1227 Value: std::convert::TryInto<T>,
1228 {
1229 (
1231 Box::new(unsafe {
1232 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0)
1233 }),
1234 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1235 )
1236 }
1237 fn animated_property_info<T>()
1238 -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1239 where
1240 T: Clone + Default + InterpolatedPropertyValue + std::convert::TryInto<Value> + 'static,
1241 Value: std::convert::TryInto<T>,
1242 {
1243 (
1245 Box::new(unsafe {
1246 rtti::MaybeAnimatedPropertyInfoWrapper(
1247 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0),
1248 )
1249 }),
1250 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1251 )
1252 }
1253
1254 fn property_info_for_type(
1255 ty: &Type,
1256 name: &str,
1257 ) -> Option<(Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)> {
1258 Some(match ty {
1259 Type::Float32 => animated_property_info::<f32>(),
1260 Type::Int32 => animated_property_info::<i32>(),
1261 Type::String => property_info::<SharedString>(),
1262 Type::Color => animated_property_info::<Color>(),
1263 Type::Brush => animated_property_info::<Brush>(),
1264 Type::Duration => animated_property_info::<i64>(),
1265 Type::Angle => animated_property_info::<f32>(),
1266 Type::PhysicalLength => animated_property_info::<f32>(),
1267 Type::LogicalLength => animated_property_info::<f32>(),
1268 Type::Rem => animated_property_info::<f32>(),
1269 Type::Image => property_info::<i_slint_core::graphics::Image>(),
1270 Type::Bool => property_info::<bool>(),
1271 Type::ComponentFactory => property_info::<ComponentFactory>(),
1272 Type::Struct(s)
1273 if matches!(
1274 s.name,
1275 StructName::BuiltinPrivate(BuiltinPrivateStruct::StateInfo)
1276 ) =>
1277 {
1278 property_info::<i_slint_core::properties::StateInfo>()
1279 }
1280 Type::Struct(_) => property_info::<Value>(),
1281 Type::Array(_) => property_info::<Value>(),
1282 Type::Easing => property_info::<i_slint_core::animations::EasingCurve>(),
1283 Type::Percent => animated_property_info::<f32>(),
1284 Type::Enumeration(e) => {
1285 macro_rules! match_enum_type {
1286 ($( $(#[$enum_doc:meta])* enum $Name:ident { $($body:tt)* })*) => {
1287 match e.name.as_str() {
1288 $(
1289 stringify!($Name) => property_info::<i_slint_core::items::$Name>(),
1290 )*
1291 x => unreachable!("Unknown non-builtin enum {x}"),
1292 }
1293 }
1294 }
1295 if e.node.is_some() {
1296 property_info::<Value>()
1297 } else {
1298 i_slint_common::for_each_enums!(match_enum_type)
1299 }
1300 }
1301 Type::LayoutCache => property_info::<SharedVector<f32>>(),
1302 Type::ArrayOfU16 => property_info::<SharedVector<u16>>(),
1303 Type::Function { .. } | Type::Callback { .. } => return None,
1304 Type::StyledText => property_info::<StyledText>(),
1305 Type::Invalid
1307 | Type::Void
1308 | Type::InferredProperty
1309 | Type::InferredCallback
1310 | Type::Model
1311 | Type::PathData
1312 | Type::UnitProduct(_)
1313 | Type::ElementReference => panic!("bad type {ty:?} for property {name}"),
1314 })
1315 }
1316
1317 for (name, decl) in &component.root_element.borrow().property_declarations {
1318 if decl.is_alias.is_some() {
1319 continue;
1320 }
1321 if matches!(&decl.property_type, Type::Callback { .. }) {
1322 custom_callbacks
1323 .insert(name.clone(), builder.type_builder.add_field_type::<Callback>());
1324 continue;
1325 }
1326 let Some((prop, type_info)) = property_info_for_type(&decl.property_type, name) else {
1327 continue;
1328 };
1329 custom_properties.insert(
1330 name.clone(),
1331 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1332 );
1333 }
1334 if let Some(parent_element) = component.parent_element.upgrade()
1335 && let Some(r) = &parent_element.borrow().repeated
1336 && !r.is_conditional_element
1337 {
1338 let (prop, type_info) = property_info::<u32>();
1339 custom_properties.insert(
1340 SPECIAL_PROPERTY_INDEX.into(),
1341 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1342 );
1343
1344 let model_ty =
1345 Expression::RepeaterModelReference { element: component.parent_element.clone() }.ty();
1346 let (prop, type_info) =
1347 property_info_for_type(&model_ty, SPECIAL_PROPERTY_MODEL_DATA).unwrap();
1348 custom_properties.insert(
1349 SPECIAL_PROPERTY_MODEL_DATA.into(),
1350 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1351 );
1352 }
1353
1354 let parent_item_tree_offset =
1355 if component.parent_element.upgrade().is_some() || is_popup_menu_impl {
1356 Some(builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>())
1357 } else {
1358 None
1359 };
1360
1361 let root_offset = builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>();
1362 let extra_data_offset = builder.type_builder.add_field_type::<ComponentExtraData>();
1363
1364 let change_trackers = (!builder.change_callbacks.is_empty()).then(|| {
1365 (
1366 builder.type_builder.add_field_type::<OnceCell<Vec<ChangeTracker>>>(),
1367 builder.change_callbacks,
1368 )
1369 });
1370 let timers = component
1371 .timers
1372 .borrow()
1373 .iter()
1374 .map(|_| builder.type_builder.add_field_type::<Timer>())
1375 .collect();
1376
1377 let public_properties = if component.parent_element.upgrade().is_none() {
1379 component.root_element.borrow().property_declarations.clone()
1380 } else {
1381 Default::default()
1382 };
1383
1384 let t = ItemTreeVTable {
1385 visit_children_item,
1386 layout_info,
1387 get_item_ref,
1388 get_item_tree,
1389 get_subtree_range,
1390 get_subtree,
1391 parent_node,
1392 embed_component,
1393 subtree_index,
1394 item_geometry,
1395 accessible_role,
1396 accessible_string_property,
1397 accessibility_action,
1398 supported_accessibility_actions,
1399 item_element_infos,
1400 window_adapter,
1401 drop_in_place,
1402 dealloc,
1403 };
1404 let t = ItemTreeDescription {
1405 ct: t,
1406 dynamic_type: builder.type_builder.build(),
1407 item_tree: builder.tree_array,
1408 item_array: builder.item_array,
1409 items: builder.items_types,
1410 custom_properties,
1411 custom_callbacks,
1412 original: component.clone(),
1413 original_elements: builder.original_elements,
1414 repeater: builder.repeater,
1415 repeater_names: builder.repeater_names,
1416 parent_item_tree_offset,
1417 root_offset,
1418 extra_data_offset,
1419 public_properties,
1420 compiled_globals,
1421 change_trackers,
1422 timers,
1423 popup_ids: std::cell::RefCell::new(HashMap::new()),
1424 popup_menu_description: builder.popup_menu_description,
1425 #[cfg(feature = "internal-highlight")]
1426 type_loader: std::cell::OnceCell::new(),
1427 #[cfg(feature = "internal-highlight")]
1428 raw_type_loader: std::cell::OnceCell::new(),
1429 debug_handler: std::cell::RefCell::new(Rc::new(|_, text| {
1430 i_slint_core::debug_log!("{text}")
1431 })),
1432 };
1433
1434 Rc::new(t)
1435}
1436
1437pub fn animation_for_property(
1438 component: InstanceRef,
1439 animation: &Option<i_slint_compiler::object_tree::PropertyAnimation>,
1440) -> AnimatedBindingKind {
1441 match animation {
1442 Some(i_slint_compiler::object_tree::PropertyAnimation::Static(anim_elem)) => {
1443 AnimatedBindingKind::Animation(Box::new({
1444 let component_ptr = component.as_ptr();
1445 let vtable = NonNull::from(&component.description.ct).cast();
1446 let anim_elem = Rc::clone(&anim_elem);
1447 move || -> PropertyAnimation {
1448 generativity::make_guard!(guard);
1449 let component = unsafe {
1450 InstanceRef::from_pin_ref(
1451 Pin::new_unchecked(vtable::VRef::from_raw(
1452 vtable,
1453 NonNull::new_unchecked(component_ptr as *mut u8),
1454 )),
1455 guard,
1456 )
1457 };
1458
1459 eval::new_struct_with_bindings(
1460 &anim_elem.borrow().bindings,
1461 &mut eval::EvalLocalContext::from_component_instance(component),
1462 )
1463 }
1464 }))
1465 }
1466 Some(i_slint_compiler::object_tree::PropertyAnimation::Transition {
1467 animations,
1468 state_ref,
1469 }) => {
1470 let component_ptr = component.as_ptr();
1471 let vtable = NonNull::from(&component.description.ct).cast();
1472 let animations = animations.clone();
1473 let state_ref = state_ref.clone();
1474 AnimatedBindingKind::Transition(Box::new(
1475 move || -> (PropertyAnimation, i_slint_core::animations::Instant) {
1476 generativity::make_guard!(guard);
1477 let component = unsafe {
1478 InstanceRef::from_pin_ref(
1479 Pin::new_unchecked(vtable::VRef::from_raw(
1480 vtable,
1481 NonNull::new_unchecked(component_ptr as *mut u8),
1482 )),
1483 guard,
1484 )
1485 };
1486
1487 let mut context = eval::EvalLocalContext::from_component_instance(component);
1488 let state = eval::eval_expression(&state_ref, &mut context);
1489 let state_info: i_slint_core::properties::StateInfo = state.try_into().unwrap();
1490 for a in &animations {
1491 let is_previous_state = a.state_id == state_info.previous_state;
1492 let is_current_state = a.state_id == state_info.current_state;
1493 match (a.direction, is_previous_state, is_current_state) {
1494 (TransitionDirection::In, false, true)
1495 | (TransitionDirection::Out, true, false)
1496 | (TransitionDirection::InOut, false, true)
1497 | (TransitionDirection::InOut, true, false) => {
1498 return (
1499 eval::new_struct_with_bindings(
1500 &a.animation.borrow().bindings,
1501 &mut context,
1502 ),
1503 state_info.change_time,
1504 );
1505 }
1506 _ => {}
1507 }
1508 }
1509 Default::default()
1510 },
1511 ))
1512 }
1513 None => AnimatedBindingKind::NotAnimated,
1514 }
1515}
1516
1517fn make_callback_eval_closure(
1518 expr: Expression,
1519 self_weak: ErasedItemTreeBoxWeak,
1520) -> impl Fn(&[Value]) -> Value {
1521 move |args| {
1522 let self_rc = self_weak.upgrade().unwrap();
1523 generativity::make_guard!(guard);
1524 let self_ = self_rc.unerase(guard);
1525 let instance_ref = self_.borrow_instance();
1526 let mut local_context =
1527 eval::EvalLocalContext::from_function_arguments(instance_ref, args.to_vec());
1528 eval::eval_expression(&expr, &mut local_context)
1529 }
1530}
1531
1532fn make_binding_eval_closure(
1533 expr: Expression,
1534 self_weak: ErasedItemTreeBoxWeak,
1535) -> impl Fn() -> Value {
1536 move || {
1537 let self_rc = self_weak.upgrade().unwrap();
1538 generativity::make_guard!(guard);
1539 let self_ = self_rc.unerase(guard);
1540 let instance_ref = self_.borrow_instance();
1541 eval::eval_expression(
1542 &expr,
1543 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1544 )
1545 }
1546}
1547
1548pub fn instantiate(
1549 description: Rc<ItemTreeDescription>,
1550 parent_ctx: Option<ErasedItemTreeBoxWeak>,
1551 root: Option<ErasedItemTreeBoxWeak>,
1552 window_options: Option<&WindowOptions>,
1553 globals: crate::global_component::GlobalStorage,
1554) -> DynamicComponentVRc {
1555 let instance = description.dynamic_type.clone().create_instance();
1556
1557 let component_box = ItemTreeBox { instance, description: description.clone() };
1558
1559 let self_rc = vtable::VRc::new(ErasedItemTreeBox::from(component_box));
1560 let self_weak = vtable::VRc::downgrade(&self_rc);
1561
1562 generativity::make_guard!(guard);
1563 let comp = self_rc.unerase(guard);
1564 let instance_ref = comp.borrow_instance();
1565 instance_ref.self_weak().set(self_weak.clone()).ok();
1566 let description = comp.description();
1567
1568 if let Some(WindowOptions::UseExistingWindow(existing_adapter)) = &window_options {
1569 if let Err((a, b)) = globals.window_adapter().unwrap().try_insert(existing_adapter.clone())
1570 {
1571 assert!(Rc::ptr_eq(a, &b), "window not the same as parent window");
1572 }
1573 }
1574
1575 if let Some(parent) = parent_ctx {
1576 description
1577 .parent_item_tree_offset
1578 .unwrap()
1579 .apply(instance_ref.as_ref())
1580 .set(parent)
1581 .ok()
1582 .unwrap();
1583 } else {
1584 if let Some(g) = description.compiled_globals.as_ref() {
1585 for g in g.compiled_globals.iter() {
1586 crate::global_component::instantiate(g, &globals, self_weak.clone());
1587 }
1588 }
1589 }
1590 let extra_data = description.extra_data_offset.apply(instance_ref.as_ref());
1591 extra_data.globals.set(globals).ok().unwrap();
1592 if let Some(WindowOptions::Embed { parent_item_tree, parent_item_tree_index }) = window_options
1593 {
1594 vtable::VRc::borrow_pin(&self_rc)
1595 .as_ref()
1596 .embed_component(parent_item_tree, *parent_item_tree_index);
1597 description.root_offset.apply(instance_ref.as_ref()).set(self_weak.clone()).ok().unwrap();
1598 } else {
1599 generativity::make_guard!(guard);
1600 let root = root
1601 .or_else(|| {
1602 instance_ref.parent_instance(guard).map(|parent| parent.root_weak().clone())
1603 })
1604 .unwrap_or_else(|| self_weak.clone());
1605 description.root_offset.apply(instance_ref.as_ref()).set(root).ok().unwrap();
1606 }
1607
1608 if !description.original.is_global() {
1609 let maybe_window_adapter =
1610 if let Some(WindowOptions::UseExistingWindow(adapter)) = window_options.as_ref() {
1611 Some(adapter.clone())
1612 } else {
1613 instance_ref.maybe_window_adapter()
1614 };
1615
1616 let component_rc = vtable::VRc::into_dyn(self_rc.clone());
1617 i_slint_core::item_tree::register_item_tree(&component_rc, maybe_window_adapter);
1618 }
1619
1620 for (prop_name, decl) in &description.original.root_element.borrow().property_declarations {
1622 if !matches!(
1623 decl.property_type,
1624 Type::Struct { .. } | Type::Array(_) | Type::Enumeration(_)
1625 ) || decl.is_alias.is_some()
1626 {
1627 continue;
1628 }
1629 if let Some(b) = description.original.root_element.borrow().bindings.get(prop_name)
1630 && b.borrow().two_way_bindings.is_empty()
1631 {
1632 continue;
1633 }
1634 let p = description.custom_properties.get(prop_name).unwrap();
1635 unsafe {
1636 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(p.offset));
1637 p.prop.set(item, eval::default_value_for_type(&decl.property_type), None).unwrap();
1638 }
1639 }
1640
1641 generator::handle_property_bindings_init(
1642 &description.original,
1643 |elem, prop_name, binding| unsafe {
1644 let is_root = Rc::ptr_eq(
1645 elem,
1646 &elem.borrow().enclosing_component.upgrade().unwrap().root_element,
1647 );
1648 let elem = elem.borrow();
1649 let is_const = binding.analysis.as_ref().is_some_and(|a| a.is_const);
1650
1651 let property_type = elem.lookup_property(prop_name).property_type;
1652 if let Type::Function { .. } = property_type {
1653 } else if let Type::Callback { .. } = property_type {
1655 if !matches!(binding.expression, Expression::Invalid) {
1656 let expr = binding.expression.clone();
1657 let description = description.clone();
1658 if let Some(callback_offset) =
1659 description.custom_callbacks.get(prop_name).filter(|_| is_root)
1660 {
1661 let callback = callback_offset.apply(instance_ref.as_ref());
1662 callback.set_handler(make_callback_eval_closure(expr, self_weak.clone()));
1663 } else {
1664 let item_within_component = &description.items[&elem.id];
1665 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1666 if let Some(callback) =
1667 item_within_component.rtti.callbacks.get(prop_name.as_str())
1668 {
1669 callback.set_handler(
1670 item,
1671 Box::new(make_callback_eval_closure(expr, self_weak.clone())),
1672 );
1673 } else {
1674 panic!("unknown callback {prop_name}")
1675 }
1676 }
1677 }
1678 } else if let Some(PropertiesWithinComponent { offset, prop: prop_info, .. }) =
1679 description.custom_properties.get(prop_name).filter(|_| is_root)
1680 {
1681 let is_state_info = matches!(&property_type, Type::Struct (s) if matches!(s.name, StructName::BuiltinPrivate(BuiltinPrivateStruct::StateInfo)));
1682 if is_state_info {
1683 let prop = Pin::new_unchecked(
1684 &*(instance_ref.as_ptr().add(*offset)
1685 as *const Property<i_slint_core::properties::StateInfo>),
1686 );
1687 let e = binding.expression.clone();
1688 let state_binding = make_binding_eval_closure(e, self_weak.clone());
1689 i_slint_core::properties::set_state_binding(prop, move || {
1690 state_binding().try_into().unwrap()
1691 });
1692 return;
1693 }
1694
1695 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1696 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(*offset));
1697
1698 if !matches!(binding.expression, Expression::Invalid) {
1699 if is_const {
1700 let v = eval::eval_expression(
1701 &binding.expression,
1702 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1703 );
1704 prop_info.set(item, v, None).unwrap();
1705 } else {
1706 let e = binding.expression.clone();
1707 prop_info
1708 .set_binding(
1709 item,
1710 Box::new(make_binding_eval_closure(e, self_weak.clone())),
1711 maybe_animation,
1712 )
1713 .unwrap();
1714 }
1715 }
1716 for twb in &binding.two_way_bindings {
1717 if twb.field_access.is_empty()
1718 && !matches!(&property_type, Type::Struct(..) | Type::Array(..))
1719 {
1720 prop_info
1723 .link_two_ways(item, get_property_ptr(&twb.property, instance_ref));
1724 } else {
1725 let (common, map) = prepare_for_two_way_binding(instance_ref, twb);
1726 prop_info.link_two_way_with_map(item, common, map);
1727 }
1728 }
1729 } else {
1730 let item_within_component = &description.items[&elem.id];
1731 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1732 if let Some(prop_rtti) =
1733 item_within_component.rtti.properties.get(prop_name.as_str())
1734 {
1735 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1736
1737 for twb in &binding.two_way_bindings {
1738 if twb.field_access.is_empty()
1739 && !matches!(&property_type, Type::Struct(..) | Type::Array(..))
1740 {
1741 prop_rtti
1743 .link_two_ways(item, get_property_ptr(&twb.property, instance_ref));
1744 } else {
1745 let (common, map) = prepare_for_two_way_binding(instance_ref, twb);
1746 prop_rtti.link_two_way_with_map(item, common, map);
1747 }
1748 }
1749 if !matches!(binding.expression, Expression::Invalid) {
1750 if is_const {
1751 prop_rtti
1752 .set(
1753 item,
1754 eval::eval_expression(
1755 &binding.expression,
1756 &mut eval::EvalLocalContext::from_component_instance(
1757 instance_ref,
1758 ),
1759 ),
1760 maybe_animation.as_animation(),
1761 )
1762 .unwrap();
1763 } else {
1764 let e = binding.expression.clone();
1765 prop_rtti.set_binding(
1766 item,
1767 Box::new(make_binding_eval_closure(e, self_weak.clone())),
1768 maybe_animation,
1769 );
1770 }
1771 }
1772 } else {
1773 panic!("unknown property {} in {}", prop_name, elem.id);
1774 }
1775 }
1776 },
1777 );
1778
1779 for rep_in_comp in &description.repeater {
1780 generativity::make_guard!(guard);
1781 let rep_in_comp = rep_in_comp.unerase(guard);
1782
1783 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
1784 let expr = rep_in_comp.model.clone();
1785 let model_binding_closure = make_binding_eval_closure(expr, self_weak.clone());
1786 if rep_in_comp.is_conditional {
1787 let bool_model = Rc::new(crate::value_model::BoolModel::default());
1788 repeater.set_model_binding(move || {
1789 let v = model_binding_closure();
1790 bool_model.set_value(v.try_into().expect("condition model is bool"));
1791 ModelRc::from(bool_model.clone())
1792 });
1793 } else {
1794 repeater.set_model_binding(move || {
1795 let m = model_binding_closure();
1796 if let Value::Model(m) = m {
1797 m
1798 } else {
1799 ModelRc::new(crate::value_model::ValueModel::new(m))
1800 }
1801 });
1802 }
1803 }
1804 self_rc
1805}
1806
1807fn prepare_for_two_way_binding(
1808 instance_ref: InstanceRef,
1809 twb: &i_slint_compiler::expression_tree::TwoWayBinding,
1810) -> (Pin<Rc<Property<Value>>>, Option<Rc<dyn rtti::TwoWayBindingMapping<Value>>>) {
1811 let element = twb.property.element();
1812 let name = twb.property.name();
1813 generativity::make_guard!(guard);
1814 let enclosing_component = eval::enclosing_component_instance_for_element(
1815 &element,
1816 &eval::ComponentInstance::InstanceRef(instance_ref),
1817 guard,
1818 );
1819 let map: Option<Rc<dyn rtti::TwoWayBindingMapping<Value>>> = if twb.field_access.is_empty() {
1820 None
1821 } else {
1822 struct FieldAccess(Vec<SmolStr>);
1823 impl rtti::TwoWayBindingMapping<Value> for FieldAccess {
1824 fn map_to(&self, value: &Value) -> Value {
1825 let mut value = value.clone();
1826 for f in &self.0 {
1827 match value {
1828 Value::Struct(o) => value = o.get_field(f).cloned().unwrap_or_default(),
1829 Value::Void => return Value::Void,
1830 _ => panic!("Cannot map to a field of a non-struct {value:?} - {f}"),
1831 }
1832 }
1833 value
1834 }
1835 fn map_from(&self, mut value: &mut Value, from: &Value) {
1836 for f in &self.0 {
1837 match value {
1838 Value::Struct(o) => {
1839 value = o.0.get_mut(f).expect("field not found while mapping")
1840 }
1841 _ => panic!("Cannot map to a field of a non-struct {value:?}"),
1842 }
1843 }
1844 *value = from.clone();
1845 }
1846 }
1847 Some(Rc::new(FieldAccess(twb.field_access.clone())))
1848 };
1849 let common = match enclosing_component {
1850 eval::ComponentInstance::InstanceRef(enclosing_component) => {
1851 let element = element.borrow();
1852 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1853 && let Some(x) = enclosing_component.description.custom_properties.get(name)
1854 {
1855 let item =
1856 unsafe { Pin::new_unchecked(&*enclosing_component.as_ptr().add(x.offset)) };
1857 let common = x.prop.prepare_for_two_way_binding(item);
1858 return (common, map);
1859 }
1860 let item_info = enclosing_component
1861 .description
1862 .items
1863 .get(element.id.as_str())
1864 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, name));
1865 let prop_info = item_info
1866 .rtti
1867 .properties
1868 .get(name.as_str())
1869 .unwrap_or_else(|| panic!("Property {} not in {}", name, element.id));
1870 core::mem::drop(element);
1871 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1872 prop_info.prepare_for_two_way_binding(item)
1873 }
1874 eval::ComponentInstance::GlobalComponent(glob) => {
1875 glob.as_ref().prepare_for_two_way_binding(name).unwrap()
1876 }
1877 };
1878 (common, map)
1879}
1880
1881pub(crate) fn get_property_ptr(nr: &NamedReference, instance: InstanceRef) -> *const () {
1882 let element = nr.element();
1883 generativity::make_guard!(guard);
1884 let enclosing_component = eval::enclosing_component_instance_for_element(
1885 &element,
1886 &eval::ComponentInstance::InstanceRef(instance),
1887 guard,
1888 );
1889 match enclosing_component {
1890 eval::ComponentInstance::InstanceRef(enclosing_component) => {
1891 let element = element.borrow();
1892 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1893 && let Some(x) = enclosing_component.description.custom_properties.get(nr.name())
1894 {
1895 return unsafe { enclosing_component.as_ptr().add(x.offset).cast() };
1896 };
1897 let item_info = enclosing_component
1898 .description
1899 .items
1900 .get(element.id.as_str())
1901 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, nr.name()));
1902 let prop_info = item_info
1903 .rtti
1904 .properties
1905 .get(nr.name().as_str())
1906 .unwrap_or_else(|| panic!("Property {} not in {}", nr.name(), element.id));
1907 core::mem::drop(element);
1908 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1909 unsafe { item.as_ptr().add(prop_info.offset()).cast() }
1910 }
1911 eval::ComponentInstance::GlobalComponent(glob) => glob.as_ref().get_property_ptr(nr.name()),
1912 }
1913}
1914
1915pub struct ErasedItemTreeBox(ItemTreeBox<'static>);
1916impl ErasedItemTreeBox {
1917 pub fn unerase<'a, 'id>(
1918 &'a self,
1919 _guard: generativity::Guard<'id>,
1920 ) -> Pin<&'a ItemTreeBox<'id>> {
1921 Pin::new(
1922 unsafe { core::mem::transmute::<&ItemTreeBox<'static>, &ItemTreeBox<'id>>(&self.0) },
1924 )
1925 }
1926
1927 pub fn borrow(&self) -> ItemTreeRefPin<'_> {
1928 self.0.borrow()
1930 }
1931
1932 pub fn window_adapter_ref(&self) -> Result<&WindowAdapterRc, PlatformError> {
1933 self.0.window_adapter_ref()
1934 }
1935
1936 pub fn run_setup_code(&self) {
1937 generativity::make_guard!(guard);
1938 let compo_box = self.unerase(guard);
1939 let instance_ref = compo_box.borrow_instance();
1940 for extra_init_code in self.0.description.original.init_code.borrow().iter() {
1941 eval::eval_expression(
1942 extra_init_code,
1943 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1944 );
1945 }
1946 if let Some(cts) = instance_ref.description.change_trackers.as_ref() {
1947 let self_weak = instance_ref.self_weak().get().unwrap();
1948 let v = cts
1949 .1
1950 .iter()
1951 .enumerate()
1952 .map(|(idx, _)| {
1953 let ct = ChangeTracker::default();
1954 ct.init(
1955 self_weak.clone(),
1956 move |self_weak| {
1957 let s = self_weak.upgrade().unwrap();
1958 generativity::make_guard!(guard);
1959 let compo_box = s.unerase(guard);
1960 let instance_ref = compo_box.borrow_instance();
1961 let nr = &s.0.description.change_trackers.as_ref().unwrap().1[idx].0;
1962 eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap()
1963 },
1964 move |self_weak, _| {
1965 let s = self_weak.upgrade().unwrap();
1966 generativity::make_guard!(guard);
1967 let compo_box = s.unerase(guard);
1968 let instance_ref = compo_box.borrow_instance();
1969 let e = &s.0.description.change_trackers.as_ref().unwrap().1[idx].1;
1970 eval::eval_expression(
1971 e,
1972 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1973 );
1974 },
1975 );
1976 ct
1977 })
1978 .collect::<Vec<_>>();
1979 cts.0
1980 .apply_pin(instance_ref.instance)
1981 .set(v)
1982 .unwrap_or_else(|_| panic!("run_setup_code called twice?"));
1983 }
1984 update_timers(instance_ref);
1985 }
1986}
1987impl<'id> From<ItemTreeBox<'id>> for ErasedItemTreeBox {
1988 fn from(inner: ItemTreeBox<'id>) -> Self {
1989 unsafe {
1992 ErasedItemTreeBox(core::mem::transmute::<ItemTreeBox<'id>, ItemTreeBox<'static>>(inner))
1993 }
1994 }
1995}
1996
1997pub fn get_repeater_by_name<'a, 'id>(
1998 instance_ref: InstanceRef<'a, '_>,
1999 name: &str,
2000 guard: generativity::Guard<'id>,
2001) -> (std::pin::Pin<&'a Repeater<ErasedItemTreeBox>>, Rc<ItemTreeDescription<'id>>) {
2002 let rep_index = instance_ref.description.repeater_names[name];
2003 let rep_in_comp = instance_ref.description.repeater[rep_index].unerase(guard);
2004 (rep_in_comp.offset.apply_pin(instance_ref.instance), rep_in_comp.item_tree_to_repeat.clone())
2005}
2006
2007#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2008extern "C" fn layout_info(component: ItemTreeRefPin, orientation: Orientation) -> LayoutInfo {
2009 generativity::make_guard!(guard);
2010 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2012 let orientation = crate::eval_layout::from_runtime(orientation);
2013
2014 let mut result = crate::eval_layout::get_layout_info(
2015 &instance_ref.description.original.root_element,
2016 instance_ref,
2017 &instance_ref.window_adapter(),
2018 orientation,
2019 );
2020
2021 let constraints = instance_ref.description.original.root_constraints.borrow();
2022 if constraints.has_explicit_restrictions(orientation) {
2023 crate::eval_layout::fill_layout_info_constraints(
2024 &mut result,
2025 &constraints,
2026 orientation,
2027 &|nr: &NamedReference| {
2028 eval::load_property(instance_ref, &nr.element(), nr.name())
2029 .unwrap()
2030 .try_into()
2031 .unwrap()
2032 },
2033 );
2034 }
2035 result
2036}
2037
2038#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2039unsafe extern "C" fn get_item_ref(component: ItemTreeRefPin, index: u32) -> Pin<ItemRef> {
2040 let tree = get_item_tree(component);
2041 match &tree[index as usize] {
2042 ItemTreeNode::Item { item_array_index, .. } => unsafe {
2043 generativity::make_guard!(guard);
2044 let instance_ref = InstanceRef::from_pin_ref(component, guard);
2045 core::mem::transmute::<Pin<ItemRef>, Pin<ItemRef>>(
2046 instance_ref.description.item_array[*item_array_index as usize]
2047 .apply_pin(instance_ref.instance),
2048 )
2049 },
2050 ItemTreeNode::DynamicTree { .. } => panic!("get_item_ref called on dynamic tree"),
2051 }
2052}
2053
2054#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2055extern "C" fn get_subtree_range(component: ItemTreeRefPin, index: u32) -> IndexRange {
2056 generativity::make_guard!(guard);
2057 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2058 if index as usize >= instance_ref.description.repeater.len() {
2059 let container_index = {
2060 let tree_node = &component.as_ref().get_item_tree()[index as usize];
2061 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
2062 *parent_index
2063 } else {
2064 u32::MAX
2065 }
2066 };
2067 let container = component.as_ref().get_item_ref(container_index);
2068 let container = i_slint_core::items::ItemRef::downcast_pin::<
2069 i_slint_core::items::ComponentContainer,
2070 >(container)
2071 .unwrap();
2072 container.ensure_updated();
2073 container.subtree_range()
2074 } else {
2075 let rep_in_comp =
2076 unsafe { instance_ref.description.repeater[index as usize].get_untagged() };
2077 ensure_repeater_updated(instance_ref, rep_in_comp);
2078
2079 let repeater = rep_in_comp.offset.apply(&instance_ref.instance);
2080 repeater.range().into()
2081 }
2082}
2083
2084#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2085extern "C" fn get_subtree(
2086 component: ItemTreeRefPin,
2087 index: u32,
2088 subtree_index: usize,
2089 result: &mut ItemTreeWeak,
2090) {
2091 generativity::make_guard!(guard);
2092 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2093 if index as usize >= instance_ref.description.repeater.len() {
2094 let container_index = {
2095 let tree_node = &component.as_ref().get_item_tree()[index as usize];
2096 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
2097 *parent_index
2098 } else {
2099 u32::MAX
2100 }
2101 };
2102 let container = component.as_ref().get_item_ref(container_index);
2103 let container = i_slint_core::items::ItemRef::downcast_pin::<
2104 i_slint_core::items::ComponentContainer,
2105 >(container)
2106 .unwrap();
2107 container.ensure_updated();
2108 if subtree_index == 0 {
2109 *result = container.subtree_component();
2110 }
2111 } else {
2112 let rep_in_comp =
2113 unsafe { instance_ref.description.repeater[index as usize].get_untagged() };
2114 ensure_repeater_updated(instance_ref, rep_in_comp);
2115
2116 let repeater = rep_in_comp.offset.apply(&instance_ref.instance);
2117 if let Some(instance_at) = repeater.instance_at(subtree_index) {
2118 *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(instance_at))
2119 }
2120 }
2121}
2122
2123#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2124extern "C" fn get_item_tree(component: ItemTreeRefPin) -> Slice<ItemTreeNode> {
2125 generativity::make_guard!(guard);
2126 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2127 let tree = instance_ref.description.item_tree.as_slice();
2128 unsafe { core::mem::transmute::<&[ItemTreeNode], &[ItemTreeNode]>(tree) }.into()
2129}
2130
2131#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2132extern "C" fn subtree_index(component: ItemTreeRefPin) -> usize {
2133 generativity::make_guard!(guard);
2134 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2135 if let Ok(value) = instance_ref.description.get_property(component, SPECIAL_PROPERTY_INDEX) {
2136 value.try_into().unwrap()
2137 } else {
2138 usize::MAX
2139 }
2140}
2141
2142#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2143unsafe extern "C" fn parent_node(component: ItemTreeRefPin, result: &mut ItemWeak) {
2144 generativity::make_guard!(guard);
2145 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2146
2147 let component_and_index = {
2148 if let Some(parent_offset) = instance_ref.description.parent_item_tree_offset {
2150 let parent_item_index = instance_ref
2151 .description
2152 .original
2153 .parent_element
2154 .upgrade()
2155 .and_then(|e| e.borrow().item_index.get().cloned())
2156 .unwrap_or(u32::MAX);
2157 let parent_component = parent_offset
2158 .apply(instance_ref.as_ref())
2159 .get()
2160 .and_then(|p| p.upgrade())
2161 .map(vtable::VRc::into_dyn);
2162
2163 (parent_component, parent_item_index)
2164 } else if let Some((parent_component, parent_index)) = instance_ref
2165 .description
2166 .extra_data_offset
2167 .apply(instance_ref.as_ref())
2168 .embedding_position
2169 .get()
2170 {
2171 (parent_component.upgrade(), *parent_index)
2172 } else {
2173 (None, u32::MAX)
2174 }
2175 };
2176
2177 if let (Some(component), index) = component_and_index {
2178 *result = ItemRc::new(component, index).downgrade();
2179 }
2180}
2181
2182#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2183unsafe extern "C" fn embed_component(
2184 component: ItemTreeRefPin,
2185 parent_component: &ItemTreeWeak,
2186 parent_item_tree_index: u32,
2187) -> bool {
2188 generativity::make_guard!(guard);
2189 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2190
2191 if instance_ref.description.parent_item_tree_offset.is_some() {
2192 return false;
2194 }
2195
2196 {
2197 let prc = parent_component.upgrade().unwrap();
2199 let pref = vtable::VRc::borrow_pin(&prc);
2200 let it = pref.as_ref().get_item_tree();
2201 if !matches!(
2202 it.get(parent_item_tree_index as usize),
2203 Some(ItemTreeNode::DynamicTree { .. })
2204 ) {
2205 panic!("Trying to embed into a non-dynamic index in the parents item tree")
2206 }
2207 }
2208
2209 let extra_data = instance_ref.description.extra_data_offset.apply(instance_ref.as_ref());
2210 extra_data.embedding_position.set((parent_component.clone(), parent_item_tree_index)).is_ok()
2211}
2212
2213#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2214extern "C" fn item_geometry(component: ItemTreeRefPin, item_index: u32) -> LogicalRect {
2215 generativity::make_guard!(guard);
2216 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2217
2218 let e = instance_ref.description.original_elements[item_index as usize].borrow();
2219 let g = e.geometry_props.as_ref().unwrap();
2220
2221 let load_f32 = |nr: &NamedReference| -> f32 {
2222 crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2223 .unwrap()
2224 .try_into()
2225 .unwrap()
2226 };
2227
2228 LogicalRect {
2229 origin: (load_f32(&g.x), load_f32(&g.y)).into(),
2230 size: (load_f32(&g.width), load_f32(&g.height)).into(),
2231 }
2232}
2233
2234#[allow(improper_ctypes_definitions)]
2236#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2237extern "C" fn accessible_role(component: ItemTreeRefPin, item_index: u32) -> AccessibleRole {
2238 generativity::make_guard!(guard);
2239 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2240 let nr = instance_ref.description.original_elements[item_index as usize]
2241 .borrow()
2242 .accessibility_props
2243 .0
2244 .get("accessible-role")
2245 .cloned();
2246 match nr {
2247 Some(nr) => crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2248 .unwrap()
2249 .try_into()
2250 .unwrap(),
2251 None => AccessibleRole::default(),
2252 }
2253}
2254
2255#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2256extern "C" fn accessible_string_property(
2257 component: ItemTreeRefPin,
2258 item_index: u32,
2259 what: AccessibleStringProperty,
2260 result: &mut SharedString,
2261) -> bool {
2262 generativity::make_guard!(guard);
2263 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2264 let prop_name = format!("accessible-{what}");
2265 let nr = instance_ref.description.original_elements[item_index as usize]
2266 .borrow()
2267 .accessibility_props
2268 .0
2269 .get(&prop_name)
2270 .cloned();
2271 if let Some(nr) = nr {
2272 let value = crate::eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap();
2273 match value {
2274 Value::String(s) => *result = s,
2275 Value::Bool(b) => *result = if b { "true" } else { "false" }.into(),
2276 Value::Number(x) => *result = x.to_string().into(),
2277 _ => unimplemented!("invalid type for accessible_string_property"),
2278 };
2279 true
2280 } else {
2281 false
2282 }
2283}
2284
2285#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2286extern "C" fn accessibility_action(
2287 component: ItemTreeRefPin,
2288 item_index: u32,
2289 action: &AccessibilityAction,
2290) {
2291 let perform = |prop_name, args: &[Value]| {
2292 generativity::make_guard!(guard);
2293 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2294 let nr = instance_ref.description.original_elements[item_index as usize]
2295 .borrow()
2296 .accessibility_props
2297 .0
2298 .get(prop_name)
2299 .cloned();
2300 if let Some(nr) = nr {
2301 let instance_ref = eval::ComponentInstance::InstanceRef(instance_ref);
2302 crate::eval::invoke_callback(&instance_ref, &nr.element(), nr.name(), args).unwrap();
2303 }
2304 };
2305
2306 match action {
2307 AccessibilityAction::Default => perform("accessible-action-default", &[]),
2308 AccessibilityAction::Decrement => perform("accessible-action-decrement", &[]),
2309 AccessibilityAction::Increment => perform("accessible-action-increment", &[]),
2310 AccessibilityAction::Expand => perform("accessible-action-expand", &[]),
2311 AccessibilityAction::ReplaceSelectedText(_a) => {
2312 i_slint_core::debug_log!(
2314 "AccessibilityAction::ReplaceSelectedText not implemented in interpreter's accessibility_action"
2315 );
2316 }
2317 AccessibilityAction::SetValue(a) => {
2318 perform("accessible-action-set-value", &[Value::String(a.clone())])
2319 }
2320 };
2321}
2322
2323#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2324extern "C" fn supported_accessibility_actions(
2325 component: ItemTreeRefPin,
2326 item_index: u32,
2327) -> SupportedAccessibilityAction {
2328 generativity::make_guard!(guard);
2329 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2330 instance_ref.description.original_elements[item_index as usize]
2331 .borrow()
2332 .accessibility_props
2333 .0
2334 .keys()
2335 .filter_map(|x| x.strip_prefix("accessible-action-"))
2336 .fold(SupportedAccessibilityAction::default(), |acc, value| {
2337 SupportedAccessibilityAction::from_name(&i_slint_compiler::generator::to_pascal_case(
2338 value,
2339 ))
2340 .unwrap_or_else(|| panic!("Not an accessible action: {value:?}"))
2341 | acc
2342 })
2343}
2344
2345#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2346extern "C" fn item_element_infos(
2347 component: ItemTreeRefPin,
2348 item_index: u32,
2349 result: &mut SharedString,
2350) -> bool {
2351 generativity::make_guard!(guard);
2352 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2353 *result = instance_ref.description.original_elements[item_index as usize]
2354 .borrow()
2355 .element_infos()
2356 .into();
2357 true
2358}
2359
2360#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2361extern "C" fn window_adapter(
2362 component: ItemTreeRefPin,
2363 do_create: bool,
2364 result: &mut Option<WindowAdapterRc>,
2365) {
2366 generativity::make_guard!(guard);
2367 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2368 if do_create {
2369 *result = Some(instance_ref.window_adapter());
2370 } else {
2371 *result = instance_ref.maybe_window_adapter();
2372 }
2373}
2374
2375#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2376unsafe extern "C" fn drop_in_place(component: vtable::VRefMut<ItemTreeVTable>) -> vtable::Layout {
2377 unsafe {
2378 let instance_ptr = component.as_ptr() as *mut Instance<'static>;
2379 let layout = (*instance_ptr).type_info().layout();
2380 dynamic_type::TypeInfo::drop_in_place(instance_ptr);
2381 layout.into()
2382 }
2383}
2384
2385#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2386unsafe extern "C" fn dealloc(_vtable: &ItemTreeVTable, ptr: *mut u8, layout: vtable::Layout) {
2387 unsafe { std::alloc::dealloc(ptr, layout.try_into().unwrap()) };
2388}
2389
2390#[derive(Copy, Clone)]
2391pub struct InstanceRef<'a, 'id> {
2392 pub instance: Pin<&'a Instance<'id>>,
2393 pub description: &'a ItemTreeDescription<'id>,
2394}
2395
2396impl<'a, 'id> InstanceRef<'a, 'id> {
2397 pub unsafe fn from_pin_ref(
2398 component: ItemTreeRefPin<'a>,
2399 _guard: generativity::Guard<'id>,
2400 ) -> Self {
2401 unsafe {
2402 Self {
2403 instance: Pin::new_unchecked(
2404 &*(component.as_ref().as_ptr() as *const Instance<'id>),
2405 ),
2406 description: &*(Pin::into_inner_unchecked(component).get_vtable()
2407 as *const ItemTreeVTable
2408 as *const ItemTreeDescription<'id>),
2409 }
2410 }
2411 }
2412
2413 pub fn as_ptr(&self) -> *const u8 {
2414 (&*self.instance.as_ref()) as *const Instance as *const u8
2415 }
2416
2417 pub fn as_ref(&self) -> &Instance<'id> {
2418 &self.instance
2419 }
2420
2421 pub fn borrow(self) -> ItemTreeRefPin<'a> {
2423 unsafe {
2424 Pin::new_unchecked(vtable::VRef::from_raw(
2425 NonNull::from(&self.description.ct).cast(),
2426 NonNull::from(self.instance.get_ref()).cast(),
2427 ))
2428 }
2429 }
2430
2431 pub fn self_weak(&self) -> &OnceCell<ErasedItemTreeBoxWeak> {
2432 let extra_data = self.description.extra_data_offset.apply(self.as_ref());
2433 &extra_data.self_weak
2434 }
2435
2436 pub fn root_weak(&self) -> &ErasedItemTreeBoxWeak {
2437 self.description.root_offset.apply(self.as_ref()).get().unwrap()
2438 }
2439
2440 pub fn window_adapter(&self) -> WindowAdapterRc {
2441 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2442 let root = self.root_weak().upgrade().unwrap();
2443 generativity::make_guard!(guard);
2444 let comp = root.unerase(guard);
2445 Self::get_or_init_window_adapter_ref(
2446 &comp.description,
2447 root_weak,
2448 true,
2449 comp.instance.as_pin_ref().get_ref(),
2450 )
2451 .unwrap()
2452 .clone()
2453 }
2454
2455 pub fn get_or_init_window_adapter_ref<'b, 'id2>(
2456 description: &'b ItemTreeDescription<'id2>,
2457 root_weak: ItemTreeWeak,
2458 do_create: bool,
2459 instance: &'b Instance<'id2>,
2460 ) -> Result<&'b WindowAdapterRc, PlatformError> {
2461 description
2463 .extra_data_offset
2464 .apply(instance)
2465 .globals
2466 .get()
2467 .unwrap()
2468 .window_adapter()
2469 .unwrap()
2470 .get_or_try_init(|| {
2471 let mut parent_node = ItemWeak::default();
2472 if let Some(rc) = vtable::VWeak::upgrade(&root_weak) {
2473 vtable::VRc::borrow_pin(&rc).as_ref().parent_node(&mut parent_node);
2474 }
2475
2476 if let Some(parent) = parent_node.upgrade() {
2477 let mut result = None;
2479 vtable::VRc::borrow_pin(parent.item_tree())
2480 .as_ref()
2481 .window_adapter(do_create, &mut result);
2482 result.ok_or(PlatformError::NoPlatform)
2483 } else if do_create {
2484 let extra_data = description.extra_data_offset.apply(instance);
2485 let window_adapter = i_slint_backend_selector::with_platform(|_b| {
2487 _b.create_window_adapter()
2488 })?;
2489
2490 let comp_rc = extra_data.self_weak.get().unwrap().upgrade().unwrap();
2491 WindowInner::from_pub(window_adapter.window())
2492 .set_component(&vtable::VRc::into_dyn(comp_rc));
2493 Ok(window_adapter)
2494 } else {
2495 Err(PlatformError::NoPlatform)
2496 }
2497 })
2498 }
2499
2500 pub fn maybe_window_adapter(&self) -> Option<WindowAdapterRc> {
2501 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2502 let root = self.root_weak().upgrade()?;
2503 generativity::make_guard!(guard);
2504 let comp = root.unerase(guard);
2505 Self::get_or_init_window_adapter_ref(
2506 &comp.description,
2507 root_weak,
2508 false,
2509 comp.instance.as_pin_ref().get_ref(),
2510 )
2511 .ok()
2512 .cloned()
2513 }
2514
2515 pub fn access_window<R>(
2516 self,
2517 callback: impl FnOnce(&'_ i_slint_core::window::WindowInner) -> R,
2518 ) -> R {
2519 callback(WindowInner::from_pub(self.window_adapter().window()))
2520 }
2521
2522 pub fn parent_instance<'id2>(
2523 &self,
2524 _guard: generativity::Guard<'id2>,
2525 ) -> Option<InstanceRef<'a, 'id2>> {
2526 if let Some(parent_offset) = self.description.parent_item_tree_offset
2529 && let Some(parent) =
2530 parent_offset.apply(self.as_ref()).get().and_then(vtable::VWeak::upgrade)
2531 {
2532 let parent_instance = parent.unerase(_guard);
2533 let parent_instance = unsafe {
2535 std::mem::transmute::<InstanceRef<'_, 'id2>, InstanceRef<'a, 'id2>>(
2536 parent_instance.borrow_instance(),
2537 )
2538 };
2539 return Some(parent_instance);
2540 }
2541 None
2542 }
2543}
2544
2545pub fn show_popup(
2547 element: ElementRc,
2548 instance: InstanceRef,
2549 popup: &object_tree::PopupWindow,
2550 pos_getter: impl FnOnce(InstanceRef<'_, '_>) -> LogicalPosition,
2551 close_policy: PopupClosePolicy,
2552 parent_comp: ErasedItemTreeBoxWeak,
2553 parent_window_adapter: WindowAdapterRc,
2554 parent_item: &ItemRc,
2555) {
2556 generativity::make_guard!(guard);
2557 let debug_handler = instance.description.debug_handler.borrow().clone();
2558
2559 let compiled = generate_item_tree(
2561 &popup.component,
2562 None,
2563 parent_comp.upgrade().unwrap().0.description().popup_menu_description.clone(),
2564 false,
2565 guard,
2566 );
2567 compiled.recursively_set_debug_handler(debug_handler);
2568
2569 let extra_data = instance.description.extra_data_offset.apply(instance.as_ref());
2570 let inst = instantiate(
2571 compiled,
2572 Some(parent_comp),
2573 None,
2574 Some(&WindowOptions::UseExistingWindow(parent_window_adapter.clone())),
2575 extra_data.globals.get().unwrap().clone(),
2576 );
2577 let pos = {
2578 generativity::make_guard!(guard);
2579 let compo_box = inst.unerase(guard);
2580 let instance_ref = compo_box.borrow_instance();
2581 pos_getter(instance_ref)
2582 };
2583 close_popup(element.clone(), instance, parent_window_adapter.clone());
2584 instance.description.popup_ids.borrow_mut().insert(
2585 element.borrow().id.clone(),
2586 WindowInner::from_pub(parent_window_adapter.window()).show_popup(
2587 &vtable::VRc::into_dyn(inst.clone()),
2588 pos,
2589 close_policy,
2590 parent_item,
2591 false,
2592 ),
2593 );
2594 inst.run_setup_code();
2595}
2596
2597pub fn close_popup(
2598 element: ElementRc,
2599 instance: InstanceRef,
2600 parent_window_adapter: WindowAdapterRc,
2601) {
2602 if let Some(current_id) =
2603 instance.description.popup_ids.borrow_mut().remove(&element.borrow().id)
2604 {
2605 WindowInner::from_pub(parent_window_adapter.window()).close_popup(current_id);
2606 }
2607}
2608
2609pub fn make_menu_item_tree(
2610 menu_item_tree: &Rc<object_tree::Component>,
2611 enclosing_component: &InstanceRef,
2612 condition: Option<&Expression>,
2613) -> vtable::VRc<i_slint_core::menus::MenuVTable, MenuFromItemTree> {
2614 generativity::make_guard!(guard);
2615 let mit_compiled = generate_item_tree(
2616 menu_item_tree,
2617 None,
2618 enclosing_component.description.popup_menu_description.clone(),
2619 false,
2620 guard,
2621 );
2622 let enclosing_component_weak = enclosing_component.self_weak().get().unwrap();
2623 let extra_data =
2624 enclosing_component.description.extra_data_offset.apply(enclosing_component.as_ref());
2625 let mit_inst = instantiate(
2626 mit_compiled.clone(),
2627 Some(enclosing_component_weak.clone()),
2628 None,
2629 None,
2630 extra_data.globals.get().unwrap().clone(),
2631 );
2632 mit_inst.run_setup_code();
2633 let item_tree = vtable::VRc::into_dyn(mit_inst);
2634 let menu = match condition {
2635 Some(condition) => {
2636 let binding =
2637 make_binding_eval_closure(condition.clone(), enclosing_component_weak.clone());
2638 MenuFromItemTree::new_with_condition(item_tree, move || binding().try_into().unwrap())
2639 }
2640 None => MenuFromItemTree::new(item_tree),
2641 };
2642 vtable::VRc::new(menu)
2643}
2644
2645pub fn update_timers(instance: InstanceRef) {
2646 let ts = instance.description.original.timers.borrow();
2647 for (desc, offset) in ts.iter().zip(&instance.description.timers) {
2648 let timer = offset.apply(instance.as_ref());
2649 let running =
2650 eval::load_property(instance, &desc.running.element(), desc.running.name()).unwrap();
2651 if matches!(running, Value::Bool(true)) {
2652 let millis: i64 =
2653 eval::load_property(instance, &desc.interval.element(), desc.interval.name())
2654 .unwrap()
2655 .try_into()
2656 .expect("interval must be a duration");
2657 if millis < 0 {
2658 timer.stop();
2659 continue;
2660 }
2661 let interval = core::time::Duration::from_millis(millis as _);
2662 if !timer.running() || interval != timer.interval() {
2663 let callback = desc.triggered.clone();
2664 let self_weak = instance.self_weak().get().unwrap().clone();
2665 timer.start(i_slint_core::timers::TimerMode::Repeated, interval, move || {
2666 if let Some(instance) = self_weak.upgrade() {
2667 generativity::make_guard!(guard);
2668 let c = instance.unerase(guard);
2669 let c = c.borrow_instance();
2670 let inst = eval::ComponentInstance::InstanceRef(c);
2671 eval::invoke_callback(&inst, &callback.element(), callback.name(), &[])
2672 .unwrap();
2673 }
2674 });
2675 }
2676 } else {
2677 timer.stop();
2678 }
2679 }
2680}
2681
2682pub fn restart_timer(element: ElementWeak, instance: InstanceRef) {
2683 let timers = instance.description.original.timers.borrow();
2684 if let Some((_, offset)) = timers
2685 .iter()
2686 .zip(&instance.description.timers)
2687 .find(|(desc, _)| Weak::ptr_eq(&desc.element, &element))
2688 {
2689 let timer = offset.apply(instance.as_ref());
2690 timer.restart();
2691 }
2692}