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