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