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