slint_interpreter/
dynamic_item_tree.rs

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