Skip to main content

slint_interpreter/
dynamic_item_tree.rs

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