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