1use crate::api::{CompilationResult, ComponentDefinition, Value};
5use crate::global_component::CompiledGlobalCollection;
6use crate::{dynamic_type, eval};
7use core::ptr::NonNull;
8use dynamic_type::{Instance, InstanceBox};
9use i_slint_compiler::expression_tree::{Expression, NamedReference};
10use i_slint_compiler::langtype::Type;
11use i_slint_compiler::object_tree::{ElementRc, TransitionDirection};
12use i_slint_compiler::{diagnostics::BuildDiagnostics, object_tree::PropertyDeclaration};
13use i_slint_compiler::{generator, object_tree, parser, CompilerConfiguration};
14use i_slint_core::accessibility::{
15 AccessibilityAction, AccessibleStringProperty, SupportedAccessibilityAction,
16};
17use i_slint_core::api::LogicalPosition;
18use i_slint_core::component_factory::ComponentFactory;
19use i_slint_core::item_tree::{
20 IndexRange, ItemRc, ItemTree, ItemTreeNode, ItemTreeRef, ItemTreeRefPin, ItemTreeVTable,
21 ItemTreeWeak, ItemVisitorRefMut, ItemVisitorVTable, ItemWeak, TraversalOrder,
22 VisitChildrenResult,
23};
24use i_slint_core::items::{
25 AccessibleRole, ItemRef, ItemVTable, PopupClosePolicy, PropertyAnimation,
26};
27use i_slint_core::layout::{BoxLayoutCellData, LayoutInfo, Orientation};
28use i_slint_core::lengths::{LogicalLength, LogicalRect};
29use i_slint_core::menus::MenuFromItemTree;
30use i_slint_core::model::{ModelRc, RepeatedItemTree, Repeater};
31use i_slint_core::platform::PlatformError;
32use i_slint_core::properties::{ChangeTracker, InterpolatedPropertyValue};
33use i_slint_core::rtti::{self, AnimatedBindingKind, FieldOffset, PropertyInfo};
34use i_slint_core::slice::Slice;
35use i_slint_core::timers::Timer;
36use i_slint_core::window::{WindowAdapterRc, WindowInner};
37use i_slint_core::{Brush, Color, Property, SharedString, SharedVector};
38#[cfg(feature = "internal")]
39use itertools::Either;
40use once_cell::unsync::{Lazy, OnceCell};
41use smol_str::{SmolStr, ToSmolStr};
42use std::collections::BTreeMap;
43use std::collections::HashMap;
44use std::num::NonZeroU32;
45use std::rc::Weak;
46use std::{pin::Pin, rc::Rc};
47
48pub const SPECIAL_PROPERTY_INDEX: &str = "$index";
49pub const SPECIAL_PROPERTY_MODEL_DATA: &str = "$model_data";
50
51pub(crate) type CallbackHandler = Box<dyn Fn(&[Value]) -> Value>;
52
53pub struct ItemTreeBox<'id> {
54 instance: InstanceBox<'id>,
55 description: Rc<ItemTreeDescription<'id>>,
56}
57
58impl<'id> ItemTreeBox<'id> {
59 pub fn borrow(&self) -> ItemTreeRefPin<'_> {
61 self.borrow_instance().borrow()
62 }
63
64 pub fn description(&self) -> Rc<ItemTreeDescription<'id>> {
66 self.description.clone()
67 }
68
69 pub fn borrow_instance<'a>(&'a self) -> InstanceRef<'a, 'id> {
70 InstanceRef { instance: self.instance.as_pin_ref(), description: &self.description }
71 }
72
73 pub fn window_adapter_ref(&self) -> Result<&WindowAdapterRc, PlatformError> {
74 let root_weak = vtable::VWeak::into_dyn(self.borrow_instance().root_weak().clone());
75 InstanceRef::get_or_init_window_adapter_ref(
76 &self.description,
77 root_weak,
78 true,
79 self.instance.as_pin_ref().get_ref(),
80 )
81 }
82}
83
84pub(crate) type ErasedItemTreeBoxWeak = vtable::VWeak<ItemTreeVTable, ErasedItemTreeBox>;
85
86pub(crate) struct ItemWithinItemTree {
87 offset: usize,
88 pub(crate) rtti: Rc<ItemRTTI>,
89 elem: ElementRc,
90}
91
92impl ItemWithinItemTree {
93 pub(crate) unsafe fn item_from_item_tree(
95 &self,
96 mem: *const u8,
97 ) -> Pin<vtable::VRef<'_, ItemVTable>> {
98 Pin::new_unchecked(vtable::VRef::from_raw(
99 NonNull::from(self.rtti.vtable),
100 NonNull::new(mem.add(self.offset) as _).unwrap(),
101 ))
102 }
103
104 pub(crate) fn item_index(&self) -> u32 {
105 *self.elem.borrow().item_index.get().unwrap()
106 }
107}
108
109pub(crate) struct PropertiesWithinComponent {
110 pub(crate) offset: usize,
111 pub(crate) prop: Box<dyn PropertyInfo<u8, Value>>,
112}
113
114pub(crate) struct RepeaterWithinItemTree<'par_id, 'sub_id> {
115 pub(crate) item_tree_to_repeat: Rc<ItemTreeDescription<'sub_id>>,
117 pub(crate) model: Expression,
119 offset: FieldOffset<Instance<'par_id>, Repeater<ErasedItemTreeBox>>,
121 is_conditional: bool,
123}
124
125impl RepeatedItemTree for ErasedItemTreeBox {
126 type Data = Value;
127
128 fn update(&self, index: usize, data: Self::Data) {
129 generativity::make_guard!(guard);
130 let s = self.unerase(guard);
131 let is_repeated = s.description.original.parent_element.upgrade().is_some_and(|p| {
132 p.borrow().repeated.as_ref().is_some_and(|r| !r.is_conditional_element)
133 });
134 if is_repeated {
135 s.description.set_property(s.borrow(), SPECIAL_PROPERTY_INDEX, index.into()).unwrap();
136 s.description.set_property(s.borrow(), SPECIAL_PROPERTY_MODEL_DATA, data).unwrap();
137 }
138 }
139
140 fn init(&self) {
141 self.run_setup_code();
142 }
143
144 fn listview_layout(self: Pin<&Self>, offset_y: &mut LogicalLength) -> LogicalLength {
145 generativity::make_guard!(guard);
146 let s = self.unerase(guard);
147
148 let geom = s.description.original.root_element.borrow().geometry_props.clone().unwrap();
149
150 crate::eval::store_property(
151 s.borrow_instance(),
152 &geom.y.element(),
153 geom.y.name(),
154 Value::Number(offset_y.get() as f64),
155 )
156 .expect("cannot set y");
157
158 let h: LogicalLength = crate::eval::load_property(
159 s.borrow_instance(),
160 &geom.height.element(),
161 geom.height.name(),
162 )
163 .expect("missing height")
164 .try_into()
165 .expect("height not the right type");
166
167 *offset_y += h;
168 LogicalLength::new(self.borrow().as_ref().layout_info(Orientation::Horizontal).min)
169 }
170
171 fn box_layout_data(self: Pin<&Self>, o: Orientation) -> BoxLayoutCellData {
172 BoxLayoutCellData { constraint: self.borrow().as_ref().layout_info(o) }
173 }
174}
175
176impl ItemTree for ErasedItemTreeBox {
177 fn visit_children_item(
178 self: Pin<&Self>,
179 index: isize,
180 order: TraversalOrder,
181 visitor: ItemVisitorRefMut,
182 ) -> VisitChildrenResult {
183 self.borrow().as_ref().visit_children_item(index, order, visitor)
184 }
185
186 fn layout_info(self: Pin<&Self>, orientation: Orientation) -> i_slint_core::layout::LayoutInfo {
187 self.borrow().as_ref().layout_info(orientation)
188 }
189
190 fn get_item_tree(self: Pin<&Self>) -> Slice<'_, ItemTreeNode> {
191 get_item_tree(self.get_ref().borrow())
192 }
193
194 fn get_item_ref(self: Pin<&Self>, index: u32) -> Pin<ItemRef<'_>> {
195 unsafe { get_item_ref(self.get_ref().borrow(), index) }
199 }
200
201 fn get_subtree_range(self: Pin<&Self>, index: u32) -> IndexRange {
202 self.borrow().as_ref().get_subtree_range(index)
203 }
204
205 fn get_subtree(self: Pin<&Self>, index: u32, subindex: usize, result: &mut ItemTreeWeak) {
206 self.borrow().as_ref().get_subtree(index, subindex, result);
207 }
208
209 fn parent_node(self: Pin<&Self>, result: &mut ItemWeak) {
210 self.borrow().as_ref().parent_node(result)
211 }
212
213 fn embed_component(
214 self: core::pin::Pin<&Self>,
215 parent_component: &ItemTreeWeak,
216 item_tree_index: u32,
217 ) -> bool {
218 self.borrow().as_ref().embed_component(parent_component, item_tree_index)
219 }
220
221 fn subtree_index(self: Pin<&Self>) -> usize {
222 self.borrow().as_ref().subtree_index()
223 }
224
225 fn item_geometry(self: Pin<&Self>, item_index: u32) -> i_slint_core::lengths::LogicalRect {
226 self.borrow().as_ref().item_geometry(item_index)
227 }
228
229 fn accessible_role(self: Pin<&Self>, index: u32) -> AccessibleRole {
230 self.borrow().as_ref().accessible_role(index)
231 }
232
233 fn accessible_string_property(
234 self: Pin<&Self>,
235 index: u32,
236 what: AccessibleStringProperty,
237 result: &mut SharedString,
238 ) -> bool {
239 self.borrow().as_ref().accessible_string_property(index, what, result)
240 }
241
242 fn window_adapter(self: Pin<&Self>, do_create: bool, result: &mut Option<WindowAdapterRc>) {
243 self.borrow().as_ref().window_adapter(do_create, result);
244 }
245
246 fn accessibility_action(self: core::pin::Pin<&Self>, index: u32, action: &AccessibilityAction) {
247 self.borrow().as_ref().accessibility_action(index, action)
248 }
249
250 fn supported_accessibility_actions(
251 self: core::pin::Pin<&Self>,
252 index: u32,
253 ) -> SupportedAccessibilityAction {
254 self.borrow().as_ref().supported_accessibility_actions(index)
255 }
256
257 fn item_element_infos(
258 self: core::pin::Pin<&Self>,
259 index: u32,
260 result: &mut SharedString,
261 ) -> bool {
262 self.borrow().as_ref().item_element_infos(index, result)
263 }
264}
265
266i_slint_core::ItemTreeVTable_static!(static COMPONENT_BOX_VT for ErasedItemTreeBox);
267
268impl Drop for ErasedItemTreeBox {
269 fn drop(&mut self) {
270 generativity::make_guard!(guard);
271 let unerase = self.unerase(guard);
272 let instance_ref = unerase.borrow_instance();
273 if let Some(window_adapter) = instance_ref.maybe_window_adapter() {
275 i_slint_core::item_tree::unregister_item_tree(
276 instance_ref.instance,
277 vtable::VRef::new(self),
278 instance_ref.description.item_array.as_slice(),
279 &window_adapter,
280 );
281 }
282 }
283}
284
285pub type DynamicComponentVRc = vtable::VRc<ItemTreeVTable, ErasedItemTreeBox>;
286
287#[derive(Default)]
288pub(crate) struct ComponentExtraData {
289 pub(crate) globals: OnceCell<crate::global_component::GlobalStorage>,
290 pub(crate) self_weak: OnceCell<ErasedItemTreeBoxWeak>,
291 pub(crate) embedding_position: OnceCell<(ItemTreeWeak, u32)>,
292}
293
294struct ErasedRepeaterWithinComponent<'id>(RepeaterWithinItemTree<'id, 'static>);
295impl<'id, 'sub_id> From<RepeaterWithinItemTree<'id, 'sub_id>>
296 for ErasedRepeaterWithinComponent<'id>
297{
298 fn from(from: RepeaterWithinItemTree<'id, 'sub_id>) -> Self {
299 Self(unsafe {
302 core::mem::transmute::<
303 RepeaterWithinItemTree<'id, 'sub_id>,
304 RepeaterWithinItemTree<'id, 'static>,
305 >(from)
306 })
307 }
308}
309impl<'id> ErasedRepeaterWithinComponent<'id> {
310 pub fn unerase<'a, 'sub_id>(
311 &'a self,
312 _guard: generativity::Guard<'sub_id>,
313 ) -> &'a RepeaterWithinItemTree<'id, 'sub_id> {
314 unsafe {
316 core::mem::transmute::<
317 &'a RepeaterWithinItemTree<'id, 'static>,
318 &'a RepeaterWithinItemTree<'id, 'sub_id>,
319 >(&self.0)
320 }
321 }
322
323 unsafe fn get_untagged(&self) -> &RepeaterWithinItemTree<'id, 'static> {
327 &self.0
328 }
329}
330
331type Callback = i_slint_core::Callback<[Value], Value>;
332
333#[derive(Clone)]
334pub struct ErasedItemTreeDescription(Rc<ItemTreeDescription<'static>>);
335impl ErasedItemTreeDescription {
336 pub fn unerase<'a, 'id>(
337 &'a self,
338 _guard: generativity::Guard<'id>,
339 ) -> &'a Rc<ItemTreeDescription<'id>> {
340 unsafe {
342 core::mem::transmute::<
343 &'a Rc<ItemTreeDescription<'static>>,
344 &'a Rc<ItemTreeDescription<'id>>,
345 >(&self.0)
346 }
347 }
348}
349impl<'id> From<Rc<ItemTreeDescription<'id>>> for ErasedItemTreeDescription {
350 fn from(from: Rc<ItemTreeDescription<'id>>) -> Self {
351 Self(unsafe {
353 core::mem::transmute::<Rc<ItemTreeDescription<'id>>, Rc<ItemTreeDescription<'static>>>(
354 from,
355 )
356 })
357 }
358}
359
360#[repr(C)]
367pub struct ItemTreeDescription<'id> {
368 pub(crate) ct: ItemTreeVTable,
369 dynamic_type: Rc<dynamic_type::TypeInfo<'id>>,
371 item_tree: Vec<ItemTreeNode>,
372 item_array:
373 Vec<vtable::VOffset<crate::dynamic_type::Instance<'id>, ItemVTable, vtable::AllowPin>>,
374 pub(crate) items: HashMap<SmolStr, ItemWithinItemTree>,
375 pub(crate) custom_properties: HashMap<SmolStr, PropertiesWithinComponent>,
376 pub(crate) custom_callbacks: HashMap<SmolStr, FieldOffset<Instance<'id>, Callback>>,
377 repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
378 pub repeater_names: HashMap<SmolStr, usize>,
380 pub(crate) parent_item_tree_offset:
382 Option<FieldOffset<Instance<'id>, OnceCell<ErasedItemTreeBoxWeak>>>,
383 pub(crate) root_offset: FieldOffset<Instance<'id>, OnceCell<ErasedItemTreeBoxWeak>>,
384 pub(crate) window_adapter_offset: FieldOffset<Instance<'id>, OnceCell<WindowAdapterRc>>,
386 pub(crate) extra_data_offset: FieldOffset<Instance<'id>, ComponentExtraData>,
388 pub(crate) original: Rc<object_tree::Component>,
390 pub(crate) original_elements: Vec<ElementRc>,
392 public_properties: BTreeMap<SmolStr, PropertyDeclaration>,
394 change_trackers: Option<(
395 FieldOffset<Instance<'id>, OnceCell<Vec<ChangeTracker>>>,
396 Vec<(NamedReference, Expression)>,
397 )>,
398 timers: Vec<FieldOffset<Instance<'id>, Timer>>,
399 popup_ids: std::cell::RefCell<HashMap<SmolStr, NonZeroU32>>,
401
402 pub(crate) popup_menu_description: PopupMenuDescription,
403
404 compiled_globals: Option<Rc<CompiledGlobalCollection>>,
406
407 #[cfg(feature = "internal-highlight")]
410 pub(crate) type_loader:
411 std::cell::OnceCell<std::rc::Rc<i_slint_compiler::typeloader::TypeLoader>>,
412 #[cfg(feature = "internal-highlight")]
415 pub(crate) raw_type_loader:
416 std::cell::OnceCell<Option<std::rc::Rc<i_slint_compiler::typeloader::TypeLoader>>>,
417
418 pub(crate) debug_handler: std::cell::RefCell<
419 Rc<dyn Fn(Option<&i_slint_compiler::diagnostics::SourceLocation>, &str)>,
420 >,
421}
422
423#[derive(Clone, derive_more::From)]
424pub(crate) enum PopupMenuDescription {
425 Rc(Rc<ErasedItemTreeDescription>),
426 Weak(Weak<ErasedItemTreeDescription>),
427}
428impl PopupMenuDescription {
429 pub fn unerase<'id>(&self, guard: generativity::Guard<'id>) -> Rc<ItemTreeDescription<'id>> {
430 match self {
431 PopupMenuDescription::Rc(rc) => rc.unerase(guard).clone(),
432 PopupMenuDescription::Weak(weak) => weak.upgrade().unwrap().unerase(guard).clone(),
433 }
434 }
435}
436
437fn internal_properties_to_public<'a>(
438 prop_iter: impl Iterator<Item = (&'a SmolStr, &'a PropertyDeclaration)> + 'a,
439) -> impl Iterator<
440 Item = (
441 SmolStr,
442 i_slint_compiler::langtype::Type,
443 i_slint_compiler::object_tree::PropertyVisibility,
444 ),
445> + 'a {
446 prop_iter.filter(|(_, v)| v.expose_in_public_api).map(|(s, v)| {
447 let name = v
448 .node
449 .as_ref()
450 .and_then(|n| {
451 n.child_node(parser::SyntaxKind::DeclaredIdentifier)
452 .and_then(|n| n.child_token(parser::SyntaxKind::Identifier))
453 })
454 .map(|n| n.to_smolstr())
455 .unwrap_or_else(|| s.to_smolstr());
456 (name, v.property_type.clone(), v.visibility)
457 })
458}
459
460#[derive(Default)]
461pub enum WindowOptions {
462 #[default]
463 CreateNewWindow,
464 UseExistingWindow(WindowAdapterRc),
465 Embed {
466 parent_item_tree: ItemTreeWeak,
467 parent_item_tree_index: u32,
468 },
469}
470
471impl ItemTreeDescription<'_> {
472 pub fn id(&self) -> &str {
474 self.original.id.as_str()
475 }
476
477 pub fn properties(
481 &self,
482 ) -> impl Iterator<
483 Item = (
484 SmolStr,
485 i_slint_compiler::langtype::Type,
486 i_slint_compiler::object_tree::PropertyVisibility,
487 ),
488 > + '_ {
489 internal_properties_to_public(self.public_properties.iter())
490 }
491
492 pub fn global_names(&self) -> impl Iterator<Item = SmolStr> + '_ {
494 self.compiled_globals
495 .as_ref()
496 .expect("Root component should have globals")
497 .compiled_globals
498 .iter()
499 .filter(|g| g.visible_in_public_api())
500 .flat_map(|g| g.names().into_iter())
501 }
502
503 pub fn global_properties(
504 &self,
505 name: &str,
506 ) -> Option<
507 impl Iterator<
508 Item = (
509 SmolStr,
510 i_slint_compiler::langtype::Type,
511 i_slint_compiler::object_tree::PropertyVisibility,
512 ),
513 > + '_,
514 > {
515 let g = self.compiled_globals.as_ref().expect("Root component should have globals");
516 g.exported_globals_by_name
517 .get(&crate::normalize_identifier(name))
518 .and_then(|global_idx| g.compiled_globals.get(*global_idx))
519 .map(|global| internal_properties_to_public(global.public_properties()))
520 }
521
522 pub fn create(
524 self: Rc<Self>,
525 options: WindowOptions,
526 ) -> Result<DynamicComponentVRc, PlatformError> {
527 i_slint_backend_selector::with_platform(|_b| {
528 Ok(())
530 })?;
531
532 let instance = instantiate(self, None, None, Some(&options), Default::default());
533 if let WindowOptions::UseExistingWindow(existing_adapter) = options {
534 WindowInner::from_pub(existing_adapter.window())
535 .set_component(&vtable::VRc::into_dyn(instance.clone()));
536 }
537 instance.run_setup_code();
538 Ok(instance)
539 }
540
541 pub fn set_property(
547 &self,
548 component: ItemTreeRefPin,
549 name: &str,
550 value: Value,
551 ) -> Result<(), crate::api::SetPropertyError> {
552 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
553 panic!("mismatch instance and vtable");
554 }
555 generativity::make_guard!(guard);
556 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
557 if let Some(alias) = self
558 .original
559 .root_element
560 .borrow()
561 .property_declarations
562 .get(name)
563 .and_then(|d| d.is_alias.as_ref())
564 {
565 eval::store_property(c, &alias.element(), alias.name(), value)
566 } else {
567 eval::store_property(c, &self.original.root_element, name, value)
568 }
569 }
570
571 pub fn set_binding(
576 &self,
577 component: ItemTreeRefPin,
578 name: &str,
579 binding: Box<dyn Fn() -> Value>,
580 ) -> Result<(), ()> {
581 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
582 return Err(());
583 }
584 let x = self.custom_properties.get(name).ok_or(())?;
585 unsafe {
586 x.prop
587 .set_binding(
588 Pin::new_unchecked(&*component.as_ptr().add(x.offset)),
589 binding,
590 i_slint_core::rtti::AnimatedBindingKind::NotAnimated,
591 )
592 .unwrap()
593 };
594 Ok(())
595 }
596
597 pub fn get_property(&self, component: ItemTreeRefPin, name: &str) -> Result<Value, ()> {
602 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
603 return Err(());
604 }
605 generativity::make_guard!(guard);
606 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
608 if let Some(alias) = self
609 .original
610 .root_element
611 .borrow()
612 .property_declarations
613 .get(name)
614 .and_then(|d| d.is_alias.as_ref())
615 {
616 eval::load_property(c, &alias.element(), alias.name())
617 } else {
618 eval::load_property(c, &self.original.root_element, name)
619 }
620 }
621
622 pub fn set_callback_handler(
627 &self,
628 component: Pin<ItemTreeRef>,
629 name: &str,
630 handler: CallbackHandler,
631 ) -> Result<(), ()> {
632 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
633 return Err(());
634 }
635 if let Some(alias) = self
636 .original
637 .root_element
638 .borrow()
639 .property_declarations
640 .get(name)
641 .and_then(|d| d.is_alias.as_ref())
642 {
643 generativity::make_guard!(guard);
644 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
646 let inst = eval::ComponentInstance::InstanceRef(c);
647 eval::set_callback_handler(&inst, &alias.element(), alias.name(), handler)?
648 } else {
649 let x = self.custom_callbacks.get(name).ok_or(())?;
650 let sig = x.apply(unsafe { &*(component.as_ptr() as *const dynamic_type::Instance) });
651 sig.set_handler(handler);
652 }
653 Ok(())
654 }
655
656 pub fn invoke(
661 &self,
662 component: ItemTreeRefPin,
663 name: &SmolStr,
664 args: &[Value],
665 ) -> Result<Value, ()> {
666 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
667 return Err(());
668 }
669 generativity::make_guard!(guard);
670 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
672 let borrow = self.original.root_element.borrow();
673 let decl = borrow.property_declarations.get(name).ok_or(())?;
674
675 let (elem, name) = if let Some(alias) = &decl.is_alias {
676 (alias.element(), alias.name())
677 } else {
678 (self.original.root_element.clone(), name)
679 };
680
681 let inst = eval::ComponentInstance::InstanceRef(c);
682
683 if matches!(&decl.property_type, Type::Function { .. }) {
684 eval::call_function(&inst, &elem, name, args.to_vec()).ok_or(())
685 } else {
686 eval::invoke_callback(&inst, &elem, name, args).ok_or(())
687 }
688 }
689
690 pub fn get_global(
692 &self,
693 component: ItemTreeRefPin,
694 global_name: &str,
695 ) -> Result<Pin<Rc<dyn crate::global_component::GlobalComponent>>, ()> {
696 if !core::ptr::eq((&self.ct) as *const _, component.get_vtable() as *const _) {
697 return Err(());
698 }
699 generativity::make_guard!(guard);
700 let c = unsafe { InstanceRef::from_pin_ref(component, guard) };
702 let extra_data = c.description.extra_data_offset.apply(c.instance.get_ref());
703 let g = extra_data.globals.get().unwrap().get(global_name).clone();
704 g.ok_or(())
705 }
706
707 pub fn recursively_set_debug_handler(
708 &self,
709 handler: Rc<dyn Fn(Option<&i_slint_compiler::diagnostics::SourceLocation>, &str)>,
710 ) {
711 *self.debug_handler.borrow_mut() = handler.clone();
712
713 for r in &self.repeater {
714 generativity::make_guard!(guard);
715 r.unerase(guard).item_tree_to_repeat.recursively_set_debug_handler(handler.clone());
716 }
717 }
718}
719
720#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
721extern "C" fn visit_children_item(
722 component: ItemTreeRefPin,
723 index: isize,
724 order: TraversalOrder,
725 v: ItemVisitorRefMut,
726) -> VisitChildrenResult {
727 generativity::make_guard!(guard);
728 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
729 let comp_rc = instance_ref.self_weak().get().unwrap().upgrade().unwrap();
730 i_slint_core::item_tree::visit_item_tree(
731 instance_ref.instance,
732 &vtable::VRc::into_dyn(comp_rc),
733 get_item_tree(component).as_slice(),
734 index,
735 order,
736 v,
737 |_, order, visitor, index| {
738 if index as usize >= instance_ref.description.repeater.len() {
739 VisitChildrenResult::CONTINUE
741 } else {
742 let rep_in_comp =
745 unsafe { instance_ref.description.repeater[index as usize].get_untagged() };
746 ensure_repeater_updated(instance_ref, rep_in_comp);
747 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
748 repeater.visit(order, visitor)
749 }
750 },
751 )
752}
753
754fn ensure_repeater_updated<'id>(
756 instance_ref: InstanceRef<'_, 'id>,
757 rep_in_comp: &RepeaterWithinItemTree<'id, '_>,
758) {
759 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
760 let init = || {
761 let instance = instantiate(
762 rep_in_comp.item_tree_to_repeat.clone(),
763 instance_ref.self_weak().get().cloned(),
764 None,
765 None,
766 Default::default(),
767 );
768 instance
769 };
770 if let Some(lv) = &rep_in_comp
771 .item_tree_to_repeat
772 .original
773 .parent_element
774 .upgrade()
775 .unwrap()
776 .borrow()
777 .repeated
778 .as_ref()
779 .unwrap()
780 .is_listview
781 {
782 let assume_property_logical_length =
783 |prop| unsafe { Pin::new_unchecked(&*(prop as *const Property<LogicalLength>)) };
784 let get_prop = |nr: &NamedReference| -> LogicalLength {
785 eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap().try_into().unwrap()
786 };
787 repeater.ensure_updated_listview(
788 init,
789 assume_property_logical_length(get_property_ptr(&lv.viewport_width, instance_ref)),
790 assume_property_logical_length(get_property_ptr(&lv.viewport_height, instance_ref)),
791 assume_property_logical_length(get_property_ptr(&lv.viewport_y, instance_ref)),
792 get_prop(&lv.listview_width),
793 assume_property_logical_length(get_property_ptr(&lv.listview_height, instance_ref)),
794 );
795 } else {
796 repeater.ensure_updated(init);
797 }
798}
799
800pub(crate) struct ItemRTTI {
802 vtable: &'static ItemVTable,
803 type_info: dynamic_type::StaticTypeInfo,
804 pub(crate) properties: HashMap<&'static str, Box<dyn eval::ErasedPropertyInfo>>,
805 pub(crate) callbacks: HashMap<&'static str, Box<dyn eval::ErasedCallbackInfo>>,
806}
807
808fn rtti_for<T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<ItemVTable>>(
809) -> (&'static str, Rc<ItemRTTI>) {
810 let rtti = ItemRTTI {
811 vtable: T::static_vtable(),
812 type_info: dynamic_type::StaticTypeInfo::new::<T>(),
813 properties: T::properties()
814 .into_iter()
815 .map(|(k, v)| (k, Box::new(v) as Box<dyn eval::ErasedPropertyInfo>))
816 .collect(),
817 callbacks: T::callbacks()
818 .into_iter()
819 .map(|(k, v)| (k, Box::new(v) as Box<dyn eval::ErasedCallbackInfo>))
820 .collect(),
821 };
822 (T::name(), Rc::new(rtti))
823}
824
825pub async fn load(
829 source: String,
830 path: std::path::PathBuf,
831 mut compiler_config: CompilerConfiguration,
832) -> CompilationResult {
833 let is_native = match &compiler_config.style {
835 Some(s) => s == "native",
836 None => std::env::var("SLINT_STYLE").map_or(true, |s| s == "native"),
837 };
838 if is_native {
839 #[cfg(target_arch = "wasm32")]
841 let target = web_sys::window()
842 .and_then(|window| window.navigator().platform().ok())
843 .map_or("wasm", |platform| {
844 let platform = platform.to_ascii_lowercase();
845 if platform.contains("mac")
846 || platform.contains("iphone")
847 || platform.contains("ipad")
848 {
849 "apple"
850 } else if platform.contains("android") {
851 "android"
852 } else if platform.contains("win") {
853 "windows"
854 } else if platform.contains("linux") {
855 "linux"
856 } else {
857 "wasm"
858 }
859 });
860 #[cfg(not(target_arch = "wasm32"))]
861 let target = "";
862 compiler_config.style = Some(
863 i_slint_common::get_native_style(i_slint_backend_selector::HAS_NATIVE_STYLE, target)
864 .to_string(),
865 );
866 }
867
868 let diag = BuildDiagnostics::default();
869 #[cfg(feature = "internal-highlight")]
870 let (path, mut diag, loader, raw_type_loader) =
871 i_slint_compiler::load_root_file_with_raw_type_loader(
872 &path,
873 &path,
874 source,
875 diag,
876 compiler_config,
877 )
878 .await;
879 #[cfg(not(feature = "internal-highlight"))]
880 let (path, mut diag, loader) =
881 i_slint_compiler::load_root_file(&path, &path, source, diag, compiler_config).await;
882 if diag.has_errors() {
883 return CompilationResult {
884 components: HashMap::new(),
885 diagnostics: diag.into_iter().collect(),
886 #[cfg(feature = "internal")]
887 structs_and_enums: Vec::new(),
888 #[cfg(feature = "internal")]
889 named_exports: Vec::new(),
890 };
891 }
892
893 #[cfg(feature = "internal-highlight")]
894 let loader = Rc::new(loader);
895 #[cfg(feature = "internal-highlight")]
896 let raw_type_loader = raw_type_loader.map(Rc::new);
897
898 let doc = loader.get_document(&path).unwrap();
899
900 let compiled_globals = Rc::new(CompiledGlobalCollection::compile(doc));
901 let mut components = HashMap::new();
902
903 let popup_menu_description = if let Some(popup_menu_impl) = &doc.popup_menu_impl {
904 PopupMenuDescription::Rc(Rc::new_cyclic(|weak| {
905 generativity::make_guard!(guard);
906 ErasedItemTreeDescription::from(generate_item_tree(
907 popup_menu_impl,
908 Some(compiled_globals.clone()),
909 PopupMenuDescription::Weak(weak.clone()),
910 true,
911 guard,
912 ))
913 }))
914 } else {
915 PopupMenuDescription::Weak(Default::default())
916 };
917
918 for c in doc.exported_roots() {
919 generativity::make_guard!(guard);
920 #[allow(unused_mut)]
921 let mut it = generate_item_tree(
922 &c,
923 Some(compiled_globals.clone()),
924 popup_menu_description.clone(),
925 false,
926 guard,
927 );
928 #[cfg(feature = "internal-highlight")]
929 {
930 let _ = it.type_loader.set(loader.clone());
931 let _ = it.raw_type_loader.set(raw_type_loader.clone());
932 }
933 components.insert(c.id.to_string(), ComponentDefinition { inner: it.into() });
934 }
935
936 if components.is_empty() {
937 diag.push_error_with_span("No component found".into(), Default::default());
938 };
939
940 #[cfg(feature = "internal")]
941 let structs_and_enums = doc.used_types.borrow().structs_and_enums.clone();
942
943 #[cfg(feature = "internal")]
944 let named_exports = doc
945 .exports
946 .iter()
947 .filter_map(|export| match &export.1 {
948 Either::Left(component) if !component.is_global() => {
949 Some((&export.0.name, &component.id))
950 }
951 Either::Right(ty) => match &ty {
952 Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
953 Some((&export.0.name, s.name.as_ref().unwrap()))
954 }
955 Type::Enumeration(en) => Some((&export.0.name, &en.name)),
956 _ => None,
957 },
958 _ => None,
959 })
960 .filter(|(export_name, type_name)| *export_name != *type_name)
961 .map(|(export_name, type_name)| (type_name.to_string(), export_name.to_string()))
962 .collect::<Vec<_>>();
963
964 CompilationResult {
965 diagnostics: diag.into_iter().collect(),
966 components,
967 #[cfg(feature = "internal")]
968 structs_and_enums,
969 #[cfg(feature = "internal")]
970 named_exports,
971 }
972}
973
974fn generate_rtti() -> HashMap<&'static str, Rc<ItemRTTI>> {
975 let mut rtti = HashMap::new();
976 use i_slint_core::items::*;
977 rtti.extend(
978 [
979 rtti_for::<ComponentContainer>(),
980 rtti_for::<Empty>(),
981 rtti_for::<ImageItem>(),
982 rtti_for::<ClippedImage>(),
983 rtti_for::<ComplexText>(),
984 rtti_for::<SimpleText>(),
985 rtti_for::<Rectangle>(),
986 rtti_for::<BasicBorderRectangle>(),
987 rtti_for::<BorderRectangle>(),
988 rtti_for::<TouchArea>(),
989 rtti_for::<FocusScope>(),
990 rtti_for::<SwipeGestureHandler>(),
991 rtti_for::<Path>(),
992 rtti_for::<Flickable>(),
993 rtti_for::<WindowItem>(),
994 rtti_for::<TextInput>(),
995 rtti_for::<Clip>(),
996 rtti_for::<BoxShadow>(),
997 rtti_for::<Rotate>(),
998 rtti_for::<Opacity>(),
999 rtti_for::<Layer>(),
1000 rtti_for::<ContextMenu>(),
1001 rtti_for::<MenuItem>(),
1002 ]
1003 .iter()
1004 .cloned(),
1005 );
1006
1007 trait NativeHelper {
1008 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>);
1009 }
1010 impl NativeHelper for () {
1011 fn push(_rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {}
1012 }
1013 impl<
1014 T: 'static + Default + rtti::BuiltinItem + vtable::HasStaticVTable<ItemVTable>,
1015 Next: NativeHelper,
1016 > NativeHelper for (T, Next)
1017 {
1018 fn push(rtti: &mut HashMap<&str, Rc<ItemRTTI>>) {
1019 let info = rtti_for::<T>();
1020 rtti.insert(info.0, info.1);
1021 Next::push(rtti);
1022 }
1023 }
1024 i_slint_backend_selector::NativeWidgets::push(&mut rtti);
1025
1026 rtti
1027}
1028
1029pub(crate) fn generate_item_tree<'id>(
1030 component: &Rc<object_tree::Component>,
1031 compiled_globals: Option<Rc<CompiledGlobalCollection>>,
1032 popup_menu_description: PopupMenuDescription,
1033 is_popup_menu_impl: bool,
1034 guard: generativity::Guard<'id>,
1035) -> Rc<ItemTreeDescription<'id>> {
1036 thread_local! {
1039 static RTTI: Lazy<HashMap<&'static str, Rc<ItemRTTI>>> = Lazy::new(generate_rtti);
1040 }
1041
1042 struct TreeBuilder<'id> {
1043 tree_array: Vec<ItemTreeNode>,
1044 item_array:
1045 Vec<vtable::VOffset<crate::dynamic_type::Instance<'id>, ItemVTable, vtable::AllowPin>>,
1046 original_elements: Vec<ElementRc>,
1047 items_types: HashMap<SmolStr, ItemWithinItemTree>,
1048 type_builder: dynamic_type::TypeBuilder<'id>,
1049 repeater: Vec<ErasedRepeaterWithinComponent<'id>>,
1050 repeater_names: HashMap<SmolStr, usize>,
1051 change_callbacks: Vec<(NamedReference, Expression)>,
1052 popup_menu_description: PopupMenuDescription,
1053 }
1054 impl generator::ItemTreeBuilder for TreeBuilder<'_> {
1055 type SubComponentState = ();
1056
1057 fn push_repeated_item(
1058 &mut self,
1059 item_rc: &ElementRc,
1060 repeater_count: u32,
1061 parent_index: u32,
1062 _component_state: &Self::SubComponentState,
1063 ) {
1064 self.tree_array.push(ItemTreeNode::DynamicTree { index: repeater_count, parent_index });
1065 self.original_elements.push(item_rc.clone());
1066 let item = item_rc.borrow();
1067 let base_component = item.base_type.as_component();
1068 self.repeater_names.insert(item.id.clone(), self.repeater.len());
1069 generativity::make_guard!(guard);
1070 let repeated_element_info = item.repeated.as_ref().unwrap();
1071 self.repeater.push(
1072 RepeaterWithinItemTree {
1073 item_tree_to_repeat: generate_item_tree(
1074 base_component,
1075 None,
1076 self.popup_menu_description.clone(),
1077 false,
1078 guard,
1079 ),
1080 offset: self.type_builder.add_field_type::<Repeater<ErasedItemTreeBox>>(),
1081 model: repeated_element_info.model.clone(),
1082 is_conditional: repeated_element_info.is_conditional_element,
1083 }
1084 .into(),
1085 );
1086 }
1087
1088 fn push_native_item(
1089 &mut self,
1090 rc_item: &ElementRc,
1091 child_offset: u32,
1092 parent_index: u32,
1093 _component_state: &Self::SubComponentState,
1094 ) {
1095 let item = rc_item.borrow();
1096 let rt = RTTI.with(|rtti| {
1097 rtti.get(&*item.base_type.as_native().class_name)
1098 .unwrap_or_else(|| {
1099 panic!(
1100 "Native type not registered: {}",
1101 item.base_type.as_native().class_name
1102 )
1103 })
1104 .clone()
1105 });
1106
1107 let offset = self.type_builder.add_field(rt.type_info);
1108
1109 self.tree_array.push(ItemTreeNode::Item {
1110 is_accessible: !item.accessibility_props.0.is_empty(),
1111 children_index: child_offset,
1112 children_count: item.children.len() as u32,
1113 parent_index,
1114 item_array_index: self.item_array.len() as u32,
1115 });
1116 self.item_array.push(unsafe { vtable::VOffset::from_raw(rt.vtable, offset) });
1117 self.original_elements.push(rc_item.clone());
1118 debug_assert_eq!(self.original_elements.len(), self.tree_array.len());
1119 self.items_types.insert(
1120 item.id.clone(),
1121 ItemWithinItemTree { offset, rtti: rt, elem: rc_item.clone() },
1122 );
1123 for (prop, expr) in &item.change_callbacks {
1124 self.change_callbacks.push((
1125 NamedReference::new(rc_item, prop.clone()),
1126 Expression::CodeBlock(expr.borrow().clone()),
1127 ));
1128 }
1129 }
1130
1131 fn enter_component(
1132 &mut self,
1133 _item: &ElementRc,
1134 _sub_component: &Rc<object_tree::Component>,
1135 _children_offset: u32,
1136 _component_state: &Self::SubComponentState,
1137 ) -> Self::SubComponentState {
1138 }
1140
1141 fn enter_component_children(
1142 &mut self,
1143 _item: &ElementRc,
1144 _repeater_count: u32,
1145 _component_state: &Self::SubComponentState,
1146 _sub_component_state: &Self::SubComponentState,
1147 ) {
1148 todo!()
1149 }
1150 }
1151
1152 let mut builder = TreeBuilder {
1153 tree_array: vec![],
1154 item_array: vec![],
1155 original_elements: vec![],
1156 items_types: HashMap::new(),
1157 type_builder: dynamic_type::TypeBuilder::new(guard),
1158 repeater: vec![],
1159 repeater_names: HashMap::new(),
1160 change_callbacks: vec![],
1161 popup_menu_description,
1162 };
1163
1164 if !component.is_global() {
1165 generator::build_item_tree(component, &(), &mut builder);
1166 } else {
1167 for (prop, expr) in component.root_element.borrow().change_callbacks.iter() {
1168 builder.change_callbacks.push((
1169 NamedReference::new(&component.root_element, prop.clone()),
1170 Expression::CodeBlock(expr.borrow().clone()),
1171 ));
1172 }
1173 }
1174
1175 let mut custom_properties = HashMap::new();
1176 let mut custom_callbacks = HashMap::new();
1177 fn property_info<T>() -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1178 where
1179 T: PartialEq + Clone + Default + std::convert::TryInto<Value> + 'static,
1180 Value: std::convert::TryInto<T>,
1181 {
1182 (
1184 Box::new(unsafe {
1185 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0)
1186 }),
1187 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1188 )
1189 }
1190 fn animated_property_info<T>(
1191 ) -> (Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)
1192 where
1193 T: Clone + Default + InterpolatedPropertyValue + std::convert::TryInto<Value> + 'static,
1194 Value: std::convert::TryInto<T>,
1195 {
1196 (
1198 Box::new(unsafe {
1199 rtti::MaybeAnimatedPropertyInfoWrapper(
1200 vtable::FieldOffset::<u8, Property<T>, _>::new_from_offset_pinned(0),
1201 )
1202 }),
1203 dynamic_type::StaticTypeInfo::new::<Property<T>>(),
1204 )
1205 }
1206
1207 fn property_info_for_type(
1208 ty: &Type,
1209 ) -> Option<(Box<dyn PropertyInfo<u8, Value>>, dynamic_type::StaticTypeInfo)> {
1210 Some(match ty {
1211 Type::Float32 => animated_property_info::<f32>(),
1212 Type::Int32 => animated_property_info::<i32>(),
1213 Type::String => property_info::<SharedString>(),
1214 Type::Color => animated_property_info::<Color>(),
1215 Type::Brush => animated_property_info::<Brush>(),
1216 Type::Duration => animated_property_info::<i64>(),
1217 Type::Angle => animated_property_info::<f32>(),
1218 Type::PhysicalLength => animated_property_info::<f32>(),
1219 Type::LogicalLength => animated_property_info::<f32>(),
1220 Type::Rem => animated_property_info::<f32>(),
1221 Type::Image => property_info::<i_slint_core::graphics::Image>(),
1222 Type::Bool => property_info::<bool>(),
1223 Type::ComponentFactory => property_info::<ComponentFactory>(),
1224 Type::Struct(s)
1225 if s.name.as_ref().is_some_and(|name| name.ends_with("::StateInfo")) =>
1226 {
1227 property_info::<i_slint_core::properties::StateInfo>()
1228 }
1229 Type::Struct(_) => property_info::<Value>(),
1230 Type::Array(_) => property_info::<Value>(),
1231 Type::Easing => property_info::<i_slint_core::animations::EasingCurve>(),
1232 Type::Percent => animated_property_info::<f32>(),
1233 Type::Enumeration(e) => {
1234 macro_rules! match_enum_type {
1235 ($( $(#[$enum_doc:meta])* enum $Name:ident { $($body:tt)* })*) => {
1236 match e.name.as_str() {
1237 $(
1238 stringify!($Name) => property_info::<i_slint_core::items::$Name>(),
1239 )*
1240 x => unreachable!("Unknown non-builtin enum {x}"),
1241 }
1242 }
1243 }
1244 if e.node.is_some() {
1245 property_info::<Value>()
1246 } else {
1247 i_slint_common::for_each_enums!(match_enum_type)
1248 }
1249 }
1250 Type::LayoutCache => property_info::<SharedVector<f32>>(),
1251 Type::Function { .. } | Type::Callback { .. } => return None,
1252
1253 Type::Invalid
1255 | Type::Void
1256 | Type::InferredProperty
1257 | Type::InferredCallback
1258 | Type::Model
1259 | Type::PathData
1260 | Type::UnitProduct(_)
1261 | Type::ElementReference => panic!("bad type {ty:?}"),
1262 })
1263 }
1264
1265 for (name, decl) in &component.root_element.borrow().property_declarations {
1266 if decl.is_alias.is_some() {
1267 continue;
1268 }
1269 if matches!(&decl.property_type, Type::Callback { .. }) {
1270 custom_callbacks
1271 .insert(name.clone(), builder.type_builder.add_field_type::<Callback>());
1272 continue;
1273 }
1274 let Some((prop, type_info)) = property_info_for_type(&decl.property_type) else { continue };
1275 custom_properties.insert(
1276 name.clone(),
1277 PropertiesWithinComponent { offset: builder.type_builder.add_field(type_info), prop },
1278 );
1279 }
1280 if let Some(parent_element) = component.parent_element.upgrade() {
1281 if let Some(r) = &parent_element.borrow().repeated {
1282 if !r.is_conditional_element {
1283 let (prop, type_info) = property_info::<u32>();
1284 custom_properties.insert(
1285 SPECIAL_PROPERTY_INDEX.into(),
1286 PropertiesWithinComponent {
1287 offset: builder.type_builder.add_field(type_info),
1288 prop,
1289 },
1290 );
1291
1292 let model_ty = Expression::RepeaterModelReference {
1293 element: component.parent_element.clone(),
1294 }
1295 .ty();
1296 let (prop, type_info) = property_info_for_type(&model_ty).unwrap();
1297 custom_properties.insert(
1298 SPECIAL_PROPERTY_MODEL_DATA.into(),
1299 PropertiesWithinComponent {
1300 offset: builder.type_builder.add_field(type_info),
1301 prop,
1302 },
1303 );
1304 }
1305 }
1306 }
1307
1308 let parent_item_tree_offset =
1309 if component.parent_element.upgrade().is_some() || is_popup_menu_impl {
1310 Some(builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>())
1311 } else {
1312 None
1313 };
1314
1315 let root_offset = builder.type_builder.add_field_type::<OnceCell<ErasedItemTreeBoxWeak>>();
1316
1317 let window_adapter_offset = builder.type_builder.add_field_type::<OnceCell<WindowAdapterRc>>();
1318
1319 let extra_data_offset = builder.type_builder.add_field_type::<ComponentExtraData>();
1320
1321 let change_trackers = (!builder.change_callbacks.is_empty()).then(|| {
1322 (
1323 builder.type_builder.add_field_type::<OnceCell<Vec<ChangeTracker>>>(),
1324 builder.change_callbacks,
1325 )
1326 });
1327 let timers = component
1328 .timers
1329 .borrow()
1330 .iter()
1331 .map(|_| builder.type_builder.add_field_type::<Timer>())
1332 .collect();
1333
1334 let public_properties = if component.parent_element.upgrade().is_none() {
1336 component.root_element.borrow().property_declarations.clone()
1337 } else {
1338 Default::default()
1339 };
1340
1341 let t = ItemTreeVTable {
1342 visit_children_item,
1343 layout_info,
1344 get_item_ref,
1345 get_item_tree,
1346 get_subtree_range,
1347 get_subtree,
1348 parent_node,
1349 embed_component,
1350 subtree_index,
1351 item_geometry,
1352 accessible_role,
1353 accessible_string_property,
1354 accessibility_action,
1355 supported_accessibility_actions,
1356 item_element_infos,
1357 window_adapter,
1358 drop_in_place,
1359 dealloc,
1360 };
1361 let t = ItemTreeDescription {
1362 ct: t,
1363 dynamic_type: builder.type_builder.build(),
1364 item_tree: builder.tree_array,
1365 item_array: builder.item_array,
1366 items: builder.items_types,
1367 custom_properties,
1368 custom_callbacks,
1369 original: component.clone(),
1370 original_elements: builder.original_elements,
1371 repeater: builder.repeater,
1372 repeater_names: builder.repeater_names,
1373 parent_item_tree_offset,
1374 root_offset,
1375 window_adapter_offset,
1376 extra_data_offset,
1377 public_properties,
1378 compiled_globals,
1379 change_trackers,
1380 timers,
1381 popup_ids: std::cell::RefCell::new(HashMap::new()),
1382 popup_menu_description: builder.popup_menu_description,
1383 #[cfg(feature = "internal-highlight")]
1384 type_loader: std::cell::OnceCell::new(),
1385 #[cfg(feature = "internal-highlight")]
1386 raw_type_loader: std::cell::OnceCell::new(),
1387 debug_handler: std::cell::RefCell::new(Rc::new(|_, text| {
1388 i_slint_core::debug_log!("{text}")
1389 })),
1390 };
1391
1392 Rc::new(t)
1393}
1394
1395pub fn animation_for_property(
1396 component: InstanceRef,
1397 animation: &Option<i_slint_compiler::object_tree::PropertyAnimation>,
1398) -> AnimatedBindingKind {
1399 match animation {
1400 Some(i_slint_compiler::object_tree::PropertyAnimation::Static(anim_elem)) => {
1401 AnimatedBindingKind::Animation(eval::new_struct_with_bindings(
1402 &anim_elem.borrow().bindings,
1403 &mut eval::EvalLocalContext::from_component_instance(component),
1404 ))
1405 }
1406 Some(i_slint_compiler::object_tree::PropertyAnimation::Transition {
1407 animations,
1408 state_ref,
1409 }) => {
1410 let component_ptr = component.as_ptr();
1411 let vtable = NonNull::from(&component.description.ct).cast();
1412 let animations = animations.clone();
1413 let state_ref = state_ref.clone();
1414 AnimatedBindingKind::Transition(Box::new(
1415 move || -> (PropertyAnimation, i_slint_core::animations::Instant) {
1416 generativity::make_guard!(guard);
1417 let component = unsafe {
1418 InstanceRef::from_pin_ref(
1419 Pin::new_unchecked(vtable::VRef::from_raw(
1420 vtable,
1421 NonNull::new_unchecked(component_ptr as *mut u8),
1422 )),
1423 guard,
1424 )
1425 };
1426
1427 let mut context = eval::EvalLocalContext::from_component_instance(component);
1428 let state = eval::eval_expression(&state_ref, &mut context);
1429 let state_info: i_slint_core::properties::StateInfo = state.try_into().unwrap();
1430 for a in &animations {
1431 let is_previous_state = a.state_id == state_info.previous_state;
1432 let is_current_state = a.state_id == state_info.current_state;
1433 match (a.direction, is_previous_state, is_current_state) {
1434 (TransitionDirection::In, false, true)
1435 | (TransitionDirection::Out, true, false)
1436 | (TransitionDirection::InOut, false, true)
1437 | (TransitionDirection::InOut, true, false) => {
1438 return (
1439 eval::new_struct_with_bindings(
1440 &a.animation.borrow().bindings,
1441 &mut context,
1442 ),
1443 state_info.change_time,
1444 );
1445 }
1446 _ => {}
1447 }
1448 }
1449 Default::default()
1450 },
1451 ))
1452 }
1453 None => AnimatedBindingKind::NotAnimated,
1454 }
1455}
1456
1457fn make_callback_eval_closure(
1458 expr: Expression,
1459 self_weak: &ErasedItemTreeBoxWeak,
1460) -> impl Fn(&[Value]) -> Value {
1461 let self_weak = self_weak.clone();
1462 move |args| {
1463 let self_rc = self_weak.upgrade().unwrap();
1464 generativity::make_guard!(guard);
1465 let self_ = self_rc.unerase(guard);
1466 let instance_ref = self_.borrow_instance();
1467 let mut local_context =
1468 eval::EvalLocalContext::from_function_arguments(instance_ref, args.to_vec());
1469 eval::eval_expression(&expr, &mut local_context)
1470 }
1471}
1472
1473fn make_binding_eval_closure(
1474 expr: Expression,
1475 self_weak: &ErasedItemTreeBoxWeak,
1476) -> impl Fn() -> Value {
1477 let self_weak = self_weak.clone();
1478 move || {
1479 let self_rc = self_weak.upgrade().unwrap();
1480 generativity::make_guard!(guard);
1481 let self_ = self_rc.unerase(guard);
1482 let instance_ref = self_.borrow_instance();
1483 eval::eval_expression(
1484 &expr,
1485 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1486 )
1487 }
1488}
1489
1490pub fn instantiate(
1491 description: Rc<ItemTreeDescription>,
1492 parent_ctx: Option<ErasedItemTreeBoxWeak>,
1493 root: Option<ErasedItemTreeBoxWeak>,
1494 window_options: Option<&WindowOptions>,
1495 mut globals: crate::global_component::GlobalStorage,
1496) -> DynamicComponentVRc {
1497 let instance = description.dynamic_type.clone().create_instance();
1498
1499 let component_box = ItemTreeBox { instance, description: description.clone() };
1500
1501 let self_rc = vtable::VRc::new(ErasedItemTreeBox::from(component_box));
1502 let self_weak = vtable::VRc::downgrade(&self_rc);
1503
1504 generativity::make_guard!(guard);
1505 let comp = self_rc.unerase(guard);
1506 let instance_ref = comp.borrow_instance();
1507 instance_ref.self_weak().set(self_weak.clone()).ok();
1508 let description = comp.description();
1509
1510 if let Some(parent) = parent_ctx {
1511 description
1512 .parent_item_tree_offset
1513 .unwrap()
1514 .apply(instance_ref.as_ref())
1515 .set(parent)
1516 .ok()
1517 .unwrap();
1518 } else {
1519 if let Some(g) = description.compiled_globals.as_ref() {
1520 for g in g.compiled_globals.iter() {
1521 crate::global_component::instantiate(g, &mut globals, self_weak.clone());
1522 }
1523 }
1524 let extra_data = description.extra_data_offset.apply(instance_ref.as_ref());
1525 extra_data.globals.set(globals).ok().unwrap();
1526 }
1527
1528 if let Some(WindowOptions::Embed { parent_item_tree, parent_item_tree_index }) = window_options
1529 {
1530 vtable::VRc::borrow_pin(&self_rc)
1531 .as_ref()
1532 .embed_component(parent_item_tree, *parent_item_tree_index);
1533 description.root_offset.apply(instance_ref.as_ref()).set(self_weak.clone()).ok().unwrap();
1534 } else {
1535 generativity::make_guard!(guard);
1536 let root = root
1537 .or_else(|| {
1538 instance_ref.parent_instance(guard).map(|parent| parent.root_weak().clone())
1539 })
1540 .unwrap_or_else(|| self_weak.clone());
1541 description.root_offset.apply(instance_ref.as_ref()).set(root).ok().unwrap();
1542 }
1543
1544 if !description.original.is_global() {
1545 let maybe_window_adapter =
1546 if let Some(WindowOptions::UseExistingWindow(adapter)) = window_options.as_ref() {
1547 Some(adapter.clone())
1548 } else {
1549 instance_ref.maybe_window_adapter()
1550 };
1551
1552 let component_rc = vtable::VRc::into_dyn(self_rc.clone());
1553 i_slint_core::item_tree::register_item_tree(&component_rc, maybe_window_adapter);
1554 }
1555
1556 if let Some(WindowOptions::UseExistingWindow(existing_adapter)) = &window_options {
1557 description
1558 .window_adapter_offset
1559 .apply(instance_ref.as_ref())
1560 .set(existing_adapter.clone())
1561 .ok()
1562 .unwrap();
1563 }
1564
1565 for (prop_name, decl) in &description.original.root_element.borrow().property_declarations {
1567 if !matches!(
1568 decl.property_type,
1569 Type::Struct { .. } | Type::Array(_) | Type::Enumeration(_)
1570 ) || decl.is_alias.is_some()
1571 {
1572 continue;
1573 }
1574 if let Some(b) = description.original.root_element.borrow().bindings.get(prop_name) {
1575 if b.borrow().two_way_bindings.is_empty() {
1576 continue;
1577 }
1578 }
1579 let p = description.custom_properties.get(prop_name).unwrap();
1580 unsafe {
1581 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(p.offset));
1582 p.prop.set(item, eval::default_value_for_type(&decl.property_type), None).unwrap();
1583 }
1584 }
1585
1586 generator::handle_property_bindings_init(
1587 &description.original,
1588 |elem, prop_name, binding| unsafe {
1589 let is_root = Rc::ptr_eq(
1590 elem,
1591 &elem.borrow().enclosing_component.upgrade().unwrap().root_element,
1592 );
1593 let elem = elem.borrow();
1594 let is_const = binding.analysis.as_ref().is_some_and(|a| a.is_const);
1595
1596 let property_type = elem.lookup_property(prop_name).property_type;
1597 if let Type::Function { .. } = property_type {
1598 } else if let Type::Callback { .. } = property_type {
1600 if !matches!(binding.expression, Expression::Invalid) {
1601 let expr = binding.expression.clone();
1602 let description = description.clone();
1603 if let Some(callback_offset) =
1604 description.custom_callbacks.get(prop_name).filter(|_| is_root)
1605 {
1606 let callback = callback_offset.apply(instance_ref.as_ref());
1607 callback.set_handler(make_callback_eval_closure(expr, &self_weak));
1608 } else {
1609 let item_within_component = &description.items[&elem.id];
1610 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1611 if let Some(callback) =
1612 item_within_component.rtti.callbacks.get(prop_name.as_str())
1613 {
1614 callback.set_handler(
1615 item,
1616 Box::new(make_callback_eval_closure(expr, &self_weak)),
1617 );
1618 } else {
1619 panic!("unknown callback {prop_name}")
1620 }
1621 }
1622 }
1623 } else if let Some(PropertiesWithinComponent { offset, prop: prop_info, .. }) =
1624 description.custom_properties.get(prop_name).filter(|_| is_root)
1625 {
1626 let is_state_info = matches!(property_type, Type::Struct (s) if s.name.as_ref().is_some_and(|name| name.ends_with("::StateInfo")));
1627 if is_state_info {
1628 let prop = Pin::new_unchecked(
1629 &*(instance_ref.as_ptr().add(*offset)
1630 as *const Property<i_slint_core::properties::StateInfo>),
1631 );
1632 let e = binding.expression.clone();
1633 let state_binding = make_binding_eval_closure(e, &self_weak);
1634 i_slint_core::properties::set_state_binding(prop, move || {
1635 state_binding().try_into().unwrap()
1636 });
1637 return;
1638 }
1639
1640 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1641 let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(*offset));
1642
1643 if !matches!(binding.expression, Expression::Invalid) {
1644 if is_const {
1645 let v = eval::eval_expression(
1646 &binding.expression,
1647 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1648 );
1649 prop_info.set(item, v, None).unwrap();
1650 } else {
1651 let e = binding.expression.clone();
1652 prop_info
1653 .set_binding(
1654 item,
1655 Box::new(make_binding_eval_closure(e, &self_weak)),
1656 maybe_animation,
1657 )
1658 .unwrap();
1659 }
1660 }
1661 for nr in &binding.two_way_bindings {
1662 prop_info.link_two_ways(item, get_property_ptr(nr, instance_ref));
1664 }
1665 } else {
1666 let item_within_component = &description.items[&elem.id];
1667 let item = item_within_component.item_from_item_tree(instance_ref.as_ptr());
1668 if let Some(prop_rtti) =
1669 item_within_component.rtti.properties.get(prop_name.as_str())
1670 {
1671 let maybe_animation = animation_for_property(instance_ref, &binding.animation);
1672 for nr in &binding.two_way_bindings {
1673 prop_rtti.link_two_ways(item, get_property_ptr(nr, instance_ref));
1675 }
1676 if !matches!(binding.expression, Expression::Invalid) {
1677 if is_const {
1678 prop_rtti
1679 .set(
1680 item,
1681 eval::eval_expression(
1682 &binding.expression,
1683 &mut eval::EvalLocalContext::from_component_instance(
1684 instance_ref,
1685 ),
1686 ),
1687 maybe_animation.as_animation(),
1688 )
1689 .unwrap();
1690 } else {
1691 let e = binding.expression.clone();
1692 prop_rtti.set_binding(
1693 item,
1694 Box::new(make_binding_eval_closure(e, &self_weak)),
1695 maybe_animation,
1696 );
1697 }
1698 }
1699 } else {
1700 panic!("unknown property {} in {}", prop_name, elem.id);
1701 }
1702 }
1703 },
1704 );
1705
1706 for rep_in_comp in &description.repeater {
1707 generativity::make_guard!(guard);
1708 let rep_in_comp = rep_in_comp.unerase(guard);
1709
1710 let repeater = rep_in_comp.offset.apply_pin(instance_ref.instance);
1711 let expr = rep_in_comp.model.clone();
1712 let model_binding_closure = make_binding_eval_closure(expr, &self_weak);
1713 if rep_in_comp.is_conditional {
1714 let bool_model = Rc::new(crate::value_model::BoolModel::default());
1715 repeater.set_model_binding(move || {
1716 let v = model_binding_closure();
1717 bool_model.set_value(v.try_into().expect("condition model is bool"));
1718 ModelRc::from(bool_model.clone())
1719 });
1720 } else {
1721 repeater.set_model_binding(move || {
1722 let m = model_binding_closure();
1723 if let Value::Model(m) = m {
1724 m.clone()
1725 } else {
1726 ModelRc::new(crate::value_model::ValueModel::new(m))
1727 }
1728 });
1729 }
1730 }
1731 self_rc
1732}
1733
1734pub(crate) fn get_property_ptr(nr: &NamedReference, instance: InstanceRef) -> *const () {
1735 let element = nr.element();
1736 generativity::make_guard!(guard);
1737 let enclosing_component = eval::enclosing_component_instance_for_element(
1738 &element,
1739 &eval::ComponentInstance::InstanceRef(instance),
1740 guard,
1741 );
1742 match enclosing_component {
1743 eval::ComponentInstance::InstanceRef(enclosing_component) => {
1744 let element = element.borrow();
1745 if element.id == element.enclosing_component.upgrade().unwrap().root_element.borrow().id
1746 {
1747 if let Some(x) = enclosing_component.description.custom_properties.get(nr.name()) {
1748 return unsafe { enclosing_component.as_ptr().add(x.offset).cast() };
1749 };
1750 };
1751 let item_info = enclosing_component
1752 .description
1753 .items
1754 .get(element.id.as_str())
1755 .unwrap_or_else(|| panic!("Unknown element for {}.{}", element.id, nr.name()));
1756 let prop_info = item_info
1757 .rtti
1758 .properties
1759 .get(nr.name().as_str())
1760 .unwrap_or_else(|| panic!("Property {} not in {}", nr.name(), element.id));
1761 core::mem::drop(element);
1762 let item = unsafe { item_info.item_from_item_tree(enclosing_component.as_ptr()) };
1763 unsafe { item.as_ptr().add(prop_info.offset()).cast() }
1764 }
1765 eval::ComponentInstance::GlobalComponent(glob) => glob.as_ref().get_property_ptr(nr.name()),
1766 }
1767}
1768
1769pub struct ErasedItemTreeBox(ItemTreeBox<'static>);
1770impl ErasedItemTreeBox {
1771 pub fn unerase<'a, 'id>(
1772 &'a self,
1773 _guard: generativity::Guard<'id>,
1774 ) -> Pin<&'a ItemTreeBox<'id>> {
1775 Pin::new(
1776 unsafe { core::mem::transmute::<&ItemTreeBox<'static>, &ItemTreeBox<'id>>(&self.0) },
1778 )
1779 }
1780
1781 pub fn borrow(&self) -> ItemTreeRefPin<'_> {
1782 self.0.borrow()
1784 }
1785
1786 pub fn window_adapter_ref(&self) -> Result<&WindowAdapterRc, PlatformError> {
1787 self.0.window_adapter_ref()
1788 }
1789
1790 pub fn run_setup_code(&self) {
1791 generativity::make_guard!(guard);
1792 let compo_box = self.unerase(guard);
1793 let instance_ref = compo_box.borrow_instance();
1794 for extra_init_code in self.0.description.original.init_code.borrow().iter() {
1795 eval::eval_expression(
1796 extra_init_code,
1797 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1798 );
1799 }
1800 if let Some(cts) = instance_ref.description.change_trackers.as_ref() {
1801 let self_weak = instance_ref.self_weak().get().unwrap();
1802 let v = cts
1803 .1
1804 .iter()
1805 .enumerate()
1806 .map(|(idx, _)| {
1807 let ct = ChangeTracker::default();
1808 ct.init(
1809 self_weak.clone(),
1810 move |self_weak| {
1811 let s = self_weak.upgrade().unwrap();
1812 generativity::make_guard!(guard);
1813 let compo_box = s.unerase(guard);
1814 let instance_ref = compo_box.borrow_instance();
1815 let nr = &s.0.description.change_trackers.as_ref().unwrap().1[idx].0;
1816 eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap()
1817 },
1818 move |self_weak, _| {
1819 let s = self_weak.upgrade().unwrap();
1820 generativity::make_guard!(guard);
1821 let compo_box = s.unerase(guard);
1822 let instance_ref = compo_box.borrow_instance();
1823 let e = &s.0.description.change_trackers.as_ref().unwrap().1[idx].1;
1824 eval::eval_expression(
1825 e,
1826 &mut eval::EvalLocalContext::from_component_instance(instance_ref),
1827 );
1828 },
1829 );
1830 ct
1831 })
1832 .collect::<Vec<_>>();
1833 cts.0
1834 .apply_pin(instance_ref.instance)
1835 .set(v)
1836 .unwrap_or_else(|_| panic!("run_setup_code called twice?"));
1837 }
1838 update_timers(instance_ref);
1839 }
1840}
1841impl<'id> From<ItemTreeBox<'id>> for ErasedItemTreeBox {
1842 fn from(inner: ItemTreeBox<'id>) -> Self {
1843 unsafe {
1846 ErasedItemTreeBox(core::mem::transmute::<ItemTreeBox<'id>, ItemTreeBox<'static>>(inner))
1847 }
1848 }
1849}
1850
1851pub fn get_repeater_by_name<'a, 'id>(
1852 instance_ref: InstanceRef<'a, '_>,
1853 name: &str,
1854 guard: generativity::Guard<'id>,
1855) -> (std::pin::Pin<&'a Repeater<ErasedItemTreeBox>>, Rc<ItemTreeDescription<'id>>) {
1856 let rep_index = instance_ref.description.repeater_names[name];
1857 let rep_in_comp = instance_ref.description.repeater[rep_index].unerase(guard);
1858 (rep_in_comp.offset.apply_pin(instance_ref.instance), rep_in_comp.item_tree_to_repeat.clone())
1859}
1860
1861#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
1862extern "C" fn layout_info(component: ItemTreeRefPin, orientation: Orientation) -> LayoutInfo {
1863 generativity::make_guard!(guard);
1864 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
1866 let orientation = crate::eval_layout::from_runtime(orientation);
1867
1868 let mut result = crate::eval_layout::get_layout_info(
1869 &instance_ref.description.original.root_element,
1870 instance_ref,
1871 &instance_ref.window_adapter(),
1872 orientation,
1873 );
1874
1875 let constraints = instance_ref.description.original.root_constraints.borrow();
1876 if constraints.has_explicit_restrictions(orientation) {
1877 crate::eval_layout::fill_layout_info_constraints(
1878 &mut result,
1879 &constraints,
1880 orientation,
1881 &|nr: &NamedReference| {
1882 eval::load_property(instance_ref, &nr.element(), nr.name())
1883 .unwrap()
1884 .try_into()
1885 .unwrap()
1886 },
1887 );
1888 }
1889 result
1890}
1891
1892#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
1893unsafe extern "C" fn get_item_ref(component: ItemTreeRefPin, index: u32) -> Pin<ItemRef> {
1894 let tree = get_item_tree(component);
1895 match &tree[index as usize] {
1896 ItemTreeNode::Item { item_array_index, .. } => {
1897 generativity::make_guard!(guard);
1898 let instance_ref = InstanceRef::from_pin_ref(component, guard);
1899 core::mem::transmute::<Pin<ItemRef>, Pin<ItemRef>>(
1900 instance_ref.description.item_array[*item_array_index as usize]
1901 .apply_pin(instance_ref.instance),
1902 )
1903 }
1904 ItemTreeNode::DynamicTree { .. } => panic!("get_item_ref called on dynamic tree"),
1905 }
1906}
1907
1908#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
1909extern "C" fn get_subtree_range(component: ItemTreeRefPin, index: u32) -> IndexRange {
1910 generativity::make_guard!(guard);
1911 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
1912 if index as usize >= instance_ref.description.repeater.len() {
1913 let container_index = {
1914 let tree_node = &component.as_ref().get_item_tree()[index as usize];
1915 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
1916 *parent_index
1917 } else {
1918 u32::MAX
1919 }
1920 };
1921 let container = component.as_ref().get_item_ref(container_index);
1922 let container = i_slint_core::items::ItemRef::downcast_pin::<
1923 i_slint_core::items::ComponentContainer,
1924 >(container)
1925 .unwrap();
1926 container.ensure_updated();
1927 container.subtree_range()
1928 } else {
1929 let rep_in_comp =
1930 unsafe { instance_ref.description.repeater[index as usize].get_untagged() };
1931 ensure_repeater_updated(instance_ref, rep_in_comp);
1932
1933 let repeater = rep_in_comp.offset.apply(&instance_ref.instance);
1934 repeater.range().into()
1935 }
1936}
1937
1938#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
1939extern "C" fn get_subtree(
1940 component: ItemTreeRefPin,
1941 index: u32,
1942 subtree_index: usize,
1943 result: &mut ItemTreeWeak,
1944) {
1945 generativity::make_guard!(guard);
1946 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
1947 if index as usize >= instance_ref.description.repeater.len() {
1948 let container_index = {
1949 let tree_node = &component.as_ref().get_item_tree()[index as usize];
1950 if let ItemTreeNode::DynamicTree { parent_index, .. } = tree_node {
1951 *parent_index
1952 } else {
1953 u32::MAX
1954 }
1955 };
1956 let container = component.as_ref().get_item_ref(container_index);
1957 let container = i_slint_core::items::ItemRef::downcast_pin::<
1958 i_slint_core::items::ComponentContainer,
1959 >(container)
1960 .unwrap();
1961 container.ensure_updated();
1962 if subtree_index == 0 {
1963 *result = container.subtree_component();
1964 }
1965 } else {
1966 let rep_in_comp =
1967 unsafe { instance_ref.description.repeater[index as usize].get_untagged() };
1968 ensure_repeater_updated(instance_ref, rep_in_comp);
1969
1970 let repeater = rep_in_comp.offset.apply(&instance_ref.instance);
1971 if let Some(instance_at) = repeater.instance_at(subtree_index) {
1972 *result = vtable::VRc::downgrade(&vtable::VRc::into_dyn(instance_at))
1973 }
1974 }
1975}
1976
1977#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
1978extern "C" fn get_item_tree(component: ItemTreeRefPin) -> Slice<ItemTreeNode> {
1979 generativity::make_guard!(guard);
1980 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
1981 let tree = instance_ref.description.item_tree.as_slice();
1982 unsafe { core::mem::transmute::<&[ItemTreeNode], &[ItemTreeNode]>(tree) }.into()
1983}
1984
1985#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
1986extern "C" fn subtree_index(component: ItemTreeRefPin) -> usize {
1987 generativity::make_guard!(guard);
1988 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
1989 if let Ok(value) = instance_ref.description.get_property(component, SPECIAL_PROPERTY_INDEX) {
1990 value.try_into().unwrap()
1991 } else {
1992 usize::MAX
1993 }
1994}
1995
1996#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
1997unsafe extern "C" fn parent_node(component: ItemTreeRefPin, result: &mut ItemWeak) {
1998 generativity::make_guard!(guard);
1999 let instance_ref = InstanceRef::from_pin_ref(component, guard);
2000
2001 let component_and_index = {
2002 if let Some(parent_offset) = instance_ref.description.parent_item_tree_offset {
2004 let parent_item_index = instance_ref
2005 .description
2006 .original
2007 .parent_element
2008 .upgrade()
2009 .and_then(|e| e.borrow().item_index.get().cloned())
2010 .unwrap_or(u32::MAX);
2011 let parent_component = parent_offset
2012 .apply(instance_ref.as_ref())
2013 .get()
2014 .and_then(|p| p.upgrade())
2015 .map(vtable::VRc::into_dyn);
2016
2017 (parent_component, parent_item_index)
2018 } else if let Some((parent_component, parent_index)) = instance_ref
2019 .description
2020 .extra_data_offset
2021 .apply(instance_ref.as_ref())
2022 .embedding_position
2023 .get()
2024 {
2025 (parent_component.upgrade(), *parent_index)
2026 } else {
2027 (None, u32::MAX)
2028 }
2029 };
2030
2031 if let (Some(component), index) = component_and_index {
2032 *result = ItemRc::new(component, index).downgrade();
2033 }
2034}
2035
2036#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2037unsafe extern "C" fn embed_component(
2038 component: ItemTreeRefPin,
2039 parent_component: &ItemTreeWeak,
2040 parent_item_tree_index: u32,
2041) -> bool {
2042 generativity::make_guard!(guard);
2043 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2044
2045 if instance_ref.description.parent_item_tree_offset.is_some() {
2046 return false;
2048 }
2049
2050 {
2051 let prc = parent_component.upgrade().unwrap();
2053 let pref = vtable::VRc::borrow_pin(&prc);
2054 let it = pref.as_ref().get_item_tree();
2055 if !matches!(
2056 it.get(parent_item_tree_index as usize),
2057 Some(ItemTreeNode::DynamicTree { .. })
2058 ) {
2059 panic!("Trying to embed into a non-dynamic index in the parents item tree")
2060 }
2061 }
2062
2063 let extra_data = instance_ref.description.extra_data_offset.apply(instance_ref.as_ref());
2064 extra_data.embedding_position.set((parent_component.clone(), parent_item_tree_index)).is_ok()
2065}
2066
2067#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2068extern "C" fn item_geometry(component: ItemTreeRefPin, item_index: u32) -> LogicalRect {
2069 generativity::make_guard!(guard);
2070 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2071
2072 let e = instance_ref.description.original_elements[item_index as usize].borrow();
2073 let g = e.geometry_props.as_ref().unwrap();
2074
2075 let load_f32 = |nr: &NamedReference| -> f32 {
2076 crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2077 .unwrap()
2078 .try_into()
2079 .unwrap()
2080 };
2081
2082 LogicalRect {
2083 origin: (load_f32(&g.x), load_f32(&g.y)).into(),
2084 size: (load_f32(&g.width), load_f32(&g.height)).into(),
2085 }
2086}
2087
2088#[allow(improper_ctypes_definitions)]
2090#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2091extern "C" fn accessible_role(component: ItemTreeRefPin, item_index: u32) -> AccessibleRole {
2092 generativity::make_guard!(guard);
2093 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2094 let nr = instance_ref.description.original_elements[item_index as usize]
2095 .borrow()
2096 .accessibility_props
2097 .0
2098 .get("accessible-role")
2099 .cloned();
2100 match nr {
2101 Some(nr) => crate::eval::load_property(instance_ref, &nr.element(), nr.name())
2102 .unwrap()
2103 .try_into()
2104 .unwrap(),
2105 None => AccessibleRole::default(),
2106 }
2107}
2108
2109#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2110extern "C" fn accessible_string_property(
2111 component: ItemTreeRefPin,
2112 item_index: u32,
2113 what: AccessibleStringProperty,
2114 result: &mut SharedString,
2115) -> bool {
2116 generativity::make_guard!(guard);
2117 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2118 let prop_name = format!("accessible-{what}");
2119 let nr = instance_ref.description.original_elements[item_index as usize]
2120 .borrow()
2121 .accessibility_props
2122 .0
2123 .get(&prop_name)
2124 .cloned();
2125 if let Some(nr) = nr {
2126 let value = crate::eval::load_property(instance_ref, &nr.element(), nr.name()).unwrap();
2127 match value {
2128 Value::String(s) => *result = s,
2129 Value::Bool(b) => *result = if b { "true" } else { "false" }.into(),
2130 Value::Number(x) => *result = x.to_string().into(),
2131 _ => unimplemented!("invalid type for accessible_string_property"),
2132 };
2133 true
2134 } else {
2135 false
2136 }
2137}
2138
2139#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2140extern "C" fn accessibility_action(
2141 component: ItemTreeRefPin,
2142 item_index: u32,
2143 action: &AccessibilityAction,
2144) {
2145 let perform = |prop_name, args: &[Value]| {
2146 generativity::make_guard!(guard);
2147 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2148 let nr = instance_ref.description.original_elements[item_index as usize]
2149 .borrow()
2150 .accessibility_props
2151 .0
2152 .get(prop_name)
2153 .cloned();
2154 if let Some(nr) = nr {
2155 let instance_ref = eval::ComponentInstance::InstanceRef(instance_ref);
2156 crate::eval::invoke_callback(&instance_ref, &nr.element(), nr.name(), args).unwrap();
2157 }
2158 };
2159
2160 match action {
2161 AccessibilityAction::Default => perform("accessible-action-default", &[]),
2162 AccessibilityAction::Decrement => perform("accessible-action-decrement", &[]),
2163 AccessibilityAction::Increment => perform("accessible-action-increment", &[]),
2164 AccessibilityAction::Expand => perform("accessible-action-expand", &[]),
2165 AccessibilityAction::ReplaceSelectedText(_a) => {
2166 i_slint_core::debug_log!("AccessibilityAction::ReplaceSelectedText not implemented in interpreter's accessibility_action");
2168 }
2169 AccessibilityAction::SetValue(a) => {
2170 perform("accessible-action-set-value", &[Value::String(a.clone())])
2171 }
2172 };
2173}
2174
2175#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2176extern "C" fn supported_accessibility_actions(
2177 component: ItemTreeRefPin,
2178 item_index: u32,
2179) -> SupportedAccessibilityAction {
2180 generativity::make_guard!(guard);
2181 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2182 let val = instance_ref.description.original_elements[item_index as usize]
2183 .borrow()
2184 .accessibility_props
2185 .0
2186 .keys()
2187 .filter_map(|x| x.strip_prefix("accessible-action-"))
2188 .fold(SupportedAccessibilityAction::default(), |acc, value| {
2189 SupportedAccessibilityAction::from_name(&i_slint_compiler::generator::to_pascal_case(
2190 value,
2191 ))
2192 .unwrap_or_else(|| panic!("Not an accessible action: {value:?}"))
2193 | acc
2194 });
2195 val
2196}
2197
2198#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2199extern "C" fn item_element_infos(
2200 component: ItemTreeRefPin,
2201 item_index: u32,
2202 result: &mut SharedString,
2203) -> bool {
2204 generativity::make_guard!(guard);
2205 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2206 *result = instance_ref.description.original_elements[item_index as usize]
2207 .borrow()
2208 .element_infos()
2209 .into();
2210 true
2211}
2212
2213#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2214extern "C" fn window_adapter(
2215 component: ItemTreeRefPin,
2216 do_create: bool,
2217 result: &mut Option<WindowAdapterRc>,
2218) {
2219 generativity::make_guard!(guard);
2220 let instance_ref = unsafe { InstanceRef::from_pin_ref(component, guard) };
2221 if do_create {
2222 *result = Some(instance_ref.window_adapter());
2223 } else {
2224 *result = instance_ref.maybe_window_adapter();
2225 }
2226}
2227
2228#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2229unsafe extern "C" fn drop_in_place(component: vtable::VRefMut<ItemTreeVTable>) -> vtable::Layout {
2230 let instance_ptr = component.as_ptr() as *mut Instance<'static>;
2231 let layout = (*instance_ptr).type_info().layout();
2232 dynamic_type::TypeInfo::drop_in_place(instance_ptr);
2233 layout.into()
2234}
2235
2236#[cfg_attr(not(feature = "ffi"), i_slint_core_macros::remove_extern)]
2237unsafe extern "C" fn dealloc(_vtable: &ItemTreeVTable, ptr: *mut u8, layout: vtable::Layout) {
2238 std::alloc::dealloc(ptr, layout.try_into().unwrap());
2239}
2240
2241#[derive(Copy, Clone)]
2242pub struct InstanceRef<'a, 'id> {
2243 pub instance: Pin<&'a Instance<'id>>,
2244 pub description: &'a ItemTreeDescription<'id>,
2245}
2246
2247impl<'a, 'id> InstanceRef<'a, 'id> {
2248 pub unsafe fn from_pin_ref(
2249 component: ItemTreeRefPin<'a>,
2250 _guard: generativity::Guard<'id>,
2251 ) -> Self {
2252 Self {
2253 instance: Pin::new_unchecked(&*(component.as_ref().as_ptr() as *const Instance<'id>)),
2254 description: &*(Pin::into_inner_unchecked(component).get_vtable()
2255 as *const ItemTreeVTable
2256 as *const ItemTreeDescription<'id>),
2257 }
2258 }
2259
2260 pub fn as_ptr(&self) -> *const u8 {
2261 (&*self.instance.as_ref()) as *const Instance as *const u8
2262 }
2263
2264 pub fn as_ref(&self) -> &Instance<'id> {
2265 &self.instance
2266 }
2267
2268 pub fn borrow(self) -> ItemTreeRefPin<'a> {
2270 unsafe {
2271 Pin::new_unchecked(vtable::VRef::from_raw(
2272 NonNull::from(&self.description.ct).cast(),
2273 NonNull::from(self.instance.get_ref()).cast(),
2274 ))
2275 }
2276 }
2277
2278 pub fn self_weak(&self) -> &OnceCell<ErasedItemTreeBoxWeak> {
2279 let extra_data = self.description.extra_data_offset.apply(self.as_ref());
2280 &extra_data.self_weak
2281 }
2282
2283 pub fn root_weak(&self) -> &ErasedItemTreeBoxWeak {
2284 self.description.root_offset.apply(self.as_ref()).get().unwrap()
2285 }
2286
2287 pub fn window_adapter(&self) -> WindowAdapterRc {
2288 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2289 let root = self.root_weak().upgrade().unwrap();
2290 generativity::make_guard!(guard);
2291 let comp = root.unerase(guard);
2292 Self::get_or_init_window_adapter_ref(
2293 &comp.description,
2294 root_weak,
2295 true,
2296 comp.instance.as_pin_ref().get_ref(),
2297 )
2298 .unwrap()
2299 .clone()
2300 }
2301
2302 pub fn get_or_init_window_adapter_ref<'b, 'id2>(
2303 description: &'b ItemTreeDescription<'id2>,
2304 root_weak: ItemTreeWeak,
2305 do_create: bool,
2306 instance: &'b Instance<'id2>,
2307 ) -> Result<&'b WindowAdapterRc, PlatformError> {
2308 description.window_adapter_offset.apply(instance).get_or_try_init(|| {
2310 let mut parent_node = ItemWeak::default();
2311 if let Some(rc) = vtable::VWeak::upgrade(&root_weak) {
2312 vtable::VRc::borrow_pin(&rc).as_ref().parent_node(&mut parent_node);
2313 }
2314
2315 if let Some(parent) = parent_node.upgrade() {
2316 let mut result = None;
2318 vtable::VRc::borrow_pin(parent.item_tree())
2319 .as_ref()
2320 .window_adapter(do_create, &mut result);
2321 result.ok_or(PlatformError::NoPlatform)
2322 } else if do_create {
2323 let extra_data = description.extra_data_offset.apply(instance);
2324 let window_adapter = i_slint_backend_selector::with_platform(|_b| {
2326 return _b.create_window_adapter();
2327 })?;
2328
2329 let comp_rc = extra_data.self_weak.get().unwrap().upgrade().unwrap();
2330 WindowInner::from_pub(window_adapter.window())
2331 .set_component(&vtable::VRc::into_dyn(comp_rc));
2332 Ok(window_adapter)
2333 } else {
2334 Err(PlatformError::NoPlatform)
2335 }
2336 })
2337 }
2338
2339 pub fn maybe_window_adapter(&self) -> Option<WindowAdapterRc> {
2340 let root_weak = vtable::VWeak::into_dyn(self.root_weak().clone());
2341 let root = self.root_weak().upgrade()?;
2342 generativity::make_guard!(guard);
2343 let comp = root.unerase(guard);
2344 Self::get_or_init_window_adapter_ref(
2345 &comp.description,
2346 root_weak,
2347 false,
2348 comp.instance.as_pin_ref().get_ref(),
2349 )
2350 .ok()
2351 .cloned()
2352 }
2353
2354 pub fn access_window<R>(
2355 self,
2356 callback: impl FnOnce(&'_ i_slint_core::window::WindowInner) -> R,
2357 ) -> R {
2358 callback(WindowInner::from_pub(self.window_adapter().window()))
2359 }
2360
2361 pub fn parent_instance<'id2>(
2362 &self,
2363 _guard: generativity::Guard<'id2>,
2364 ) -> Option<InstanceRef<'a, 'id2>> {
2365 if let Some(parent_offset) = self.description.parent_item_tree_offset {
2368 if let Some(parent) =
2369 parent_offset.apply(self.as_ref()).get().and_then(vtable::VWeak::upgrade)
2370 {
2371 let parent_instance = parent.unerase(_guard);
2372 let parent_instance = unsafe {
2374 std::mem::transmute::<InstanceRef<'_, 'id2>, InstanceRef<'a, 'id2>>(
2375 parent_instance.borrow_instance(),
2376 )
2377 };
2378 return Some(parent_instance);
2379 };
2380 }
2381 None
2382 }
2383
2384 pub fn toplevel_instance<'id2>(
2385 &self,
2386 _guard: generativity::Guard<'id2>,
2387 ) -> InstanceRef<'a, 'id2> {
2388 generativity::make_guard!(guard2);
2389 if let Some(parent) = self.parent_instance(guard2) {
2390 let tl = parent.toplevel_instance(_guard);
2391 unsafe { std::mem::transmute::<InstanceRef<'_, 'id2>, InstanceRef<'a, 'id2>>(tl) }
2394 } else {
2395 unsafe { std::mem::transmute::<InstanceRef<'a, 'id>, InstanceRef<'a, 'id2>>(*self) }
2397 }
2398 }
2399}
2400
2401pub fn show_popup(
2403 element: ElementRc,
2404 instance: InstanceRef,
2405 popup: &object_tree::PopupWindow,
2406 pos_getter: impl FnOnce(InstanceRef<'_, '_>) -> LogicalPosition,
2407 close_policy: PopupClosePolicy,
2408 parent_comp: ErasedItemTreeBoxWeak,
2409 parent_window_adapter: WindowAdapterRc,
2410 parent_item: &ItemRc,
2411) {
2412 generativity::make_guard!(guard);
2413 let compiled = generate_item_tree(
2415 &popup.component,
2416 None,
2417 parent_comp.upgrade().unwrap().0.description().popup_menu_description.clone(),
2418 false,
2419 guard,
2420 );
2421 let inst = instantiate(
2422 compiled,
2423 Some(parent_comp),
2424 None,
2425 Some(&WindowOptions::UseExistingWindow(parent_window_adapter.clone())),
2426 Default::default(),
2427 );
2428 let pos = {
2429 generativity::make_guard!(guard);
2430 let compo_box = inst.unerase(guard);
2431 let instance_ref = compo_box.borrow_instance();
2432 pos_getter(instance_ref)
2433 };
2434 close_popup(element.clone(), instance, parent_window_adapter.clone());
2435 instance.description.popup_ids.borrow_mut().insert(
2436 element.borrow().id.clone(),
2437 WindowInner::from_pub(parent_window_adapter.window()).show_popup(
2438 &vtable::VRc::into_dyn(inst.clone()),
2439 pos,
2440 close_policy,
2441 parent_item,
2442 false,
2443 ),
2444 );
2445 inst.run_setup_code();
2446}
2447
2448pub fn close_popup(
2449 element: ElementRc,
2450 instance: InstanceRef,
2451 parent_window_adapter: WindowAdapterRc,
2452) {
2453 if let Some(current_id) =
2454 instance.description.popup_ids.borrow_mut().remove(&element.borrow().id)
2455 {
2456 WindowInner::from_pub(parent_window_adapter.window()).close_popup(current_id);
2457 }
2458}
2459
2460pub fn make_menu_item_tree(
2461 menu_item_tree: &Rc<object_tree::Component>,
2462 enclosing_component: &InstanceRef,
2463) -> MenuFromItemTree {
2464 generativity::make_guard!(guard);
2465 let mit_compiled = generate_item_tree(
2466 menu_item_tree,
2467 None,
2468 enclosing_component.description.popup_menu_description.clone(),
2469 false,
2470 guard,
2471 );
2472 let mit_inst = instantiate(
2473 mit_compiled.clone(),
2474 Some(enclosing_component.self_weak().get().unwrap().clone()),
2475 None,
2476 None,
2477 Default::default(),
2478 );
2479 mit_inst.run_setup_code();
2480 MenuFromItemTree::new(vtable::VRc::into_dyn(mit_inst))
2481}
2482
2483pub fn update_timers(instance: InstanceRef) {
2484 let ts = instance.description.original.timers.borrow();
2485 for (desc, offset) in ts.iter().zip(&instance.description.timers) {
2486 let timer = offset.apply(instance.as_ref());
2487 let running =
2488 eval::load_property(instance, &desc.running.element(), desc.running.name()).unwrap();
2489 if matches!(running, Value::Bool(true)) {
2490 let millis: i64 =
2491 eval::load_property(instance, &desc.interval.element(), desc.interval.name())
2492 .unwrap()
2493 .try_into()
2494 .expect("interval must be a duration");
2495 if millis < 0 {
2496 timer.stop();
2497 continue;
2498 }
2499 let interval = core::time::Duration::from_millis(millis as _);
2500 if !timer.running() || interval != timer.interval() {
2501 let callback = desc.triggered.clone();
2502 let self_weak = instance.self_weak().get().unwrap().clone();
2503 timer.start(i_slint_core::timers::TimerMode::Repeated, interval, move || {
2504 if let Some(instance) = self_weak.upgrade() {
2505 generativity::make_guard!(guard);
2506 let c = instance.unerase(guard);
2507 let c = c.borrow_instance();
2508 let inst = eval::ComponentInstance::InstanceRef(c);
2509 eval::invoke_callback(&inst, &callback.element(), callback.name(), &[])
2510 .unwrap();
2511 }
2512 });
2513 }
2514 } else {
2515 timer.stop();
2516 }
2517 }
2518}