Skip to main content

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