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