Skip to main content

slint_interpreter/
dynamic_item_tree.rs

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