slint_interpreter/
dynamic_item_tree.rs

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